此UI库是用duilib库改写的,在整体架构上是一致的,使用上省略了控件的一些属性,可能是因为这些属性不常用,或基本用不到,所以省略,但核心代码还存在,没打开。
首先消息主流程说起:
(1)创建窗口,并注册类后,所有这一类窗口的消息均从WndProc走,面前来看所有窗口消息都从这个静态函数走,在创建窗口的过程中将实例指针传给CreateWindowEx最后一个参数,这样可以在WndProc中拿到这个指针;
(2)
1. WndProc首先捕获WM_NCCREATE消息,然后抓到窗口指针,将窗口指针保存在用户数据中:
:SetWindowLong(hWnd, GWLP_USERDATA, (LONG)(LONG_PTR)pThis);
2. 然后若果是销毁消息WM_NCDESTROY,就从GWLP_USERDATA中取出窗口类实例指针,调用OnFinalMessage来做窗口类实例的收尾操作,这里一般是delete this销毁实例。
3. 然后调用窗口实例的HandleMessage消息,有窗口实例负责处理各种各样的WINDOWS消息(注意这里没有再调用系统默认消息处理函数,所以有些函数需要自己调用DefWindowProc),没有找到窗口指针的消息直接调用DefWindowProc
4.窗口(GMWindowImplBase)的HandleMessage首先调用自己的OnXXX函数,若此函数将Handled置为TRUE自不在往下走,否则调用PaintManager->HandleMessage若返回TRUE则不再往下走,否则调用基类WindowWnd::HandleMessage这个函数就捕获了TIMER消息,处理了延时销毁什么的事务。
5.PaintManager::MessageHandler这个函数很关键,首先处理了内置的消息FILTER,然后处理各类消息,例如WM_PAINT,WM_NCHITTEST,WM_SIZE,WM_MOUSEHOVER等等,最为关键的是WM_PAINT消息
绘制主要分为两种:有PAINT消息的使用的是WM_PAINT绘制,无PAINT消息的层窗口使用的是UpdateLayeredWindow绘制,这两种绘制实现几乎一致差别在于最后绘制到DC的时候WM_PAINT使用的是BITBLE,层窗口使用的是UpdateLayeredWindow API函数具体过程:
首先根据需要重新计算所有控件的位置:m_pRoot->SetPos(rcClient);
然后创建一个内存表面,然后调用顶层控件的DoPaint函数,传入这个表面,这个函数会将他自己和他所有的子控件都绘制在这个表面上,
然后将这个表面bitblt到hc上面。
6.m_pRoot->SetPos(rcClient)细节
首先将整体框架计算SETPOS,然后各个Container分别计算自己的Pos和计算每个m_items的Pos,调用setpos设置。这里面有很多细节例如float怎么处理,滚动条怎么处理,format对齐怎么处理等待。
这里特别要说的是
float->相对于父控件浮动,若无宽高属性则默认相对于父控件进行平移,即大小和父控件一致,整体平移pos中指定的xy
relfloat->相对父控件浮动,若无宽高属性则ringht=父控件right, width=父控件width
absfloat->相对于窗口进行浮动,若无宽高,则则ringht=窗口right, width=窗口width
Container::SetFloatPos()是专门为容器增加的函数是自定义函数,需要外部调用,主要是让容器将自己作为父控件进行浮动,若没有宽高则参见以上说明
7.Container::DoPaint细节
先绘制自己的背景,背景图,状体图,文字,边框
然后依次(注意这里的依次,以XML从上到下的依次,后面的控件绘制的图像可能会覆盖前面控件的图像)绘制各个item->DoPaint,有个特例就是format=topmost最后绘制,她位于Z轴最顶端。
还有就是float有时候可能会跑到别人的绘制区域,顶点容器需要遍历每个容器和控件,如果绘制区域不在容器或控件的区域内,则看看是不是有浮动容器在绘制区域内,有就绘制。
width和pos哪个在后面哪个生效。
以上消息处理流程完结
(3)消息循环谁来产生:
在程序入口处有PaintManager静态方法负责产生,首先设置一些属性
PaintManager::SetCheckUIThread(true);
然后调用
PaintManager::MessageLoop();运行消息循环
函数实现如下:
MSG msg = { 0 };
while (::GetMessage(&msg, NULL, 0, 0))
{
if (!PaintManager::TranslateMessage(&msg))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
PaintManager::TranslateMessage函数主要处理1.ActiveX的TranslateAccelerator消息,还有当前窗口对应的PaintManager对象的->PreMessageHandler消息,或者是父窗口的PaintManager对象的->PreMessageHandler消息,若处理过了则不再往下传递。
(4)呈现
主要有两个重要因素:位置和绘制 SetPos 和 DoPaint