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, false);
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;