duilib

一、Duilib的整体架构


二、Duilib框架基本流程(Win32创建窗口流程)

         第一步:实例句柄与渲染类关联

                                  CPaintManagerUI::SetInstance(hInstance);

                CPaintManagerUI::SetResourcePath(LPCTSTRpStrPath)

         第二步: 初始化COM库, 为加载COM库提供支持

            ::CoInitialize

    第三步:创建窗口类

    第四步:调用CWindowWnd::Create创建窗口,Create函数实现了注册窗口,指定回调函数,创建窗口,处理消息等。

    第五步:窗口居中   

            CWindowWnd::CenterWindow

       第六步:处理消息循环

                     CPaintManagerUI::MessageLoop

    第七步:CPaintManagerUI::MessageLoop

            ::CoUninitialize()

三、Duilib窗口元素的创建机制

    第一步:响应WM_CREATE消息

    第二步:主窗口类与窗口句柄关联

              m_pm.Init(m_hWnd)

     第三步:加载XML并动态创建界面无素,与布局界面元素

                   CDialogBuilder builder;

                            CDialogBuilderCallbackEx cb;

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

        第四步:附加控件到HASH表

                CPaintManagerUI::AttachDialog

                                 InitControls

                                          FindControl

                                                  __FindControlFromNameHash

                                                 pManager->m_mNameHash.Insert

       第五步:添加通知处理

                CPaintManagerUI::AddNotifier

       第六步:窗口的绘制(以上是窗口的创建过程,通过xml,所有控件都被加载到CPaintManagerUI

            CPaintManagerUI响应WM_PAINT消息,开始双缓存绘图

                     m_pRoot->DoPaint绘背景图 

                 CControlUI::DoPaint

                 CRenderEngine 真正的绘图类

             pPostPaintControl->DoPostPaint 在背景图上绘制控件

            ::BitBlt 把离屏视图画到主屏上

四、Duilib消息处理机制:

         第一步:注册消息处理函数

                  在CWindowWnd注册窗口RegisterWindowClass())里,注册消息回调函数(__WndProc);

         第二步:消息分发

                  消息回调函数(处理所有系统发送的消息),然后回调函数通过子类的CMainFrameWnd::HandleMessage对消息进行分发

       非窗口消息通过CMainFrameWnd::HandleMessage调用CPaintManagerUI::MessageHandler进行分发

         第三步:消息循环

              在CPaintManagerUI类的MessageLoop处理消息循环。

                  接收到消息以后,进入消息回调函数(__WndProc);

以下内容以鼠标单机Button事件为例:

         第四步:处理控件消息

                  鼠标按下时(WM_LBUTTONDOWN),查找鼠标点击的控件

                  处理控件的鼠标按下消息:通过调用基类CControlUI:: DoEvent,引起子类如CButtonUI::DoEvent事件。

                  子类的DoEvent对不同类型的事件进行处理。通过CPaintManagerUI:: SendNotify回调控件注册的事件。

//-------------------------------------------------------------------------------------------------

1.VC6使用记得Project Settings -> C/C++ -> Preprocessor definitions添加_USRDLL,UILIB_EXPORTS。

2.mfc中新建duilib窗口退出时整个程序退出了,是因为duilib窗口退出时使用了PostQuitMessage(0),使得整个程序退出,换成Close(); 但是不知道什么原因有的duilib窗口先退出了,mfc窗口出非法访问错误也退出了,有的duilib窗口就不会。

3.建duilib窗口要PostQuitMessage(0);   ::CoInitialize(NULL);

 

①:框架程序基本流程

   -> WinMain(入口函数)

-> CPaintManagerUI::SetResourceInstance  (实例句柄与渲染类关联)

-> ::CoInitialize  (初始化COM库, 为加载COM库提供支持)

-> new C360SafeFrameWnd (创建窗口类)

-> pFrame->Create (注册窗口类与创建窗口)

->RegisterSuperclass (注册一个超类 即已有一个窗口类的基上再注册一个窗口类)

->RegisterWindowClass(注册窗口类)

-> ::CreateWindowEx (创建窗口,此时触发 WM_CREATE消息)

-> HandleMessage  ( WM_CREATE消息处理OnCreate)

-> pFrame->CenterWindow (窗口居中显示)

-> CPaintManagerUI::MessageLoop (处理消息循环)

-> ::CoUninitialize();   (退出程序并释放COM库)

 

②:上面的流程图是一个基本的WIN32程序框架,相对于 DUILIB库,此时我们应关心的是窗口上的元素是如何建立起来的。这时我们应关心WM_CREATE的消息处理函数OnCreate的实现

-> C360SafeFrameWnd:: OnCreate

  -> m_pm.Init(m_hWnd) (主窗口类与窗口句柄关联)

-> CControlUI* pRoot = builder.Create(加载XML并动态创建界面无素,与布局界面元素,核心函数单独分析 注意:CDialogBuilder并不是一个对话框类)

-> m_pm.AttachDialog (附加控件数据到HASH表中)

      -> InitControls (初始化控件)

             ->FindControl

->__FindControlFromNameHash

    ->pManager->m_mNameHash.Insert(把控件插入到Hash中)

 

-> m_pm.AddNotifier增加通知处理

③:接下来我们应关心的是XML的加载过程,及XML中的数是如何与窗口类进行关联的

-> CDialogBuilder::Create (加载XML文件)

->  m_xml.LoadFromFile

-> CMarkup::LoadFromFile (也可从资源文件中加载::LoadResource)

    ->  CMarkup::LoadFromMem(把外部XML数据加入到内存)

->  m_xml.GetRoot (获得XML 根结点, 开始解析XML数据)

->  "window"  (解析根结点window下的三个公共元素属性)

   ->  "image"  pManager->AddImage(加载设备无关位图并关联到图像HASH中m_mImageHash.Insert)

 ->  "font"  pManager->AddFont(增加字体)

->  CPaintManagerUI::AddFont(增加字体 并关联字体数组m_aCustomFonts.Add)

->  "default" (增加默认属性 如:垂直滚动条和水平滚动条)

   -> _Parse  (解析具体的控件元素 动态生成控件元素)

             -> node.GetName (跳过已经解析过的三个元素"image" "font" "default")

-> _tcscmp (根据元素的名称 动态生成控件"Edit" "List" "Text"  "Combo" "Label" "Button"  "Option" "Slider" "Control" "ActiveX" "Progress"  "Container" "TabLayout" "ListHeader" "TileLayout" "DialogLayout" "VerticalLayout" "ListHeaderItem"  "ListTextElement"  "HorizontalLayout"  "ListLabelElement" "ListExpandElement" "ListContainerElement")

-> pContainer->Add(pControl)   (增加控件)

-> m_items.Add(关联于控件数组中)

           -> pControl->SetAttribute设置控件属性

上面的工作完成了XML数据的加载,并动态生成了控件,把控件加载到了控件列表,建立了与控件相关的HASH。接下来我们应关心的是控件如何显示在界面上的。

通过上面的源码,我们应关心几个核心类的关联:

->首先产生主窗口类:C360SafeFrameWnd,

->在主窗口类中放置成变量 CPaintManagerUIm_pm; 与 控件绘制和消息处理关联起来

->CPaintManagerUI::MessageHandler WM_PAINT

->双缓存绘图 

-> m_pRoot->DoPaint绘背景图 

-> CControlUI::DoPaint

-> CRenderEngine 真正的绘图类

-> pPostPaintControl->DoPostPaint在背景图上绘制控件

->::BitBlt 把离屏视图画到主屏上

 

当界面元素正常显示后,接下来我们应关心的是如何处理控件的事件

-> C360SafeFrameWnd:: HandleMessage (应用需要处理的消息及处理函数)

-> m_pm.MessageHandler(DUILIB库帮我们处理的消息及相关的处理函数)

-> CWindowWnd::HandleMessage(应用层和 DUILIB都不处理的消息交由系统默认处理)


//-----------------------------------------------------------------------------------

CTileLayoutUI: 父类CContainerUI

四、通用
1、script
CMarkup
CMarkupNode

2、language
CUIUtility

3、multi-thread
CriticalSection
AutoCriticalSection
CMutex
CAutoMutex
CEvent
CAutoEvent
CManualEvent

五、主要数据成员
1、CPaintManagerUI
CControlUI* m_pRoot: 如果控件是叠加的则存放最下层的控件对象,否则存放第一个创建的控件对象
CControlUI* m_pFocus: 存放获得焦点的控件对象指针
CControlUI* m_pEventHover: 存放当前有鼠标移进移出事件的控件对象指针
CControlUI* m_pEventClick: 存放当前有点击事件的控件对象指针
CControlUI* m_pEventKey: 存放当前有按键事件的控件对象指针
CStdPtrArray m_aNotifiers: 记录所有需要事件通知的窗口,根据窗口名称调用相应的消息处理函数
CStdPtrArray m_aNameHash: 保存控件对象指针hash表(用控件名称生成hash值)

CStdPtrArray m_aPostPaint: panel的fade效果
CStdPtrArray m_aMessageFilters: 保存需要进行消息过滤的控件或功能(如动画类)
CStdPtrArray m_aDelayedCleanup: 
CStdPtrArray m_aPreMessages: 预处理消息
HWND m_hWndPaint: 控件布局窗口句柄
HDC m_hDcPaint: 控件布局窗口设备DC
HDC m_hDcOffscreen: 离屏内存DC
HBITMAP m_hbmpOffscreen: 离屏内存DC相关联HBITMAP

2、CControlUI
CPaintManagerUI* m_pManager: 窗口消息或绘图管理器
CControlUI* m_pParent: 逻辑上的父窗口(控件)对象指针
CStdString m_sName: 控件标识
CStdString m_sText: 控件显示标题或显示脚本字符串
CStdString m_sToolTip: 控件的Tip信息

3、CContainerUI
CStdPtrArray m_items: 同一层的控件对象或控件对象的子对象,例如canvas上放置的按钮、combox由edit和list两个子对象组成,其它还有tab等。具体见 CDropDownUI、CTabFolderUI、CNavigatorPanelUI三个类定义

4、CDialogLayoutUI
CStdValArray m_aModes: 用于存放在Layout上绝对坐标转成相对坐标(CDialogLayoutUI::RecalcArea)的控件对象(指针、大小、模式),目的是否为了让布局上的控件随布局变化而变化,能够正确绘图???

六、控件属性
待完成

七、脚本例子
<Dialog>
  <WindowCanvas pos=/"0,0,600,800/">
  <DialogLayout pos=/"0,0,600,800/">
    <Button pos=/"390, 30, 490, 58/" text=/"OK/" name=/"ok/"/>
  </DialogLayout>
  </WindowCanvas>
</Dialog>

八、绘图及事件处理
1、绘图
STEP01. CWindowWnd::__WndProc: 主窗口程序
STEP02. pThis->HandleMessage: pThis是布局窗口对象指针,并与布局窗口绑定(SetWindowLongPtr)

STEP03. m_pm.MessageHandler: m_pm为CPaintManagerUI唯一实例对象
STEP04. CPaintManagerUI::MessageHandler: 处理WM_PAINT
STEP05. m_pRoot->DoPaint: m_pRoot为最下层的控件对象(在本例中为CWindowCanvasUI控件,对应脚本中的WindowCanvas)
STEP06. CCanvasUI::DoPaint: 往画布上绘制背景色、边角弧形、水印等。
STEP07. CContainerUI::DoPaint: 在最下层具有容器特性的控件(CWindowCanvasUI控件)上画容器内所有控件(控件实例对象保存在m_items中)
STEP08. pControl->DoPaint: pControl为控件对象实例之一,利用多态性来调用不同控件的绘图方法
STEP09. CButtonUI::DoPaint: 按钮(对应脚本中Button)绘图方法,有下面两种方法
i)文字方法: CGUIRenderEngineUI::DPaintButton
ii)图片方法: CGUIRenderEngineUI::DoPaintBitmap
STEP10. 新一轮消息循环

2、事件
STEP01. CWindowWnd::__WndProc: 
STEP02. pThis->HandleMessage: 
STEP03. m_pm.MessageHandler: 
STEP04. CPaintManagerUI::MessageHandler: 处理WM_LBUTTONDOWN
STEP05. CPaintManagerUI::FindControl: 根据鼠标坐标查找相应控件对象
STEP06. m_pRoot->FindControl: 
STEP07. CContainerUI::FindControl: 在最下层具有容器特性的控件(CWindowCanvasUI控件)容器内查找相应控件对象
STEP08. CControlUI::FindControl: 在m_items中查找相对应的控件对象
STEP09. pControl->Event: pControl为控件对象实例之一,利用多态性来调用不同控件的事件方法
STEP10. CPaintManagerUI::MessageHandler: 处理WM_LBUTTONUP

STEP11. m_pEventClick->Event: 利用多态性来调用不同控件的事件方法(m_pEventClick说明见"主要数据成员")
STEP12. CButtonUI::Event: 按钮(对应脚本中Button)事件方法
STEP13. CButtonUI::Activate: 
STEP14. m_pManager->SendNotify: 传递控件对象指针和触发事件(文本方式)
STEP15. CPaintManagerUI::SendNotify: 注意以下两点实现是完成控制和业务分离的关键
i)利用重载特性调用注册的监听对象(窗口)的消息处理函数Notify(监听对象保存在m_aNotifiers中)
for( int i = 0; i < m_aNotifiers.GetSize(); ++i )
{
    static_cast<INotifyUI*>(m_aNotifiers[i])->Notify(Msg);
}
ii)布局窗口CStartPageWnd的消息处理,宏定义展开后实际就是重载的Notify函数
DIRECT_BEGIN_NOTIFYMAP(CStartPageWnd)
    PROCESS_BUTTON_CLICK(_T("ok"),OnOk)
    。。。
DIRECT_END_NOTIFYMAP(CStandardPageWnd)
STEP16. CStartPageWnd::OnOk: 控件消息处理函数,此处可以加入具体的事务逻辑处理
STEP17. 新一轮消息循环

3、消息定义(文本)
"click"、"changed"、"link"、"browse"、"itemclick"、"itemselect"、"dropdown"、"itemactivate"、"headerdragging"、"headerclick"、"headerdragged"、"itemexpand"、"itemcollapse"、"windowinit"、"killfocus"、"setfocus"、"timer"

九、疑问
1、Edit、Combox的下拉列表部分、ScrollBar、Tooltip控件是创建的实际窗口,这个与DirectUI思路还是有差别的
2、实例中有创建一个不进行消息处理的窗口(CFrameWindowWnd),然后又创建了一个窗口(CStandardPageWnd)用于具体的控件布局。但是我用一个窗口也能实现,原作者为什么这样还不清楚

3、控件是用文本形式来做标识的,消息类型是文本形式,是否改成数值型比较好

十、引用
引用一: http://www.viksoe.dk/code/windowless1.htm
引用二: http://directui.googlecode.com/
引用三: http://www.cnblogs.com/cutepig/archive/2010/06/14/1758204.html


转载于:https://my.oschina.net/guyson/blog/325117

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值