• 转chromeUI


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

    说明:
    本人所使用的chrome源码比较早,所以下载最新的代码多少会和文中有些差异。
    代码在windows下使用visual studio 2008 编译。
    源码下载地址 (http://code.google.com/chromium/ )

    推荐阅读:
    http://www.cnblogs.com/duguguiyu/archive/2008/10/02/1303095.html

    Ui部分的通用(说“通用”是因为chrome的主窗口框架是单独定制的)代码主要在目录树的“src/chrome/views”目录下。其中
    controls目录封装用到的控件,例如Label、Textfield等
    widget目录主要封装系统相关的UI底层细节,特别是UI消息机制。
    window目录主要封装UI Frame相关的细节,例如窗口的标题栏、系统按钮以及Frame、Dialog等的代理(设计模式术语)接口。
    如果想了解Chrome绘图技术,可以去Skia项目(http://code.google.com/p/skia )看看

    一个简单的程序

    //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();
    }
    

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

    源码分析

    TestWindow 需要继承自两个类 views::View 和views::WindowDelegate。
    在chrome中,任何控件(这里忽略操作系统原生控件的某些特殊情况)包括一个Frame都必须是一个View。
    一个顶层窗口(例如Frame、Dialog(Dialog继承自DialogDelegate,该代理继承自 WindowDelegate))必须继承自views::WindowDelegate以便传递该窗口的一些参数,例如窗口的尺寸。
    GetPreferredSize()含义很明显是告诉系统初始窗口大小(确切的说是窗口内部的根View的大小,实际窗口大小要大于该值)为(400*300),这里其实可选。
    GetContentsView()函数将自己作为一个窗口的根View传给所附属的Frame(或Dialog)。
    中间的黑色是因为客户区没有任何东西,所以背景贴图和背景贴图未覆盖的地方被显示出来。
    内部的白色框是因为系统默认会在客户区画白色的边框。

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

    UI消息机制(针对windows平台)

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

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

    Chrome控件树

    前面提到,chrome每一个控件都是一个views::View。views::View可以认为是chrome中所有控件的基类。在这个类中定义了一个通用的操作和默认实现,例如鼠标单击处理函数,如果某控件需要自定义鼠标处理事件,可以在自己的类中覆盖基类的默认实现。
    每一个views::View可以包含子views::View。所以每一个都包含一个父views::View和若干子views::View的指针,下面是源码(srcchromeviewsview.h)的定义

    // This view's parent
    View *parent_;
    
    // This view's children.
    typedef std::vector<View*> ViewList;
    ViewList child_views_;
    

    这棵树的根节点便是views::RootView(srcchromeviewswidget oot_view.h)。这个View的主要用途就是从views::Widget(srcchromeviewswidgetwidget.h)中接收UI消息。
    这棵树的最初几层可以参考chrome源码(srcchromeviewswindow on_client_view.h)

    ////////////////////////////
    
    // 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/
    
    

    说明:
    views::Window 表示一个实际的窗口,不过它并不是一个views::View。
    views::RootView 如前面所述,即整个控件树的根。
    Views::NonClientView 表示整个实际的控件树的根,实际上views::RootView只有一颗子树Views::NonClientView ,所以个人认为这两个View可以合并在一块。也许是为了从逻辑上分开吧,即RootView向上和OS层打交道,而 NonClientView则专注于下层的控件View
    views::NonClientView subclass 表示整个非客户区的View。例如窗口的标题栏、窗口图标、关闭按钮、最大化/最小化按钮、边框等就定义在此。实际上chrome默认使用views::NonClientView的子类CustomFrameView (srcchromeviewswindowcustom_frame_view.h)。此外chrome还提供原生View的封装NativeFrameView(srcchromeviewswindow ative_frame_view.h ,该类本人未作测试。)。
    views::ClientView or subclass 是窗口客户区的根,上面test实例中的TestWindow 也是一个views::View。该View就是挂在【views::ClientView or subclass】的下面(作为它的子树)。具体的挂在通过函数GetContentsView() 实现。

    Chrome消息分发机制

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

    // This method is invoked when the mouse enters this control.
    
      //
    
      // Default implementation does nothing. Override as needed.
    
      virtual void OnMouseEntered(const MouseEvent& event);
    

    当views::RootView收到windows消息时,例如鼠标移动的消息,它便会根据鼠标的位置获取具体的View,然后调用该View的相关处理函数。
    因为相关的处理函数都在views::View中有一份默认实现,所以不关系该事件的View可以不理会。而如果需要定制处理函数只需重载相关的处理函数即可。
    Chrome查找具体的View通过 GetViewForPoint函数,递归调用。View首先查找所有子View,如果事件的位置在某个子View的区域内,则调用该子View的 GetViewForPoint函数。否则返回自己。下面是RootView分发“鼠标进入”事件的源码

    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);
        }
    
  • 相关阅读:
    [CF1198D] Rectangle Painting 1
    [CF696B] Puzzles
    [CF540D] Bad Luck Island
    [P1654] OSU!
    [P6154] 游走
    [CF1265E] Beautiful Mirrors
    [CF920F] SUM and REPLACE
    [CF453B] Little Pony and Harmony Chest
    [CF808D] Array Division
    [CF1155D] Beautiful Array
  • 原文地址:https://www.cnblogs.com/kevinzhwl/p/5276650.html
Copyright © 2020-2023  润新知