注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

写着玩

Bob

 
 
 

日志

 
 
 
 

chrome UI 学习笔记  

2009-12-01 16:11:26|  分类: Chrome |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

学习Chrome源码也有一小段时间了,对该浏览器的UI部分小有了解,于是将自己的一些心得贴上来,希望对有需要的朋友有所帮助。


说明:

  1. 本人所使用的chrome源码比较早,所以下载最新的代码多少会和文中有些差异。

  2. 代码在windows下使用visual studio 2008 编译。

  3. 源码下载地址 (http://code.google.com/chromium/ )


推荐阅读:

http://www.cnblogs.com/duguguiyu/archive/2008/10/02/1303095.html


Ui部分的通用(说“通用”是因为chrome的主窗口框架是单独定制的)代码主要在目录树的“src/chrome/views”目录下。其中

  1. controls目录封装用到的控件,例如LabelTextfield

  2. widget目录主要封装系统相关的UI底层细节,特别是UI消息机制。

  3. window目录主要封装UI Frame相关的细节,例如窗口的标题栏、系统按钮以及FrameDialog等的代理设计模式术语)接口。

如果想了解Chrome绘图技术,可以去Skia项目http://code.google.com/p/skia )看看


一个简单的程序

DE<//test.h

#include "chrome/views/view.h"
#include "chrome/views/window/window_delegate.h"

class TestWindow :
    public views::View,
    public views::WindowDelegate{
public:

    virtual View* GetContentsView() {
        return this;
    }

    virtual gfx::Size GetPreferredSize(){
        return gfx::Size(400,300);
    }

    static void CreateTestWindow();

};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//test.cpp

#include "test.h"
#include "chrome/views/window/window.h"

void TestWindow::CreateTestWindow(){
    views::Window::CreateChromeWindow(NULL,gfx::Rect(),new TestWindow)->Show();
}
DE<


结果如下:
chrome UI 学习笔记 - yolcy - 写着玩



源码分析

  1. TestWindow 需要继承自两个类 views::View views::WindowDelegate

    1. chrome中,任何控件这里忽略操作系统原生控件的某些特殊情况)包括一个Frame都必须是一个View

    2. 一个顶层窗口(例如FrameDialogDialog继承自DialogDelegate,该代理继承自 WindowDelegate))必须继承自views::WindowDelegate以便传递该窗口的一些参数,例如窗口的尺寸。

  2. GetPreferredSize()含义很明显是告诉系统初始窗口大小确切的说是窗口内部的根View的大小,实际窗口大小要大于该值)为(400*300),这里其实可选。

  3. GetContentsView()函数将自己作为一个窗口的根View传给所附属的Frame(或Dialog)。

  4. 中间的黑色是因为客户区没有任何东西,所以背景贴图和背景贴图未覆盖的地方被显示出来。

  5. 内部的白色框是因为系统默认会在客户区画白色的边框。


为了方面其他代码调用,这里提供一个静态函数CreateTestWindow()打开该窗口



UI消息机制(针对windows平台

Windows 每一个控件都单独处理消息,chrome一个整的的窗口可以看做一个(这里忽略原生空间的封装)Windows)控件,所以所有控件的消息都会发到一块去,所以chrome Ui框架有一套机制来分发到具体的(Chrome)控件。


Windows版本的chromeUI部分基于WTL(http://sourceforge.net/projects/wtl/)。chrome通过在此处(src\chrome\views\widget\widget_win.h)中的绑定来获取windows UI消息。接着Chrome内置的一套消息分发机制将该消息发送到具体的chrome 控件。


Chrome控件树

前面提到,chrome每一个控件都是一个views::Viewviews::View可以认为是chrome中所有控件的基类。在这个类中定义了一个通用的操作和默认实现,例如鼠标单击处理函数,如果某控件需要自定义鼠标处理事件,可以在自己的类中覆盖基类的默认实现。

每一个views::View可以包含子views::View。所以每一个都包含一个父views::View和若干子views::View的指针,下面是源码(src\chrome\views\view.h)的定义

// This view's parent

View *parent_;


// This view's children.

typedef std::vector<View*> ViewList;

ViewList child_views_;

这棵树的根节点便是views::RootView(src\chrome\views\widget\root_view.h)。这个View的主要用途就是从views::Widget(src\chrome\views\widget\widget.h)中接收UI消息。

这棵树的最初几层可以参考chrome源码(src\chrome\views\window\non_client_view.h)


DE<////////////////////////////////////////////////////////////////////////////////

// NonClientView

//

// The NonClientView is the logical root of all Views contained within a

// Window, except for the RootView which is its parent and of which it is the

// sole child. The NonClientView has two children, the NonClientFrameView which

// is responsible for painting and responding to events from the non-client

// portions of the window, and the ClientView, which is responsible for the

// same for the client area of the window:

//

// +- views::Window ------------------------------------+

// | +- views::RootView ------------------------------+ |

// | | +- views::NonClientView ---------------------+ | |

// | | | +- views::NonClientView subclass ---+ | | |

// | | | | | | | |

// | | | | << all painting and event receiving >> | | | |

// | | | | << of the non-client areas of a >> | | | |

// | | | | << views::Window. >> | | | |

// | | | | | | | |

// | | | +----------------------------------------+ | | |

// | | | +- views::ClientView or subclass --------+ | | |

// | | | | | | | |

// | | | | << all painting and event receiving >> | | | |

// | | | | << of the client areas of a >> | | | |

// | | | | << views::Window. >> | | | |

// | | | | | | | |

// | | | +----------------------------------------+ | | |

// | | +--------------------------------------------+ | |

// | +------------------------------------------------+ |

// +----------------------------------------------------+

//

// The NonClientFrameView and ClientView are siblings because due to theme

// changes the NonClientFrameView may be replaced with different

// implementations (e.g. during the switch from DWM/Aero-Glass to Vista Basic/

DE<



说明:

  1. views::Window 表示一个实际的窗口,不过它并不是一个views::View

  2. views::RootView 如前面所述,即整个控件树的根。

  3. Views::NonClientView 表示整个实际的控件树的根,实际上views::RootView只有一颗子树Views::NonClientView ,所以个人认为这两个View可以合并在一块。也许是为了从逻辑上分开吧,即RootView向上和OS层打交道,而 NonClientView则专注于下层的控件View

  4. views::NonClientView subclass 表示整个非客户区的View。例如窗口的标题栏、窗口图标、关闭按钮、最大化/最小化按钮、边框等就定义在此。实际上chrome默认使用views::NonClientView的子类CustomFrameView (src\chrome\views\window\custom_frame_view.h)。此外chrome还提供原生View的封装NativeFrameViewsrc\chrome\views\window\native_frame_view.h ,该类本人未作测试。)。

  5. views::ClientView or subclass 是窗口客户区的根,上面test实例中的TestWindow 也是一个views::View。该View就是挂在【views::ClientView or subclass】的下面作为它的子树)。具体的挂在通过函数GetContentsView() 实现。




Chrome消息分发机制

下面以一个鼠标移入事件说明,views::View中定义了函数OnMouseEntered,当鼠标移入某控件时,该函数被调用。下面是相关的函数声明。



DE<// This method is invoked when the mouse enters this control.

  //

  // Default implementation does nothing. Override as needed.

  virtual void OnMouseEntered(const MouseEvent& event);
DE<


views::RootView收到windows消息时,例如鼠标移动的消息,它便会根据鼠标的位置获取具体的View,然后调用该View的相关处理函数。

因为相关的处理函数都在views::View中有一份默认实现,所以不关系该事件的View可以不理会。而如果需要定制处理函数只需重载相关的处理函数即可。

Chrome查找具体的View通过 GetViewForPoint函数,递归调用。View首先查找所有子View,如果事件的位置在某个子View的区域内,则调用该子ViewGetViewForPoint函数。否则返回自己。下面是RootView分发“鼠标进入”事件的源码



DE<void RootView::OnMouseMoved(const MouseEvent& e) {
  View* v = GetViewForPoint(e.location());
  // Find the first enabled view.

  while (v && !v->IsEnabled())
    v = v->GetParent();
  if (v && v != this) {
    if (v != mouse_move_handler_) {
      if (mouse_move_handler_ != NULL) {
        MouseEvent exited_event(Event::ET_MOUSE_EXITED, 0, 0, 0);
        mouse_move_handler_->OnMouseExited(exited_event);
      }

      mouse_move_handler_ = v;

      MouseEvent entered_event(Event::ET_MOUSE_ENTERED,
                               this,
                               mouse_move_handler_,
                               e.location(),
                               0);
      mouse_move_handler_->OnMouseEntered(entered_event);
    }
DE<

  评论这张
 
阅读(1365)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017