天天看點

Duilib學習筆記《01》— duilib整體架構認識

 從GoogleCode上下載下傳的duilib工程中附帶的一副總體設計圖(如下所示),可以先整體了解一下,有個初步的認識,對後續進一步深入了解學習會很有幫助。

Duilib學習筆記《01》— duilib整體架構認識

通過設計圖有了一個初步認識後,接下來開始進一步深入學習了解,主要從以下幾個方面進行了解學習:

庫的組成;架構基本流程;元素建立機制;消息處理機制。

1.1 工具庫

由于duilib沒有對外部的任何庫進行依賴,是以在其内部實作了很多用于支撐項目的基礎類(如下圖所示)。這些類分布在Util檔案夾中:

Duilib學習筆記《01》— duilib整體架構認識

UI相關:CPoint / CSize / CDuiRect

簡單容器:CStdPtrArray / CStdValArray / CStdString / CStdStringPtrMap

上面這些類看名字就基本能夠了解其具體的含義了,當然除了基本的基礎庫,還有一些和視窗使用相關的工具的封裝,如視窗工具:WindowImplBase,這個工具我們在這裡不詳述,後面使用中會經常用到。

1.2 控件庫

Duilib學習筆記《01》— duilib整體架構認識

控件庫在duilib的實作中被分為了兩塊:Core和Control:

Core中包含的是所有控件公用的部分,裡面主要是一些基類和繪制的封裝。

Control中包含的就是各個不同的控件的行為了。

這當中尤其要注意控件基類CControlUI和容器基類CContainerUI,這是duilib核心類(如下圖所示)中是很重要的兩部分:

Duilib學習筆記《01》— duilib整體架構認識

        CControlUI在整個控件體系中非常重要,它是所有控件的基類,也是組成控件樹的基本元素,控件樹中所有的節點都是一個CControlUI。

        它基本包括了所有控件公共的屬性,如:位置,大小,顔色,是否有焦點,是否被啟用等等。當然這個類中還提供了非常多的基礎函數,用于重載來實作子控件,如擷取控件名稱和ClassName,是否顯示等等。

        另外為了友善從XML中直接解析出控件的各個屬性,這個類中還在提供了一個SetAttribute的方法,傳入字元串的屬性名稱和值對特定的屬性進行設定,内部其實就是挨個比較字元串去完成的,是以平時使用的時候就還是不要使用的比較好了,因為每個屬性實際上都有特定的方法來擷取和設定。

        另外每個控件中還有幾個事件管理的對象——CEventSource,這些對象會在特定的時機被觸發,如OnInit,調用其中儲存的各個回調函數。

        有了基本的控件基類之後,我們就需要容器來将他管理起來,這個容器就是CContainerUI,其内部用一個數組來儲存所有的CControlUI的對象,後續的所有工作,就都是基于這個對象來進行的了。

這樣在CContainerUI裡面,主要實作了一下幾個功能:

子控件的查找:CContainerUI::FindControl

子控件的生命周期管理:是否銷毀(在Remove的時候自動銷毀) / 是否延遲銷毀(交給CPaintMangerUI去一起銷毀)。

滾動條:所有的容器都支援滾動條,在其内部會對鍵盤和滑鼠滾輪事件進行處理(CContainerUI::DoEvent),對其内部所有的元素調整位置,最後在繪制的時候實作滾動的效果

繪制:由于容器中有很多元素,是以為了加快容器的繪制,繪制的時候會擷取其真正需要繪制的區域,如果子控件不在此區域中,那麼就不予繪制了

而對于這些控件的繪制實作以及相關使用,在後續具體進一步學習中再深入詳解。

架構的基本流程實際上類似Win32建立視窗流程,如果對于Win32比較熟悉,這部分可以很快掌握。

<code>1</code>

<code>int</code> <code>APIENTRY WinMain(</code><code>HINSTANCE</code> <code>hInstance,</code><code>HINSTANCE</code> <code>hPrevInstance,</code><code>LPSTR</code> <code>lpCmdLine,</code><code>int</code> <code>nCmdShow)</code>

<code>2</code>

<code>{</code>

<code>3</code>

<code>    </code><code>CPaintManagerUI::SetInstance(hInstance);       </code><code>// 第一步: 執行個體句柄與渲染類關聯</code>

<code>4</code>

<code>    </code><code>CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T(</code><code>"skin"</code><code>));</code>

<code>5</code>

<code>6</code>

<code>    </code><code>HRESULT</code> <code>Hr = ::CoInitialize(NULL);             </code><code>// 第二步:初始化COM庫, 為加載COM庫提供支援</code>

<code>7</code>

<code>    </code><code>if</code><code>( FAILED(Hr) )</code><code>return</code> <code>0;</code>

<code>8</code>

<code>9</code>

<code>    </code><code>CMainFrameWnd* pFrame =</code><code>new</code> <code>CMainFrameWnd();   </code><code>// 第三步:建立視窗類</code>

<code>10</code>

<code>    </code><code>if</code><code>( pFrame == NULL )</code><code>return</code> <code>0;</code>

<code>11</code>

<code>12</code>

<code>    </code><code>pFrame-&gt;Create(NULL, _T(</code><code>"主程式"</code><code>), UI_WNDSTYLE_FRAME, 0L, 0, 0, 800, 600);</code><code>// 第四步:注冊視窗類與建立視窗</code>

<code>13</code>

<code>    </code><code>//  實際上這裡調用Create操作和Win32建立窗體一樣,内部實際上做了以下操作:</code>

<code>14</code>

<code>    </code><code>//  -&gt; RegisterSuperclass (注冊一個超類 即已有一個視窗類的基上再注冊一個視窗類)</code>

<code>15</code>

<code>    </code><code>//  -&gt; RegisterWindowClass (注冊視窗類)</code>

<code>16</code>

<code>    </code><code>//  -&gt; ::CreateWindowEx (建立視窗,此時觸發 WM_CREATE 消息)</code>

<code>17</code>

<code>    </code><code>//  -&gt; HandleMessage  ( WM_CREATE消息處理OnCreate)</code>

<code>18</code>

<code>19</code>

<code>    </code><code>pFrame-&gt;CenterWindow();         </code><code>// 第五步:視窗居中顯示</code>

<code>20</code>

<code>    </code><code>::ShowWindow(*pFrame, SW_SHOW);</code>

<code>21</code>

<code>22</code>

<code>    </code><code>CPaintManagerUI::MessageLoop();</code><code>// 第六步:處理消息循環</code>

<code>23</code>

<code>24</code>

<code>    </code><code>::CoUninitialize();            </code><code>// 第七部:退出程式并釋放COM庫</code>

<code>25</code>

<code>    </code><code>return</code> <code>0;</code>

<code>26</code>

<code>}</code>

第一步:響應WM_CREATE消息;

第二步:主視窗類與視窗句柄關聯;

   m_pm.Init(m_hWnd)

第三步:加載XML并動态建立界面無素,與布局界面元素            

第四步:附加控件到HASH表      

第五步:添加通知處理

<code>CPaintManagerUI::AddNotifier</code>

第六步:視窗的繪制(以上是視窗的建立過程,通過xml,所有控件都被加載到CPaintManagerUI)

            CPaintManagerUI響應WM_PAINT消息,開始雙緩存繪圖                      m_pRoot-&gt;DoPaint繪背景圖                   CControlUI::DoPaint                  CRenderEngine 真正的繪圖類              pPostPaintControl-&gt;DoPostPaint 在背景圖上繪制控件             ::BitBlt 把離屏視圖畫到主屏上

 第一步:注冊消息處理函數

在CWindowWnd注冊視窗(RegisterWindowClass())裡,注冊消息回調函數(__WndProc);

第二步:消息分發

        消息回調函數(處理所有系統發送的消息),然後回調函數通過子類的CMainFrameWnd::HandleMessage對消息進行分發。

       非視窗消息通過CMainFrameWnd::HandleMessage調用CPaintManagerUI::MessageHandler進行分發。

第三步:消息循環

      在CPaintManagerUI類的MessageLoop處理消息循環; 

      接收到消息以後,進入消息回調函數(__WndProc);

(注:以下内容以滑鼠單機Button事件為例)

第四步:處理控件消息

        滑鼠按下時(WM_LBUTTONDOWN),查找滑鼠點選的控件。

        處理控件的滑鼠按下消息:通過調用基類CControlUI:: DoEvent,引起子類如CButtonUI::DoEvent事件。

       子類的DoEvent對不同類型的事件進行處理。通過CPaintManagerUI:: SendNotify回調控件注冊的事件。