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


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

通過設計圖有了一個初步認識后,接下來開始進一步深入學習了解,主要從以下幾個方面進行了解學習:
庫的組成;框架基本流程;元素創建機制;消息處理機制。


1. 庫的基本組成

1.1 工具庫

由於duilib沒有對外部的任何庫進行依賴,所以在其內部實現了很多用於支撐項目的基礎類(如下圖所示)。這些類分布在Util文件夾中:
 
  • UI相關:CPoint / CSize / CDuiRect
  • 簡單容器:CStdPtrArray / CStdValArray / CStdString / CStdStringPtrMap

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

1.2 控件庫

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

  • Core中包含的是所有控件公用的部分,里面主要是一些基類和繪制的封裝。
  • Control中包含的就是各個不同的控件的行為了。

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

1.2.1. 控件基類:CControlUI

        CControlUI在整個控件體系中非常重要,它是所有控件的基類,也是組成控件樹的基本元素,控件樹中所有的節點都是一個CControlUI。
        它基本包括了所有控件公共的屬性,如:位置,大小,顏色,是否有焦點,是否被啟用等等。當然這個類中還提供了非常多的基礎函數,用於重載來實現子控件,如獲取控件名稱和ClassName,是否顯示等等。
        另外為了方便從XML中直接解析出控件的各個屬性,這個類中還在提供了一個SetAttribute的方法,傳入字符串的屬性名稱和值對特定的屬性進行設置,內部其實就是挨個比較字符串去完成的,所以平時使用的時候就還是不要使用的比較好了,因為每個屬性實際上都有特定的方法來獲取和設置。
        另外每個控件中還有幾個事件管理的對象——CEventSource,這些對象會在特定的時機被觸發,如OnInit,調用其中保存的各個回調函數。

1.2.2. 容器基類:CContainerUI

        有了基本的控件基類之后,我們就需要容器來將他管理起來,這個容器就是CContainerUI,其內部用一個數組來保存所有的CControlUI的對象,后續的所有工作,就都是基於這個對象來進行的了。
這樣在CContainerUI里面,主要實現了一下幾個功能:

  • 子控件的查找:CContainerUI::FindControl
  • 子控件的生命周期管理:是否銷毀(在Remove的時候自動銷毀) / 是否延遲銷毀(交給CPaintMangerUI去一起銷毀)。
  • 滾動條:所有的容器都支持滾動條,在其內部會對鍵盤和鼠標滾輪事件進行處理(CContainerUI::DoEvent),對其內部所有的元素調整位置,最后在繪制的時候實現滾動的效果
  • 繪制:由於容器中有很多元素,所以為了加快容器的繪制,繪制的時候會獲取其真正需要繪制的區域,如果子控件不在此區域中,那么就不予繪制了

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


2. 框架基本流程

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

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    CPaintManagerUI::SetInstance(hInstance);        // 第一步: 實例句柄與渲染類關聯
    CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skin"));
    HRESULT Hr = ::CoInitialize(NULL);              // 第二步:初始化COM庫, 為加載COM庫提供支持
    if( FAILED(Hr) ) 
        return 0;
    CMainFrameWnd* pFrame = new CMainFrameWnd();    // 第三步:創建窗口類
    if( pFrame == NULL ) 
        return 0;
    pFrame->Create(NULL, _T("主程序"), UI_WNDSTYLE_FRAME, 0L, 0, 0, 800, 600); // 第四步:注冊窗口類與創建窗口
    //  實際上這里調用Create操作和Win32創建窗體一樣,內部實際上做了以下操作:
    //  -> RegisterSuperclass (注冊一個超類 即已有一個窗口類的基上再注冊一個窗口類)
    //  -> RegisterWindowClass (注冊窗口類)
    //  -> ::CreateWindowEx (創建窗口,此時觸發 WM_CREATE 消息)
    //  -> HandleMessage  ( WM_CREATE消息處理OnCreate)
    pFrame->CenterWindow();          // 第五步:窗口居中顯示
    ::ShowWindow(*pFrame, SW_SHOW);
    CPaintManagerUI::MessageLoop(); // 第六步:處理消息循環
    ::CoUninitialize();             // 第七部:退出程序並釋放COM庫
    return 0;
}

3. 元素創建機制

第一步:響應WM_CREATE消息;

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

m_pm.Init(m_hWnd)

第三步:加載XML並動態創建界面無素,與布局界面元素  

CDialogBuilder builder;</span>

CDialogBuilderCallbackEx cb;</span>

CControlUI* pRoot =builder.Create(_T("skin.xml"), (UINT)0,  &amp;cb, &amp;m_pm);

第四步:附加控件到HASH表

PaintManagerUI::AttachDialog

    InitControls

        FindControl

             __FindControlFromNameHash

            pManager-&gt;m_mNameHash.Insert

第五步:添加通知處理

CPaintManagerUI::AddNotifier

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

CPaintManagerUI響應WM_PAINT消息,開始雙緩存繪圖

m_pRoot->DoPaint繪背景圖 

CControlUI::DoPaint

CRenderEngine 真正的繪圖類

pPostPaintControl->DoPostPaint 在背景圖上繪制控件

            ::BitBlt 把離屏視圖畫到主屏上

4.消息處理機制

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

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

第二步:消息分發

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

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

第三步:消息循環

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

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

(注:以下內容以鼠標單機Button事件為例)

第四步:處理控件消息

        鼠標按下時(WM_LBUTTONDOWN),查找鼠標點擊的控件。

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

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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM