一、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值)
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)
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
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)用于具体的控件布局。但是我用一个窗口也能实现,原作者为什么这样还不清楚
十、引用
引用一: http://www.viksoe.dk/code/windowless1.htm
引用二: http://directui.googlecode.com/
引用三: http://www.cnblogs.com/cutepig/archive/2010/06/14/1758204.html