转chromeUI2
基本控件
前面提到过,chrome的基础控件在目录“ src/chrome/views/controls”目录下。
下面是chrome自带的控件一览
//按钮
D:\chrometrunk\chrometrunk\src\chrome\views\controls\button>ls
button.cc custom_button.cc native_button.cc radio_button.h
button.h custom_button.h native_button.h text_button.cc
button_dropdown.cc image_button.cc native_button_win.cc text_button.h
button_dropdown.h image_button.h native_button_win.h
checkbox.cc menu_button.cc native_button_wrapper.h
checkbox.h menu_button.h radio_button.cc
//菜单
D:\chrometrunk\chrometrunk\src\chrome\views\controls\menu>ls
chrome_menu.cc controller.h menu.h
chrome_menu.h menu.cc view_menu_delegate.h
//滑动条
D:\chrometrunk\chrometrunk\src\chrome\views\controls\scrollbar>ls
bitmap_scroll_bar.cc native_scroll_bar.cc scroll_bar.cc
bitmap_scroll_bar.h native_scroll_bar.h scroll_bar.h
//表格
D:\chrometrunk\chrometrunk\src\chrome\views\controls\table>ls
group_table_view.cc table_view.cc table_view_unittest.cc
group_table_view.h table_view.h
//树
D:\chrometrunk\chrometrunk\src\chrome\views\controls\tree>ls
tree_model.h tree_node_iterator_unittest.cc tree_view.cc
tree_node_iterator.h tree_node_model.h tree_view.h
//其他控件
D:\chrometrunk\chrometrunk\src\chrome\views\controls>ls
button label_unittest.cc native_control_win.h tabbed_pane.h
combo_box.cc link.cc scroll_view.cc table
combo_box.h link.h scroll_view.h text_field.cc
hwnd_view.cc menu scrollbar text_field.h
hwnd_view.h message_box_view.cc separator.cc throbber.cc
image_view.cc message_box_view.h separator.h throbber.h
image_view.h native_control.cc single_split_view.cc tree
label.cc native_control.h single_split_view.h
label.h native_control_win.cc tabbed_pane.cc
尽管chrome提供丰富的控件,但是如果打算使用chrome这一套UI框架开发自己的程序,这些远远不够用,幸运的是基于chrome开发自定义控件相当的方便。
接着上面的例子,下面的程序在客户区添加一个Button和Label。当点击按钮后,Label显示“点击了“。下面是源码:
//test.h
#include "chrome/views/view.h"
#include "chrome/views/window/window_delegate.h"
#include "chrome/views/controls/button/button.h"
namespace views{
class Label;
class TextButton;
}
class ChromeCanvas;
class TestWindow :
public views::View,
public views::WindowDelegate,
public views::ButtonListener{
public:
TestWindow();
virtual View* GetContentsView() {
return this;
}
virtual gfx::Size GetPreferredSize(){
return gfx::Size(400,300);
}
virtual void Layout();
virtual void Paint(ChromeCanvas* canvas);
virtual void ButtonPressed(views::Button* sender);
static void CreateTestWindow();
private:
views::Label * lable_;
views::TextButton * button_;
};
//test.cpp
#include "test.h"
#include "chrome/views/window/window.h"
#include "chrome/views/controls/button/text_button.h"
#include "chrome/views/controls/label.h"
#include "chrome/common/gfx/chrome_canvas.h"
TestWindow::TestWindow(){
this->lable_ = new views::Label(L"");
this->button_ = new views::TextButton(this,L"点击");
AddChildView(this->lable_);
AddChildView(this->button_);
}
void TestWindow::Layout(){
this->lable_->SetBounds(10,10,50,30);
this->button_->SetBounds(10,50,60,30);
}
void TestWindow::Paint(ChromeCanvas* canvas){
canvas->FillRectInt(SK_ColorWHITE, 0, 0, width(), height());
}
void TestWindow::ButtonPressed(views::Button* sender){
if(sender == this->button_){
this->lable_->SetText(L"点击了");
}
}
void TestWindow::CreateTestWindow(){
views::Window::CreateChromeWindow(NULL,gfx::Rect(),new TestWindow)->Show();
}
结果如下:
chrome UI 学习笔记2 - yolcy - 写着玩
源码解析
包含相关的控件views::Label * lable_ ,views::TextButton * button_;
通过实现 views::ButtonListener接口来处理button_的点击事件
使用 AddChildView 将两个控件添加为子控件。
在buttonPressed函数中添加事件处理的代码。
Paint 函数将整个客户区画成白色
Layout函数设置子控件的位置。
Paint&&Layout
Chrome使用Skia作为二位绘图引擎,而绘图体现在每一个View的Paint函数中,views::View的Paint函数屁事都不干,所以在第一个程序中可以看到客户区将背景图片和默认的黑色显示出来,而第二个函数,我们覆盖默认的Paint函数并且将整个View (TestWindow)的区间绘成白色。
每一个View的Layout函数用于定位它所有的子View。当因为某些原因整个窗口需要重构时,根View调用Layout函数,然后递归调用每一个子View的Layout函数。关于Chrome的Layout,chrome还提供一个GridLayout(src\chrome\views\grid_layout.h)和FillLayout(src\chrome\views\fill_layout.h)。具体的用法可以参考chrome中的源码。
事件处理
chrome控件通常的事件处理方式是通过声明一个接口,然后将该接口指针作为一个成员变量来使用实现。例如按钮事件的接口声明如下:
// An interface implemented by an object to let it know that a button was
// pressed.
class ButtonListener {
public:
virtual void ButtonPressed(Button* sender) = 0;
};
一般情况下,控件的父View会继承子控件依赖的借口并实现相关的函数,然后将自己作为参数传递给这些控件实现完整的事件处理。一个父View可能包含好几个同类的控件,所以这些接口一般会包含一个sender以便区分不同的控件发送者。
其他控件的使用,可以参考源码中的代码,这里不多叙述。
原生控件
一般来说,如果有设计良好,使用方便的控件可用就完全不必自己从轮子造起,chrome就是这样。chrome提供的很多控件并不是自己从零绘制的,而是基于原生的操作系统控件,最典型的是TextField(src\chrome\views\controls\text_field.h)。该控件底层的实现完全使用WTL提供的richedit(http://msdn.microsoft.com/en-us/library/bb787873(VS.85).aspx#_win32_Rich_Edit_Version_2.0)。当然为了方便扩展,chrome也提供相关的类方便用户封装其他的原生控件。
下面以封装IE浏览器说明问题,当用户需要在UI中嵌入浏览器,可以将下面的View像上面的代码中Button那样使用。
首先获得WTL对IE的封装(实际上是一个com),相关源码可参考下面的代码(http://devel.openocr.org/svn/openocr/trunk/cuneiform/interface/icrashreport/wtl/samples/tabbrowser/browserview.h )。
接着新建一个View,并封装该原生控件,具体的源码可参考:
// ie_view.h BrowserView.h 是原生控件的封装头文件
#include "BrowserView.h"
#include "string"
#include "chrome/views/view.h"
#include "base/scoped_ptr.h"
namespace views{
class HWNDView;
}
class IEView :
public views::View{
public:
explicit IEView(const std::wstring & url);
virtual ~IEView();
virtual void Layout();
virtual void ViewHierarchyChanged(bool is_add,
views::View* parent,
views::View* child);
private:
void initChildViews();
scoped_ptr<CBrowserView> iebrowser_;
views::HWNDView * iebrowser_view_;
std::wstring url_;
protected:
DISALLOW_COPY_AND_ASSIGN(IEView);
};
// ie_view.cpp
#include "ie_view.h"
#include "base/logging.h"
#include "chrome/views/controls/hwnd_view.h"
#include "chrome/views/widget/widget.h"
IEView::IEView(const std::wstring & url) :
url_(url) {
}
IEView::~IEView() {
}
void IEView::Layout() {
this->iebrowser_view_->SetBounds(0,0,width(),height());
}
void IEView::initChildViews() {
iebrowser_.reset( new CBrowserView );
HWND hwnd = GetWidget()->GetNativeView();
RECT rc = {0,0,100,100};
HWND hwnd_ie = iebrowser_->Create( hwnd, 0, url_.c_str(), WS_CHILD|WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL);
DWORD errcode = GetLastError();
DCHECK(iebrowser_->IsWindow());
iebrowser_view_ = new views::HWNDView;
DCHECK(iebrowser_view_) << "LocationBarView::Init - OOM!";
AddChildView(iebrowser_view_);
iebrowser_view_->SetAssociatedFocusView(this);
iebrowser_view_->Attach(iebrowser_->m_hWnd);
}
void IEView::ViewHierarchyChanged(bool is_add,
views::View* parent,
views::View* child) {
if (is_add && child == this ) {
this->initChildViews();
}
}
这个过程中,最关键的View就是 views::HWNDView。该View提供一个机制将原生控件和Chrome View进行绑定,以便用户能够像View一样操作原生控件。
ViewHierarchyChanged函数在它所述的View被add和remove时被调用。上面源码的意思是当 IEView被父View调用AddChildView函数将其加为子View时被调用。之所以不放在构造函数内是因为有些控件的初始化依赖一个窗口句柄,而在构造函数结束之前,这个句柄很可能还没有初始化。