看了点其他东西,结果发现以前看的chrome代码有些忘了,看来写博客确实是好事,能帮助人很容易的回忆起来
chrome的是基于wtl的,他的view类比较庞大,具体的子类又分为native和custom
native就是windows的自有空间,比如button,custom的就是directui了,界面里划分出不同的区域,区域显示出类似button的效果
以radiobutton为例,继承关系是view->button->nativebutton->checkboxbutton->radiobutton
nativebutton.h里面 组合了NativeButtonWrapper* native_wrapper_;
在nativebutton的ViewHierarchyChanged()里创建 native_wrapper_ = CreateWrapper();
createwrapper这个是checkbox和radiobutton都要重载的,radio的是
CreateRadioButtonWrapper(this);
而这里实际最终调用的就是
HWND control_hwnd = CreateWindowEx(
GetAdditionalExStyle(), L"BUTTON",
L"", WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | BS_RADIOBUTTON,
0, 0, width(), height(), GetWidget()->GetNativeView(), NULL, NULL, NULL);
所以这里如果想重绘checkbox之类的原生控件的话,就需要在nativexx父窗口里处理相应的wm_paint,drawitem之类,chrome里面默认都没有对原生控件的自绘处理
再来看custombutton的textbutton
这套流程走的相当苦闷
首先widghtwin收到mouse消息,然后通过rootview去查鼠标所在位置属于哪个子窗口并通知,
子窗口收到后,发现需要重绘,这时候就会调用schedulepaint,这又是个反方向的找到最终的rootVIEW
并转发到widgtwin,最后调用invalidaterect(最子窗口的区域) ,然后widghtwin就会收到wm_paint,这时候去
执行绘画,绘画过程也是一样的恶心,widghtwin这里beginpaint,然后把整个rootview的所有子窗口全部重画到
一个canvas里去,最后画到dc,endpaint
view->button->custombutton->textbutton,绘制通过
void WidgetWin::OnPaint(HDC dc) {
root_view_->OnPaint(hwnd());
}
root_view然后将所有的子控件都画到canvas上,
ProcessPaint(&canvas);
这里有个递归调用所有子控件,最终就到了我们想要的Onpaint里面
void TextButton::Paint(gfx::Canvas* canvas, bool for_drag)
比如接收到鼠标移入移出等需要重绘的时候,控件调用paint通知root_view
这里root_view绘画的时候,onpaint并没有直接调用redrawwindow,而是调用了
SchedulePaint(gfx::Rect(ps.rcPaint), false);
schedupaint里,如果在消息到达这里的时候上一次的绘画还没执行,那他会合并这个无效区域,就相当于少画了一次
if (invalid_rect_.IsEmpty())
invalid_rect_ = r;
else
invalid_rect_ = invalid_rect_.Union(r);
然后是通过消息的方式去具体执行redraw
pending_paint_task_ = new PaintTask(this);
MessageLoop::current()->PostTask(FROM_HERE, pending_paint_task_);
void Run() {
if (root_view_)
root_view_->PaintNow();
}
绘画的时候就是从内存的图里直接画到dc上
这样做的好处就是可合并多个绘画动作,这个mfc的处理里也是类似的
这里root_view是个大集合,所有的控件都要addchildview进来的,
widghtwin继承自WindowImpl,是一个windows下window的实现类,通过调用windows api RegisterClassEx,createwindowex创建窗口,并将消息转到wndproc->onwndproc->ProcessWindowMessage
然后子类只要重载ProcessWindowMessage就可以处理消息了,这就相当于转到了wtl的那一套
widghtwin里面包含了rootview的一个实例root_view_
menu也放到这里吧,他实现menu是通过新创建一个大小为0的窗口,然后在这个窗口上注册消息,并弹出具体的menu
TrackPopupMenuEx(menu_, flags, point.x(), point.y(), host_window_->hwnd(),
NULL);