2024年4月3日发(作者:)

SetFocusedViewWithReason(v, kReasonFocusTraversal);

}

}

tab键被按下时在View之间切换焦点,根据shift键是否被按下,进行向前、向后切换。

具体流程如下:

1、 获取下一个焦点View

2、 将焦点设置到该View上

View* FocusManager::GetNextFocusableView(View* original_starting_view,

bool reverse,

bool dont_loop) {

FocusTraversable* focus_traversable = NULL;

// Let's revalidate the focused view.

ValidateFocusedView();

View* starting_view = NULL;

if (original_starting_view) {

// Search up the containment hierarchy to see if a view is acting as

// a pane, and wants to implement its own focus traversable to keep

// the focus trapped within that pane.

View* pane_search = original_starting_view;

while (pane_search) {

focus_traversable = pane_search->GetPaneFocusTraversable();

if (focus_traversable) {

starting_view = original_starting_view;

break;

}

pane_search = pane_search->GetParent();

}

if (!focus_traversable) {

if (!reverse) {

// If the starting view has a focus traversable, use it.

// This is the case with WidgetWins for example.

focus_traversable = original_starting_view->GetFocusTraversable();

// Otherwise default to the root view.

if (!focus_traversable) {

focus_traversable = original_starting_view->GetRootView();

starting_view = original_starting_view;

}

} else {

// When you are going back, starting view's FocusTraversable

// should not be used.

focus_traversable = original_starting_view->GetRootView();

starting_view = original_starting_view;

}

}

} else {

focus_traversable = widget_->GetRootView();

}

// Traverse the FocusTraversable tree down to find the focusable view.

View* v = FindFocusableView(focus_traversable, starting_view, reverse);

if (v) {

return v;

} else {

// Let's go up in the FocusTraversable tree.

FocusTraversable* parent_focus_traversable =

focus_traversable->GetFocusTraversableParent();

starting_view = focus_traversable->GetFocusTraversableParentView();

while (parent_focus_traversable) {

FocusTraversable* new_focus_traversable = NULL;

View* new_starting_view = NULL;

// When we are going backward, the parent view might gain the next focus.

bool check_starting_view = reverse;

v = parent_focus_traversable->GetFocusSearch()->FindNextFocusableView(

starting_view, reverse, FocusSearch::UP,

check_starting_view, &new_focus_traversable, &new_starting_view);

if (new_focus_traversable) {

DCHECK(!v);

// There is a FocusTraversable, traverse it down.

v = FindFocusableView(new_focus_traversable, NULL, reverse);

}

if (v)

return v;

starting_view = focus_traversable->GetFocusTraversableParentView();

parent_focus_traversable =

parent_focus_traversable->GetFocusTraversableParent();

}

// If we get here, we have reached the end of the focus hierarchy, let's

// loop. Make sure there was at least a view to start with, to prevent

// infinitely looping in empty windows.

if (!dont_loop && original_starting_view) {

// Easy, just clear the selection and press tab again.

// By calling with NULL as the starting view, we'll start from the

// top_root_view.

return GetNextFocusableView(NULL, reverse, true);

}

}

return NULL;

}

获取下一个得到焦点的View。

View* GetFocusedView() const { return focused_view_; }

获取当前得到焦点的View

void SetFocusedViewWithReason(View* view, FocusChangeReason reason);

为一个View设置焦点,该View会成为焦点View。

void SetFocusedView(View* view)

为一个View设置焦点,该View会成为焦点View。

FocusChangeReason focus_change_reason() const

获取焦点改变的的原因。

kReasonFocusTraversal:焦点因为tab键或者shift+tab键而改变

kReasonFocusRestore:焦点因为恢复焦点而改变,如一个窗口关闭之后另一个窗口要恢

复焦点

kReasonDirectFocusChange:焦点因为直接点击到一个View上或者快捷键而使得该View

得到焦点

void ClearFocus();

清除当前获得焦点的View。

void ValidateFocusedView();

void StoreFocusedView();

void RestoreFocusedView();

存储与恢复焦点View,窗口激活、非激活时使用

void ClearStoredFocusedView();

注册、解注册快捷键:

void RegisterAccelerator(const Accelerator& accelerator,

AcceleratorTarget* target);

void UnregisterAccelerator(const Accelerator& accelerator,

AcceleratorTarget* target);

void UnregisterAccelerators(AcceleratorTarget* target);

实际上是向快捷键map中插入或者删除一项

bool ProcessAccelerator(const Accelerator& accelerator);

处理快捷键

bool FocusManager::ProcessAccelerator(const Accelerator& accelerator) {

AcceleratorMap::iterator map_iter = accelerators_.find(accelerator);

if (map_iter != accelerators_.end()) {

// We have to copy the target list here, because an AcceleratorPressed

// event handler may modify the list.

AcceleratorTargetList targets(map_iter->second);

for (AcceleratorTargetList::iterator iter = ();

iter != (); ++iter) {

if ((*iter)->AcceleratorPressed(accelerator))

return true;

}

}

return false;

}

处理流程是根据accelerator查找要处理该快捷键的目标列表,然后循环遍历该列表,调

用AcceleratorTarget的AcceleratorPressed,一旦某一个AcceleratorTarget处理了该快捷键,

则直接返回,不再继续处理。

void ViewRemoved(View* parent, View* removed);

当有View被删除时需要通知焦点管理器做相应的事情。

void FocusManager::ViewRemoved(View* parent, View* removed) {

if (focused_view_ && focused_view_ == removed)

ClearFocus();

}

如果被删除的View是焦点View,则调用ClearFocus清除焦点。

void AddFocusChangeListener(FocusChangeListener* listener);

void RemoveFocusChangeListener(FocusChangeListener* listener);

AcceleratorTarget* GetCurrentTargetForAccelerator(

const Accelerator& accelertor) const;

static bool IsTabTraversalKeyEvent(const KeyEvent& key_event);

判断是否是tab键按下

virtual void FocusNativeView(gfx::NativeView native_view);

为一个Windows原生窗口设置焦点

virtual void ClearNativeFocus();

清除Windows原生窗口的焦点

static FocusManager* GetFocusManagerForNativeView(

gfx::NativeView native_view);

static FocusManager* GetFocusManagerForNativeWindow(

gfx::NativeWindow native_window);

View* GetNextFocusableView(View* starting_view, bool reverse, bool dont_loop);

获取下一个能够拿到焦点的View

View* FocusManager::GetNextFocusableView(View* original_starting_view,

bool reverse,

bool dont_loop) {

FocusTraversable* focus_traversable = NULL;

// Let's revalidate the focused view.

ValidateFocusedView();

View* starting_view = NULL;

if (original_starting_view) {

// Search up the containment hierarchy to see if a view is acting as

// a pane, and wants to implement its own focus traversable to keep

// the focus trapped within that pane.

View* pane_search = original_starting_view;

while (pane_search) {

focus_traversable = pane_search->GetPaneFocusTraversable();

if (focus_traversable) {

starting_view = original_starting_view;

break;

}

pane_search = pane_search->GetParent();

}

if (!focus_traversable) {

if (!reverse) {

// If the starting view has a focus traversable, use it.

// This is the case with WidgetWins for example.

focus_traversable = original_starting_view->GetFocusTraversable();

// Otherwise default to the root view.

if (!focus_traversable) {

focus_traversable = original_starting_view->GetRootView();

starting_view = original_starting_view;

}

} else {

// When you are going back, starting view's FocusTraversable

// should not be used.

focus_traversable = original_starting_view->GetRootView();

starting_view = original_starting_view;

}

}

} else {

focus_traversable = widget_->GetRootView();

}

// Traverse the FocusTraversable tree down to find the focusable view.

View* v = FindFocusableView(focus_traversable, starting_view, reverse);

if (v) {

return v;

} else {

// Let's go up in the FocusTraversable tree.

FocusTraversable* parent_focus_traversable =

focus_traversable->GetFocusTraversableParent();

starting_view = focus_traversable->GetFocusTraversableParentView();

while (parent_focus_traversable) {

FocusTraversable* new_focus_traversable = NULL;

View* new_starting_view = NULL;

// When we are going backward, the parent view might gain the next focus.

bool check_starting_view = reverse;

v = parent_focus_traversable->GetFocusSearch()->FindNextFocusableView(

starting_view, reverse, FocusSearch::UP,

check_starting_view, &new_focus_traversable, &new_starting_view);

if (new_focus_traversable) {

DCHECK(!v);

// There is a FocusTraversable, traverse it down.

v = FindFocusableView(new_focus_traversable, NULL, reverse);

}

if (v)

return v;

starting_view = focus_traversable->GetFocusTraversableParentView();

parent_focus_traversable =

parent_focus_traversable->GetFocusTraversableParent();

}

// If we get here, we have reached the end of the focus hierarchy, let's

// loop. Make sure there was at least a view to start with, to prevent

// infinitely looping in empty windows.

if (!dont_loop && original_starting_view) {

// Easy, just clear the selection and press tab again.

// By calling with NULL as the starting view, we'll start from the

// top_root_view.

return GetNextFocusableView(NULL, reverse, true);

}

}

return NULL;

}

View* FindFocusableView(FocusTraversable* focus_traversable,

View* starting_view,

bool reverse);

快捷键的处理流程

views::FocusManager::ProcessAccelerator负责快捷键的最后分发,直接调用执行快捷键

的窗口的AcceleratorPressed函数。焦点在主进程的窗口中时,直接靠

views::AcceleratorHandler::Dispatch来分发消息,该函数的流程为:

1、确定是否处理该消息

2、TranslateMessage(&msg);

3、DispatchMessage(&msg);

焦点不在主进程的窗口中时,views::AcceleratorHandler::Dispatch分发消息

焦点在主窗口上时快捷键的处理流程如下所示:

焦点在网页时快捷键的处理流程如下:

views::FocusManager::ProcessAccelerator

BrowserView::PreHandleKeyboardEvent

Browser::PreHandleKeyboardEvent

TabContentsView::PreHandleKeyboardEvent

RenderViewHost::PreHandleKeyboardEvent

RenderWidgetHost::ForwardKeyboardEvent

RenderViewHost::ForwardKeyboardEvent

RenderWidgetHostViewWin::OnKeyEvent

RenderWidgetHostViewWin::ProcessWindowMessage

ATL::CWindowImplBaseT>::WindowProc

!761a6238()

!761a68ea()

!761a6899()

!761a7d31()

!761d04b9()

!761a7dfa()

views::AcceleratorHandler::Dispatch

base::MessagePumpForUI::ProcessMessageHelper

base::MessagePumpForUI::ProcessNextWindowsMessage

八、动画

1、概述

动画的本质是在指定的时间点绘制特定的内容,每次定时器事件发生时,进行一次绘制。

因此,动画所包含的最基本的元素包括定时器、状态管理、要绘制的东西等。在每个时间点,

动画都有一个状态值,它可以是要播放的帧的编号、窗口的大小、位置,或者其他东西。

Chrome实现了一套扩展性高、高效的动画系统,能够为我们管理定时器、状态等,我

们只要实现动画代理(响应动画开始、结束、动画进行等事件),就可以显示各种不同的动

画(多帧播放、窗口大小位置改变等)。

按照变化的速度,动画有以下几种:

LINEAR:匀速

EASE_OUT:先快后慢

EASE_IN:先慢后快

EASE_IN_OUT:开始和结束慢,中间快

FAST_IN_OUT:开始和结束快,中间慢

EASE_OUT_SNAP:开始快,结束慢,中间突然断掉

ZERO:恒定为0

动画系统中最核心的三个类是AnimationContainer、Animation、AnimationDelegae。

AnimationContainer是动画容器,它包含了若干个动画,这样做的好处是这些动画可以共享

一个定时器,减小系统开销。AnimationContainer负责定时器的管理,每当定时器事件发生

时,它会调用动画类的接口进行动画的状态改变。动画类Animation是一个抽象的类,它管

理了动画的状态,对应于每个定时器时刻都有一个状态,随着定时器的前进会更新这些状态。

动画代理AnimationDelegae负责动画时间发生时的动作(绘制特定的内容),每次动画状态

更新时,动画类都会通知动画代理。

动画类Animation有若干派生类,用于实现各种不同的动画。类的派生关系如下图所示:

2、主要的类

AnimationDelegate

抽象类,动画代理,如果需要接收有关动画状态的通知,则需要实现该类的接口。从该

类派生的类只要实现这些接口,就可处理动画结束、动画进行、动画取消等通知。该类有以

下成员函数:

virtual void AnimationEnded(const Animation* animation)

动画结束时会调用该函数。

virtual void AnimationProgressed(const Animation* animation)

动画进行时会调用该函数。

virtual void AnimationCanceled(const Animation* animation)

动画取消时会调用该函数。

AnimationContainer

被Animation使用,管理定时器。在内部每个Animation都会创建一个唯一的

AnimationContainer。通过调用Animation::SetContainer,可以将一组Animation分在一起,

这可以确保他们的启动、更新同时进行。AnimationContainer是自引用计数。

主要的数据成员:

base::TimeTicks last_tick_time_;

如果只有一个动画且定时器还没有被触发过,则该值为该动画被添加的时间;否则,为

最后一次调用Run的时间。

Elements elements_;

管理的动画集合,Animation的列表。

base::TimeDelta min_timer_interval_;

定时器的时间步长。

base::RepeatingTimer timer_;

周期性定时器。

Observer* observer_;

动画容器的观察者。

void Start(Element* animation);

往动画队列中添加一个动画,如果是第一个动画,则启动定时器。具体代码如下:

void AnimationContainer::Start(Element* element) {

DCHECK(elements_.count(element) == 0); // Start should only be invoked if the

// element isn't running.

if (elements_.empty()) {

last_tick_time_ = TimeTicks::Now();

SetMinTimerInterval(element->GetTimerInterval());

} else if (element->GetTimerInterval() < min_timer_interval_) {

SetMinTimerInterval(element->GetTimerInterval());

}

element->SetStartTime(last_tick_time_);

elements_.insert(element);

}

void Stop(Element* animation);

从动画列表中删除一个动画,如果是最后一个动画,则停掉定时器。具体代码如下:

void AnimationContainer::Stop(Element* element) {

DCHECK(elements_.count(element) > 0); // The element must be running.

elements_.erase(element);

if (elements_.empty()) {

timer_.Stop();

if (observer_)

observer_->AnimationContainerEmpty(this);

} else {

TimeDelta min_timer_interval = GetMinInterval();

if (min_timer_interval > min_timer_interval_)

SetMinTimerInterval(min_timer_interval);

}

}

void set_observer(Observer* observer)

base::TimeTicks last_tick_time() const

bool is_running() const

void Run();

定时器的回调函数,更新所有动画的状态,具体代码如下所示:

void AnimationContainer::Run() {

// We notify the observer after updating all the elements. If all the elements

// are deleted as a result of updating then our ref count would go to zero and

// we would be deleted before we notify our observer. We add a reference to

// ourself here to make sure we're still valid after running all the elements.

scoped_refptr this_ref(this);

TimeTicks current_time = TimeTicks::Now();

last_tick_time_ = current_time;

// Make a copy of the elements to iterate over so that if any elements are

// removed as part of invoking Step there aren't any problems.

Elements elements = elements_;

for (Elements::const_iterator i = ();

i != (); ++i) {

// Make sure the element is still valid.

if (elements_.find(*i) != elements_.end())

(*i)->Step(current_time);

}

if (observer_)

observer_->AnimationContainerProgressed(this);

}

void SetMinTimerInterval(base::TimeDelta delta);

设置定时器的步长,重置定时器。

base::TimeDelta GetMinInterval();

获取定时器的时间步长

AnimationContainer::Observer

抽象类,由AnimationContainer管理的Animation的每次更新都会通知Observer。该类

有以下抽象函数:

virtual void AnimationContainerProgressed(AnimationContainer* container) = 0;

定时器每次到时,并且所有Animation都更新之后调用。

virtual void AnimationContainerEmpty(AnimationContainer* container) = 0;

没有Animation被AnimationContainer管理时调用。

AnimationContainer::Element

抽象接口,被AnimationContainer包含的元素的接口,由Animation实现。

virtual void SetStartTime(base::TimeTicks start_time) = 0;

virtual void Step(base::TimeTicks time_now) = 0;

virtual base::TimeDelta GetTimerInterval() const = 0;

Animation

动画本身,用于动画的播放。从AnimationContainer::Element继承。主要的数据成员:

const base::TimeDelta timer_interval_;

定时器的时间步长

bool is_animating_;

是否正在进行动画

AnimationDelegate* delegate_;

动画代理

scoped_refptr container_;

动画容器的指针

base::TimeTicks start_time_;

动画的启动时间

主要的成员函数:

void Start();

开始动画。调用容器的开始方法,将本动画加入到容器中,并且还要通知代理。具体代

码如下所示:

void Animation::Start() {

if (is_animating_)

return;

if (!container_.get())

container_ = new AnimationContainer();

is_animating_ = true;

container_->Start(this);

AnimationStarted();

}

void Stop();

停止动画,将本动画从容器中删除,同事还要啊通知动画代理动画结束了,具体代码如

下所示:

void Animation::Stop() {

if (!is_animating_)

return;

is_animating_ = false;

// Notify the container first as the delegate may delete us.

container_->Stop(this);

AnimationStopped();

if (delegate_) {

if (ShouldSendCanceledFromStop())

delegate_->AnimationCanceled(this);

else

delegate_->AnimationEnded(this);

}

}

virtual double GetCurrentValue() const = 0;

获取当前状态值,0-1之间

double CurrentValueBetween(double start, double target) const;

int CurrentValueBetween(int start, int target) const;

gfx::Rect CurrentValueBetween(const gfx::Rect& start_bounds,

const gfx::Rect& target_bounds) const;

获取当前的具体状态值,当前状态值与起始值、终止值的插值。插值方式为:

其中,为当前抽象状态值,为起始状态值,为目标状态值。

void set_delegate(AnimationDelegate* delegate)

void SetContainer(AnimationContainer* container);

bool is_animating() const

base::TimeDelta timer_interval() const

static bool ShouldRenderRichAnimation();

virtual void AnimationStarted()

virtual void AnimationStopped()

virtual bool ShouldSendCanceledFromStop()

AnimationContainer* container()

base::TimeTicks start_time() const

AnimationDelegate* delegate()

virtual void SetStartTime(base::TimeTicks start_time);

virtual void Step(base::TimeTicks time_now) = 0;

virtual base::TimeDelta GetTimerInterval() const

LinearAnimation

线性时间界限动画,状态值在[0.0, 1.0]之间。

LinearAnimation : public Animation

主要的数据成员:

base::TimeDelta duration_;

double state_;

bool in_end_;

主要的成员函数:

virtual double GetCurrentValue() const;

void End();

void SetDuration(int duration);

virtual void AnimateToState(double state) = 0;

virtual void Step(base::TimeTicks time_now);

virtual void AnimationStopped();

virtual bool ShouldSendCanceledFromStop();

SlideAnimation

幻灯动画,时隐时现。LinearAnimation的子类。

SlideAnimation : public LinearAnimation

主要的数据成员:

AnimationDelegate* target_;

Tween::Type tween_type_;

bool showing_;

double value_start_;

double value_end_;

double value_current_;

int slide_duration_;

主要的成员函数:

virtual void Reset();

virtual void Reset(double value);

virtual void Show();

virtual void Hide();

virtual void SetSlideDuration(int duration)

int GetSlideDuration() const

void SetTweenType(Tween::Type tween_type)

double GetCurrentValue() const

bool IsShowing() const

bool IsClosing() const

void AnimateToState(double state);

ThrobAnimation

跳动动画,SlideAnimation的子类,可以时隐时现,如此循环。可以时隐时现,如此反

复,指定周期数

ThrobAnimation : public SlideAnimation

主要的数据成员:

int slide_duration_;

int throb_duration_;

int cycles_remaining_;

bool throbbing_;

主要的成员函数:

void StartThrobbing(int cycles_til_stop);

void SetThrobDuration(int duration)

virtual void Reset();

virtual void Show();

virtual void Hide();

virtual void SetSlideDuration(int duration)

void set_cycles_remaining(int value)

int cycles_remaining() const

virtual void Step(base::TimeTicks time_now);

void ResetForSlide();

MultiAnimation

多个动画的集合

class MultiAnimation : public Animation

主要的数据成员:

const Parts parts_;

const int cycle_time_ms_;

double current_value_;

size_t current_part_index_;

主要的成员函数:

virtual double GetCurrentValue() const

size_t current_part_index() const

virtual void Step(base::TimeTicks time_now);

const Part& GetPart(int* time_ms, size_t* part_index);

BoundsAnimator

将一个View从一个大小、一个位置移动到另一个大小、另一个位置的动画。其关键是

在每次定时器时间到时计算View的合适大小和位置,然后调度重绘。典型的例子就是标签

栏动画。

主要数据成员:

View* parent_;

BoundsAnimatorObserver* observer_;

scoped_refptr container_;

ViewToDataMap data_;

AnimationToViewMap animation_to_view_;

gfx::Rect repaint_bounds_;

主要的成员函数:

void AnimateViewTo(View* view, const gfx::Rect& target);

void SetAnimationForView(View* view, SlideAnimation* animation);

const SlideAnimation* GetAnimationForView(View* view);

void StopAnimatingView(View* view);

void SetAnimationDelegate(View* view,

AnimationDelegate* delegate,

bool delete_when_done);

bool IsAnimating(View* view) const;

bool IsAnimating() const;

void Cancel();

void set_observer(BoundsAnimatorObserver* observer)

virtual SlideAnimation* CreateAnimation();

void RemoveFromMaps(View* view);

void CleanupData(bool send_cancel, Data* data, View* view);

Animation* ResetAnimationForView(View* view);

void AnimationEndedOrCanceled(const Animation* animation,

AnimationEndType type);

void BoundsAnimator::AnimationProgressed(const Animation* animation) {

DCHECK(animation_to_view_.find(animation) != animation_to_view_.end());

View* view = animation_to_view_[animation];

DCHECK(view);

const Data& data = data_[view];

gfx::Rect new_bounds =

animation->CurrentValueBetween(_bounds, _bounds);

if (new_bounds != view->bounds()) {

gfx::Rect total_bounds = new_(view->bounds());

// Build up the region to repaint in repaint_bounds_. We'll do the repaint

// when all animations complete (in AnimationContainerProgressed).

if (repaint_bounds_.IsEmpty())

repaint_bounds_ = total_bounds;

else

repaint_bounds_ = repaint_bounds_.Union(total_bounds);

view->SetBounds(new_bounds);

}

if (te)

te->AnimationProgressed(animation);

}

最核心的一个函数,每当定时器到,动画需要更新时调用此函数。处理流程如下:

1、 计算View在当前帧的大小和位置new_bounds。

2、 将这个矩形与前一帧的矩形进行并操作,得到需要重绘的区域

repaint_bounds_。

3、

调度重绘。

具体见下图:

virtual void AnimationEnded(const Animation* animation);

virtual void AnimationCanceled(const Animation* animation);

virtual void AnimationContainerProgressed(AnimationContainer* container);

virtual void AnimationContainerEmpty(AnimationContainer* container);

3、具体的例子

新建tab时的动画:

vidBaseTabStrip::AddTabAt(intmodel_index,

bool foreground,

const TabRendererData& data) {

BaseTab* tab = CreateTab();

tab->SetData(data);

TabData d = { tab, gfx::Rect() };

tab_data_.insert(tab_data_.begin() + ModelIndexToTabIndex(model_index), d);

AddChildView(tab);

// Don't animate the first tab, it looks weird, and don't animate anything

// if the containing window isn't visible yet.

if (tab_count() > 1 && GetWindow() && GetWindow()->IsVisible())

StartInsertTabAnimation(model_index, foreground);

else

Layout();

}

关闭tab时的动画:

void BaseTabStrip::StartRemoveTabAnimation(int model_index) {

PrepareForAnimation();

// Mark the tab as closing.

BaseTab* tab = GetBaseTabAtModelIndex(model_index);

tab->set_closing(true);

// Start an animation for the tabs.

GenerateIdealBounds();

AnimateToIdealBounds();

// Animate the tab being closed to 0x0.

gfx::Rect tab_bounds = tab->bounds();

if (type() == HORIZONTAL_TAB_STRIP)

tab__width(0);

else

tab__height(0);

bounds_animator_.AnimateViewTo(tab, tab_bounds);

// Register delegate to do cleanup when done, BoundsAnimator takes

// ownership of RemoveTabDelegate.

bounds_animator_.SetAnimationDelegate(tab, new RemoveTabDelegate(this, tab),

true);

}

void BaseTabStrip::RemoveAndDeleteTab(BaseTab* tab) {

int tab_data_index = TabIndexOfTab(tab);

DCHECK(tab_data_index != -1);

// Remove the Tab from the TabStrip'

tab_data_.erase(tab_data_.begin() + tab_data_index);

delete tab;

}

九、主窗口的架构

1、概述

各个大类的组织均采用MVC的设计模式,view只负责界面显示与消息响应,具体的业

务逻辑都放在model中。

2、主要的类

标签栏:MVC TabStripModel BaseTabStrip BrowserTabStripController

工具栏:MV ToolbarModel ToolbarView

收藏栏:MV BookmarkBarView BookmarkModel

主窗口:MVC Browser BrowserWindow BrowserFrameWin BrowserRootView

页面内查找窗口:MVC FindBar FindBarController

页面: TabContents TabContentsView

主窗口的类结构如下图所示:

主窗口各个部分的派生关系图:

十、思考与借鉴意义

1、 类的设计与划分。类的属性与功能设计非常合理,正确的运用组合、继承、设计模式,

各个类的功能独立,模块性良好。

2、 类的接口设计。类的接口设计完备、无冗余、

3、 窗口的设计与组织。窗口体系的设计上,客户区与非客户区严格分开,分别处理自己的

事情。

4、 View在底层提供了对快捷键、焦点、拖拽等基础功能的支持,这使得派生类可以很方便

是拥有这些功能

5、 窗口的风格统一计算,创建和销毁有统一的流程。

6、 绘制上,绘制与窗口系统严格分开,由Canvas进行处理,提供的裁剪功能可以保证高效

的重绘。

7、 设计模式的使用。

MVC

在chrome中,复杂的UI元素,如地址栏、标签栏、搜藏栏、各种对话框、主窗口等均

采用了MVC设计模式,实现界面(排版、绘制、界面消息响应)与逻辑(内部逻辑业务数

据)的分离;简单的界面元素,如按钮、进度条等没有必要做逻辑与UI的分离。UI与逻辑

的分离,不仅仅是排版与绘制与逻辑分离,还包括用户输入、各种界面消息及响应与逻辑的

分离。

Listener

各种控件(如Button)的消息响应采用了listener模式,需要关注该事件的类从该listener

类派生,重写消息响应函数即可。这和我们的代码类似。

Delegate

委托模式使得一个对象可以把他的事情交给代理去做,和这个对象相关的对象只要和该

对象的代理打交道。例如Window类有其代理类WindowDelegate。Delegate模式的一大好

处就是代码复用。

Command

线程模型的任务队列采用了command模式,在一个地方创建一个任务,而任务的执行

则交给另外一个地方处理,只要从Task派生,重写Run函数,就可以执行各种不同的任务。

8、 为调试、单元测试提供支持,核心的模块都有单元测试。排版、绘制、消息循环等关键

的代码都有测试代码。

DirectUI的基本思想

自己虚拟出windows的窗口和控件,自己负责排版、绘制、消息分发、事件响应与传

统的界面编程比,更灵活高效。

主要内容:

(1) windows的窗口与消息机制

(2) GDI

Windows程序的基本原理:

消息循环

GetMessage

PeekMessage

PostMessage

SendMessage

窗口风格

WS_CHILD

WS_POPUP

WS_OVERLAPPED

WS_CLIPCHILD

WS_CLIPSIBLING

WS_EX_TOOLWINDOW

模态对话框的实现