Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / views / dropdown_bar_host.cc
blob6360b5d89ed91022c086324c1d0ff58abb68435d
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"
7 #include <algorithm>
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"
20 // static
21 bool DropdownBarHost::disable_animations_during_testing_ = false;
23 ////////////////////////////////////////////////////////////////////////////////
24 // DropdownBarHost, public:
26 DropdownBarHost::DropdownBarHost(BrowserView* browser_view)
27 : browser_view_(browser_view),
28 view_(NULL),
29 delegate_(NULL),
30 animation_offset_(0),
31 focus_manager_(NULL),
32 esc_accel_target_registered_(false),
33 is_visible_(false) {
36 void DropdownBarHost::Init(views::View* host_view,
37 views::View* view,
38 DropdownBarHostDelegate* delegate) {
39 DCHECK(view);
40 DCHECK(delegate);
42 view_ = view;
43 delegate_ = 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;
51 host_->Init(params);
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();
59 if (focus_manager_) {
60 focus_manager_->AddFocusChangeListener(this);
61 } else {
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.
64 NOTREACHED();
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_;
82 is_visible_ = true;
83 if (!animate || disable_animations_during_testing_) {
84 animation_->Reset(1);
85 AnimationProgressed(animation_.get());
86 } else if (!was_visible) {
87 // Don't re-start the animation.
88 animation_->Reset();
89 animation_->Show();
92 if (!was_visible)
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) {
105 if (!IsVisible())
106 return;
107 if (animate && !disable_animations_during_testing_ &&
108 !animation_->IsClosing()) {
109 animation_->Hide();
110 } else {
111 if (animation_->IsClosing()) {
112 // If we're in the middle of a close animation, skip immediately to the
113 // end of the animation.
114 StopAnimation();
115 } else {
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
120 // animating here.
121 animation_->Reset();
122 AnimationEnded(animation_.get());
127 void DropdownBarHost::StopAnimation() {
128 animation_->End();
131 bool DropdownBarHost::IsVisible() const {
132 return is_visible_;
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()) *
171 pref_size.height());
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
179 // edges.
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.
190 host_->Hide();
191 is_visible_ = false;
192 OnVisibilityChanged();
193 } else {
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) {
209 DCHECK(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;