1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/dropdown_bar_host.h"
9 #include "chrome/browser/ui/view_ids.h"
10 #include "chrome/browser/ui/views/dropdown_bar_host_delegate.h"
11 #include "chrome/browser/ui/views/dropdown_bar_view.h"
12 #include "chrome/browser/ui/views/frame/browser_view.h"
13 #include "ui/events/keycodes/keyboard_codes.h"
14 #include "ui/gfx/animation/slide_animation.h"
15 #include "ui/gfx/scrollbar_size.h"
16 #include "ui/views/focus/external_focus_tracker.h"
17 #include "ui/views/focus/view_storage.h"
18 #include "ui/views/widget/widget.h"
21 bool DropdownBarHost::disable_animations_during_testing_
= false;
23 ////////////////////////////////////////////////////////////////////////////////
24 // DropdownBarHost, public:
26 DropdownBarHost::DropdownBarHost(BrowserView
* browser_view
)
27 : browser_view_(browser_view
),
32 esc_accel_target_registered_(false),
36 void DropdownBarHost::Init(views::View
* host_view
,
38 DropdownBarHostDelegate
* delegate
) {
45 // Initialize the host.
46 host_
.reset(new views::Widget
);
47 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_CONTROL
);
48 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
49 params
.parent
= browser_view_
->GetWidget()->GetNativeView();
50 params
.opacity
= views::Widget::InitParams::TRANSLUCENT_WINDOW
;
52 host_
->SetContentsView(view_
);
54 SetHostViewNative(host_view
);
56 // Start listening to focus changes, so we can register and unregister our
57 // own handler for Escape.
58 focus_manager_
= host_
->GetFocusManager();
60 focus_manager_
->AddFocusChangeListener(this);
62 // In some cases (see bug http://crbug.com/17056) it seems we may not have
63 // a focus manager. Please reopen the bug if you hit this.
67 // Start the process of animating the opening of the widget.
68 animation_
.reset(new gfx::SlideAnimation(this));
71 DropdownBarHost::~DropdownBarHost() {
72 focus_manager_
->RemoveFocusChangeListener(this);
73 focus_tracker_
.reset(NULL
);
76 void DropdownBarHost::Show(bool animate
) {
77 // Stores the currently focused view, and tracks focus changes so that we can
78 // restore focus when the dropdown widget is closed.
79 focus_tracker_
.reset(new views::ExternalFocusTracker(view_
, focus_manager_
));
81 bool was_visible
= is_visible_
;
83 if (!animate
|| disable_animations_during_testing_
) {
85 AnimationProgressed(animation_
.get());
86 } else if (!was_visible
) {
87 // Don't re-start the animation.
93 OnVisibilityChanged();
96 void DropdownBarHost::SetFocusAndSelection() {
97 delegate_
->SetFocusAndSelection(true);
100 bool DropdownBarHost::IsAnimating() const {
101 return animation_
->is_animating();
104 void DropdownBarHost::Hide(bool animate
) {
107 if (animate
&& !disable_animations_during_testing_
&&
108 !animation_
->IsClosing()) {
111 if (animation_
->IsClosing()) {
112 // If we're in the middle of a close animation, skip immediately to the
113 // end of the animation.
116 // Otherwise we need to set both the animation state to ended and the
117 // DropdownBarHost state to ended/hidden, otherwise the next time we try
118 // to show the bar, it might refuse to do so. Note that we call
119 // AnimationEnded ourselves as Reset does not call it if we are not
122 AnimationEnded(animation_
.get());
127 void DropdownBarHost::StopAnimation() {
131 bool DropdownBarHost::IsVisible() const {
135 ////////////////////////////////////////////////////////////////////////////////
136 // DropdownBarHost, views::FocusChangeListener implementation:
137 void DropdownBarHost::OnWillChangeFocus(views::View
* focused_before
,
138 views::View
* focused_now
) {
139 // First we need to determine if one or both of the views passed in are child
140 // views of our view.
141 bool our_view_before
= focused_before
&& view_
->Contains(focused_before
);
142 bool our_view_now
= focused_now
&& view_
->Contains(focused_now
);
144 // When both our_view_before and our_view_now are false, it means focus is
145 // changing hands elsewhere in the application (and we shouldn't do anything).
146 // Similarly, when both are true, focus is changing hands within the dropdown
147 // widget (and again, we should not do anything). We therefore only need to
148 // look at when we gain initial focus and when we loose it.
149 if (!our_view_before
&& our_view_now
) {
150 // We are gaining focus from outside the dropdown widget so we must register
151 // a handler for Escape.
152 RegisterAccelerators();
153 } else if (our_view_before
&& !our_view_now
) {
154 // We are losing focus to something outside our widget so we restore the
155 // original handler for Escape.
156 UnregisterAccelerators();
160 void DropdownBarHost::OnDidChangeFocus(views::View
* focused_before
,
161 views::View
* focused_now
) {
164 ////////////////////////////////////////////////////////////////////////////////
165 // DropdownBarHost, gfx::AnimationDelegate implementation:
167 void DropdownBarHost::AnimationProgressed(const gfx::Animation
* animation
) {
168 // First, we calculate how many pixels to slide the widget.
169 gfx::Size pref_size
= view_
->GetPreferredSize();
170 animation_offset_
= static_cast<int>((1.0 - animation_
->GetCurrentValue()) *
173 // This call makes sure it appears in the right location, the size and shape
174 // is correct and that it slides in the right direction.
175 gfx::Rect dlg_rect
= GetDialogPosition(gfx::Rect());
176 SetDialogPosition(dlg_rect
);
178 // Let the view know if we are animating, and at which offset to draw the
180 delegate_
->SetAnimationOffset(animation_offset_
);
181 view_
->SchedulePaint();
184 void DropdownBarHost::AnimationEnded(const gfx::Animation
* animation
) {
185 // Place the dropdown widget in its fully opened state.
186 animation_offset_
= 0;
188 if (!animation_
->IsShowing()) {
189 // Animation has finished closing.
192 OnVisibilityChanged();
194 // Animation has finished opening.
198 ////////////////////////////////////////////////////////////////////////////////
199 // DropdownBarHost protected:
201 void DropdownBarHost::ResetFocusTracker() {
202 focus_tracker_
.reset(NULL
);
205 void DropdownBarHost::OnVisibilityChanged() {
208 void DropdownBarHost::GetWidgetBounds(gfx::Rect
* bounds
) {
210 *bounds
= browser_view_
->bounds();
213 void DropdownBarHost::RegisterAccelerators() {
214 DCHECK(!esc_accel_target_registered_
);
215 ui::Accelerator
escape(ui::VKEY_ESCAPE
, ui::EF_NONE
);
216 focus_manager_
->RegisterAccelerator(
217 escape
, ui::AcceleratorManager::kNormalPriority
, this);
218 esc_accel_target_registered_
= true;
221 void DropdownBarHost::UnregisterAccelerators() {
222 DCHECK(esc_accel_target_registered_
);
223 ui::Accelerator
escape(ui::VKEY_ESCAPE
, ui::EF_NONE
);
224 focus_manager_
->UnregisterAccelerator(escape
, this);
225 esc_accel_target_registered_
= false;