定所操作的代码不会影响我们的UI线程,切记!
CefClient接口介绍
CefLifeSpanHandler
CefBrowser对象的生命周期事件的回调接口。
OnAfterCreated:当调用CreateBrowser函数创建浏览器对象后会立马触发这个回调,在这里可以保存浏览器对象的指针DoClose:当调用CloseBrowser函数后触发这个回调OnBeforeClose:当浏览器对象即将销毁时会触发这个回调,在这里一定要释放所有对CefBrowser对象的引用,否则会导致程序无法退出。切记这个坑。OnBeforePopup:当单击了网页中会弹出新窗口的链接时,会触发这个回调。我们的项目里应该禁止新窗口的弹出,而在原控件中跳转链接
CefRenderHandler
要使用离屏渲染功能,就必须要实现这个接口类。因为项目中使用的duilib库,目前使用分层窗体机制实现异形窗体效果,不支持显示子窗口只能自绘控件,所以没法使用比较简单的子窗口的形式显示Cef浏览器对象。只能使用离屏渲染
方法,离屏渲染的数据会通过CefRenderHandler接口回调。
首先必须要开启CefSettings结构体的windowless_rendering_enabled字段
GetRootScreenRect:浏览器对象创建后触发的回调,返回最外层窗体在屏幕中的位置GetViewRect:在浏览器对象初始化后,或者浏览器大小改变时,触发这个回调来获取浏览器对象的位置。因为浏览器对象会平铺满整个控件,所以这里返回控件的位置。其中返回的左上位置要准确,否则Cef在处理一些坐标信息时会出错GetScreenPoint:在这里把传入的坐标值,由客户区坐标转换为屏幕坐标OnCursorChange:当需要修改鼠标光标时触发这个回调OnPaint:当浏览器对象有新的渲染数据后,会触发这个回调,包含了脏区和渲染数据。应该保存这些数据,然后在适当的时候贴到目标窗体上OnPopupShow:当浏览器中要弹出内部对话框时(比如弹出一个下拉菜单),触发这个回调,通知要显示或者隐藏弹出框OnPopupSize:当浏览器中要弹出内部对话框时,触发这个回调,通知弹出框的位置和大小
离屏渲染的实现
离屏渲染的效率不如真窗口渲染,如果不是必须要离屏渲染
的情况,还是用真窗口比较好。CefControl控件实现了duilib嵌入Cef浏览器对象。
在控件初始化触发Init函数时,调用CreateBrowser函数创建CefBrowser对象,这会触发
CefRenderHandler::GetViewRect回调,在这个回调里返回控件的位置。随后网页第一次渲染时触发CefRenderHandler::OnPaint回调。
绘制渲染数据的流程
当网页渲染数据改变、或者我们主动调用了CefBrowser对象的Invalidate方法时,会触发CefRenderHandler::OnPaint回调。我写了一个内存位图缓冲类MemoryDC来保存Cef传来的渲染数据,在CefControl控件中dc_cef_成员变量负责保存渲染数据。在CefRenderHandler::OnPaint回调里,根据渲染数据初始化dc_cef_,然后根据脏区把渲染数据拷贝到dc_cef_中。数据拷贝完之后,调用CefControl控件的Invalidate方法通知窗体重绘控件在CefControl控件的Paint方法里,把dc_cef_的位图数据拷贝到duilib传入的HDC中
CefControl对事件的处理
修改CefBrowser尺寸
在离屏渲染模式下,无法直接修改CefBrowser对象的尺寸。CefControl控件重写SetPos函数,在这里调用CefBrowser对象的WasResized接口通知CefBrowser对象需要改变尺寸,之后GetViewRect接口会被触发,这时依然是返回CefControl控件的位置就可以了。之后OnPaint接口会被自动触发,按照前一节的流程进行一次渲染数据的刷新
设置CefBrowser隐藏(显示)
CefControl控件重写SetVisible函数和SetInternVisible函数,在这里调用CefBrowser对象的WasHidden接口通知CefBrowser对象隐藏或显示
对系统消息的处理
在控件初始化触发Init函数时,调用窗体类的
AddMessageFilter函数把自己注册到窗体的消息过滤队列里。CefControl控件继承IUIMessageFilter接口类并重写MessageHandler函数。当系统消息进入窗体后会依次调用消息过滤队列指针来过滤消息。在MessageHandler函数里处理我们感兴趣的消息,其他消息并不过滤处理各种鼠标类消息时,判断如果鼠标不在控件范围内则不处理相关消息。获取当前鼠标的坐标,因为CefBrowser的坐标值是以自身
左上角作为原点的,所以获取的鼠标坐标要减去CefControl控件的左上角坐标值。其中处理ButtonDown、ButtonUp、MouseMove消息时,不会中断消息继续传递给窗体,这里需要让duilib窗体类处理SetCapture、ReleaseCapture等函数处理键盘消息时,判断当前控件是否获取焦点,只处理有焦点的情况WM_SETCURSOR消息处理,在
MessageHandler函数拦截WM_SETCURSOR消息,直接调用窗体类的默认消息处理函数,不让duilib处理这个消息。CefRenderHandler::OnCursorChange接口会修改鼠标光标并修改窗体的默认光标样式,而duilib处理
WM_SETCURSOR消息时会另外修改光标,所以需要拦截
渲染Popup弹出框
浏览器中,弹出框的渲染数据是需要自己额外处理的。如下拉菜单等弹出框,否则浏览器中不会显示出弹出框当需要显示弹出框时,CefRenderHandler::OnPopupSize接口会传入弹出框的位置和尺寸等数据,在这里把数据保存到rect_popup_成员变量之后会触发
CefRenderHandler::OnPaint回调,并且渲染类型会被指定为弹出框类型PET_POPUP。这时把弹出框的渲染数据保存到MemoryDC类型的dc_cef_popup_成员变量中数据拷贝完之后,调用CefControl控件的Invalidate方法通知窗体重