Chrome UI框架分析
一、总体结构
1、概述
代码结构
UI部分的通用代码放在src/chrome/views目录下。其中:
control目录中为各种控件,如Label、Textfield等,这个目录最为庞大。
widget目录中为系统相关的UI底层细节,特别是UI消息机制。
window目录中为UI Frame相关的细节,例如窗口的标题栏、系统按钮以及、Frame、Dialog等的代理接口。
focus目录下为焦点管理、快捷键相关代码。
animation中为与动画相关的代码。
主要特点
1、采用DUI技术,大部分窗口和控件都是DUI的。
2、虚窗口的基类是View,每个View可以包含子View,他们组织成一棵树结构。
3、真窗口的基类是Widget,它处理与操作系统相关的最原始的消息,在windows上,它处理所有窗口消息。Window是顶层窗口的基类,包括气泡、漂浮窗口、对话框、主窗口等。
4、View树的根节点是RootView,这个View的主要用途就是从Widget中接收UI消息,进行分发,包括排版与绘制,它是整个窗口树的根。
5、NonClientView表示整个实际的窗口树的根,是RootView唯一的子节点。RootView向上和OS层打交道,而NonClientView则专注于下层的控件View。NonClientView表示整个非客户区的View,包括标题栏、窗口图标、关闭按钮、最大化与最小化按钮、边框等,它负责非客户区的排版与绘制,这类可以被派生,以实现各种不同的客户区。ClientView是客户区的根,负责客户区的排版与绘制。
6、排版由专门的LayoutManager负责。
7、界面绘制上,绘制引擎(Canvas)与窗框系统分开。可以根据实际情况使用不同的图形引擎(Skia与D2D)。一般情况下,绘图采用Skia,字体用windows的GDI,Skia保证可以跨平台。绘制引擎提供了裁剪、平移变换等机制,使得View可以只重绘一部分,而且支持紧急与非紧急绘制。
8、控件消息的响应采用Listener模式。
9、绘制通过任务机制实现,从Task派生出一个PaintTask,其Run函数中实现绘制,该任务被放到MessagePumpForUI的任务队列中,每次消息循环的时候取出来执行。
10、广泛采用设计模式,顶层窗口的实现大类运用了代理模式,复杂的界面元素采用MVC模式实现界面(排版、绘制、界面消息响应)与逻辑的分离。
UI核心类与模块的关系如下图所示:
UI的基础代码可以分为消息循环(MessagePumpForUI与MessageLoopForUI)、真窗口(Widget以及派生类)、虚窗口(View以及派生类)、窗口树的根(RootView以及派生类,它实际上是View的派生类)、消息分发与响应、排版(LayoutManager)、绘制(Canvas、CanvasPaint等)、焦点与快捷键管理(FocusManager)组成。这些是UI最底层、最核心的模块,控件建立在这些模块的基础之上。
类的继承关系图
View是所有控件、子窗口(虚窗口)的基类。View类的派生关系图:
这其中有一个庞大的分支为各种控件,具体有:
按钮 Button
菜单 Menu
滚动条 ScrollBar
表格 TableView
树控件 TreeView
组合框 ComboBox
列表框 ListBox
编辑框 TextField
静态控件 Label
view ui框架超链接控件 Link
其中,树控件、编辑框采用windows自己的原生控件,其他控件均为DUI的。但是有些控件,如按钮等,chrome还包装了windows的原生控件。
Widget是所有真窗口的基类,它负责窗口的消息映射、子窗口的管理,每个Widget有一个RootView,作为窗口树的根,管理其子窗口。Widget类的派生关系图:
WidgetWin有两类派生类,分别是气泡与漂浮窗口(地址栏自动提示窗口、标签拖出去形成的新窗口、信息气泡、菜单);有标题栏与边框的窗口(对话框、主窗口)。WindowWin是所有有标题栏与边框的窗口的基类(对话框、主窗口)。WindowWin中有客户区(client_view_)与非客户区(frame_view_),WindowWin通过配置不同的客户区,可以形成不同的窗口。
WindowImpl是对窗口创建、风格与扩展风格、窗口过程管理的封装。
窗口的结构
窗口中最大的子窗口是RootView,它是窗口树的根,它有唯一的一个孩子NonClientView。NonClientView是逻辑上的根,它有两个孩子NonClientFrameView和ClientView,前者是窗口的非客户区,负责非客户区的绘制、消息响应;后者是窗口的客户区,负责客户区的绘制、消息响应。这种结构如下图所示:
各个类之间的关系如下图所示(虚线为包含,实线为继承):
WidgetWin中有成员变量root_view_(RootView),为其窗口树的根,而窗口在初始化时RootView通过SetContentsView设置其孩子节点NonClientView。NonClientView有两个节点frame_view_(NonClientFrameView)与client_view_(ClientView),分别是窗口的客户区与非客户区,各自处理客户区与非客户区的消息、绘制、排版等。
WindowWin继承自WidgetWin,是有边框的窗口,其中有成员变量non_client_view_。而WidgetWin并没有该成员,这是因为对于气泡等漂浮窗口,没必要划分客户区与非客户区。
非客户区与客户区View的派生关系如下图所示(虚线为包含,实线为继承):
ClientView有两个派生类,分别是DialogClientView与BrowserView,前者是对话框的客户区,后者是浏览器主窗口的客户区。
NonClientFrameView有4个派生类,其中GlassBrowserFrameView为毛玻璃效果的窗口,OpaqueBrowserFrameView为非透明的窗口。
窗口的风格
窗口的风格、扩展风格的计算由WindowWin类的的两个函数CalculateWindowStyle、CalculateWindowExStyle统一完成,最基本的风格是WS_CLIPCHILDREN(裁剪孩子)、WS_CLIPSIBLINGS(裁剪兄弟)。主窗口是WS_OVERLAPPED/WS_OVERLAPPEDWINDOW;对话框是WS_POPUP、DS_MODALFRAME;可缩放的窗口是WS_OVERLAPPED、WS_THICKFRAME;控件等原生窗口为WS_CHILD。
2、View
View是View层次结构中的一个矩形区域,是所有View的基类。View是其他View的容器。Vie
w有一些基本属性,用于缩放、排版、绘制、事件分发等。View采用简单的盒式排版管理器,类似于XUL的SprocketLayout系统。基类只提供了抽象的接口,很由子类负责实现绘制、与子类相关的属性、函数。
View的的继承关系
class View : public AcceleratorTarget
AcceleratorTarget是一个虚基类,它有唯一一个成员函数AcceleratorPressed,用于处理快捷键消息。因此,所有View都可以处理快捷键
View的主要数据成员
是否Eanable:bool enabled_;
是否能够获得焦点:bool focusable_;
View所占的区域:gfx::Rect bounds_; 注意,该区域是相对于其父窗口的坐标系。
是否需要排版:bool needs_layout_;
父节点:View* parent_;
子节点数组:ViewList child_views_;
排版管理器,永于对子窗口的排版:scoped_ptr<LayoutManager> layout_manager_;
是否可见:bool is_visible_;
背景:scoped_ptr<Background> background_;
边框:scoped_ptr<Border> border_;
是否被父亲拥有:bool is_parent_owned_;
当可视区域改变时需要通知的后代View的列表:
scoped_ptr<ViewList> descendants_to_notify_;
该View的名字:std::wstring accessible_name_;
按Tab键时下一个获得焦点的View:View* next_focusable_view_;
按Shift-Tab键时下一个获得焦点的View:View* previous_focusable_view_;
焦点管理器:FocusManager* accelerator_focus_manager_;
快捷键列表:scoped_ptr<std::vector<Accelerator> > accelerators_;
size_t registered_accelerator_count_;
右键菜单管理器:ContextMenuController* context_menu_controller_;
拖拽管理器,负责为View写拖拽数据,同时提供拖拽支持:DragController* drag_controller_;
窗口树的组织(黄为父节点指针,蓝为子节点指针):
ContextMenuController右键菜单管理器为一个抽象接口,View中有这个成员,这使得所有View及其子类都具有显示、响应右键菜单的能力:
class ContextMenuController {
public:
virtual void ShowContextMenu(View* source,
const gfx::Point& p,
bool is_mouse_gesture) = 0;
protected:
virtual ~ContextMenuController() {}
};
其中唯一一个成员函数是ShowContextMenu,用于为View显示右键菜单,其中source是要显示菜单的View的指针,p是菜单显示的位置,为屏幕坐标系。
DragController也是一个抽象的接口,负责为View的拖拽写数据,View中有这个成员,这使得所有View都支持拖拽。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论