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
!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
周期性定时器。
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
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
动画容器的指针
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
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
模态对话框的实现
发布评论