OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/views/immersive_mode_controller.h" | 5 #include "chrome/browser/ui/views/immersive_mode_controller.h" |
6 | 6 |
| 7 #include "chrome/browser/ui/views/frame/browser_frame.h" |
7 #include "chrome/browser/ui/views/frame/browser_view.h" | 8 #include "chrome/browser/ui/views/frame/browser_view.h" |
8 #include "chrome/browser/ui/views/frame/contents_container.h" | 9 #include "chrome/browser/ui/views/frame/contents_container.h" |
9 #include "chrome/browser/ui/views/tabs/tab_strip.h" | 10 #include "chrome/browser/ui/views/tabs/tab_strip.h" |
| 11 #include "chrome/browser/ui/views/toolbar_view.h" |
| 12 #include "ui/compositor/scoped_layer_animation_settings.h" |
| 13 #include "ui/gfx/transform.h" |
10 #include "ui/views/mouse_watcher_view_host.h" | 14 #include "ui/views/mouse_watcher_view_host.h" |
| 15 #include "ui/views/view.h" |
| 16 #include "ui/views/window/non_client_view.h" |
11 | 17 |
12 #if defined(USE_AURA) | 18 #if defined(USE_AURA) |
13 #include "ui/aura/window.h" | 19 #include "ui/aura/window.h" |
14 #endif | 20 #endif |
15 | 21 |
16 namespace { | 22 namespace { |
17 | 23 |
18 // Time after which the edge trigger fires and top-chrome is revealed. | 24 // Time after which the edge trigger fires and top-chrome is revealed. |
19 const int kRevealDelayMs = 200; | 25 const int kRevealDelayMs = 200; |
20 | 26 |
21 // During an immersive-mode top-of-window reveal, how long to wait after the | 27 // During an immersive-mode top-of-window reveal, how long to wait after the |
22 // mouse exits the views before hiding them again. | 28 // mouse exits the views before hiding them again. |
23 const int kHideDelayMs = 200; | 29 const int kHideDelayMs = 200; |
24 | 30 |
25 } // namespace | 31 } // namespace |
26 | 32 |
| 33 //////////////////////////////////////////////////////////////////////////////// |
| 34 |
| 35 // View to hold the tab strip, toolbar, and sometimes the bookmark bar during |
| 36 // an immersive mode reveal. Paints on top of other layers in order to appear |
| 37 // over the web contents. Immersive mode uses this view to avoid changing the |
| 38 // BrowserView's view structure in the steady state. |
| 39 // TODO(jamescook): If immersive mode becomes non-experimental, use a permanent |
| 40 // top-of-window container view in BrowserView instead of RevealView to avoid |
| 41 // reparenting. |
| 42 // TODO(jamescook): Bookmark bar does not yet work. |
| 43 class RevealView : public views::View { |
| 44 public: |
| 45 explicit RevealView(BrowserView* browser_view); |
| 46 virtual ~RevealView(); |
| 47 |
| 48 // Reparents the |browser_view_| tab strip, toolbar, and bookmark bar to |
| 49 // this view. |
| 50 void AcquireTopViews(); |
| 51 |
| 52 // Reparents tab strip, toolbar, and bookmark bar back to |browser_view_|. |
| 53 void ReleaseTopViews(); |
| 54 |
| 55 // views::View overrides: |
| 56 virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE; |
| 57 |
| 58 private: |
| 59 // None of these views are owned. |
| 60 BrowserView* browser_view_; |
| 61 TabStrip* tabstrip_; |
| 62 ToolbarView* toolbar_view_; |
| 63 |
| 64 DISALLOW_COPY_AND_ASSIGN(RevealView); |
| 65 }; |
| 66 |
| 67 RevealView::RevealView(BrowserView* browser_view) |
| 68 : browser_view_(browser_view), |
| 69 tabstrip_(NULL), |
| 70 toolbar_view_(NULL) { |
| 71 SetPaintToLayer(true); |
| 72 SetFillsBoundsOpaquely(true); |
| 73 } |
| 74 |
| 75 RevealView::~RevealView() {} |
| 76 |
| 77 void RevealView::AcquireTopViews() { |
| 78 // Reparenting causes hit tests that require a parent for |this|. |
| 79 DCHECK(parent()); |
| 80 |
| 81 tabstrip_ = browser_view_->tabstrip(); |
| 82 toolbar_view_ = browser_view_->GetToolbarView(); |
| 83 |
| 84 // Ensure the indices are what we expect before we start moving the views. |
| 85 DCHECK_EQ(browser_view_->GetIndexOf(tabstrip_), BrowserView::kTabstripIndex); |
| 86 DCHECK_EQ(browser_view_->GetIndexOf(toolbar_view_), |
| 87 BrowserView::kToolbarIndex); |
| 88 |
| 89 AddChildView(tabstrip_); |
| 90 AddChildView(toolbar_view_); |
| 91 |
| 92 // Set our initial bounds, which triggers a Layout(). |
| 93 int width = parent()->width(); |
| 94 int height = toolbar_view_->bounds().bottom(); |
| 95 SetBounds(0, 0, width, height); |
| 96 } |
| 97 |
| 98 void RevealView::ReleaseTopViews() { |
| 99 // Reparenting causes hit tests that require a parent for |this|. |
| 100 DCHECK(parent()); |
| 101 |
| 102 browser_view_->AddChildViewAt(tabstrip_, BrowserView::kTabstripIndex); |
| 103 browser_view_->AddChildViewAt(toolbar_view_, BrowserView::kToolbarIndex); |
| 104 |
| 105 // Ensure the newly restored views get painted. |
| 106 tabstrip_->SchedulePaint(); |
| 107 toolbar_view_->SchedulePaint(); |
| 108 |
| 109 tabstrip_ = NULL; |
| 110 toolbar_view_ = NULL; |
| 111 } |
| 112 |
| 113 void RevealView::PaintChildren(gfx::Canvas* canvas) { |
| 114 // Top-views depend on parts of the frame (themes, window buttons) being |
| 115 // painted underneath them. Clip rect has already been set to the bounds |
| 116 // of this view, so just paint the frame. |
| 117 views::View* frame = browser_view_->frame()->GetFrameView(); |
| 118 frame->Paint(canvas); |
| 119 |
| 120 views::View::PaintChildren(canvas); |
| 121 } |
| 122 |
| 123 //////////////////////////////////////////////////////////////////////////////// |
| 124 |
27 ImmersiveModeController::ImmersiveModeController(BrowserView* browser_view) | 125 ImmersiveModeController::ImmersiveModeController(BrowserView* browser_view) |
28 : browser_view_(browser_view), | 126 : browser_view_(browser_view), |
29 enabled_(false), | 127 enabled_(false), |
30 hide_top_views_(false) { | 128 revealed_(false) { |
31 } | 129 } |
32 | 130 |
33 ImmersiveModeController::~ImmersiveModeController() {} | 131 ImmersiveModeController::~ImmersiveModeController() { |
| 132 // Ensure views are reparented if we are deleted mid-reveal. |
| 133 if (reveal_view_.get()) { |
| 134 reveal_view_->ReleaseTopViews(); |
| 135 ResetRevealView(); |
| 136 } |
| 137 } |
34 | 138 |
35 void ImmersiveModeController::SetEnabled(bool enabled) { | 139 void ImmersiveModeController::SetEnabled(bool enabled) { |
36 if (enabled_ == enabled) | 140 if (enabled_ == enabled) |
37 return; | 141 return; |
38 enabled_ = enabled; | 142 enabled_ = enabled; |
39 hide_top_views_ = enabled; | 143 |
40 browser_view_->tabstrip()->SetImmersiveStyle(enabled_); | 144 if (enabled_) { |
41 browser_view_->Layout(); | 145 browser_view_->tabstrip()->SetImmersiveStyle(true); |
| 146 browser_view_->Layout(); |
| 147 } else { |
| 148 // Don't Layout() browser_view_ because EndReveal() does so. |
| 149 EndReveal(false); |
| 150 top_timer_.Stop(); |
| 151 } |
42 | 152 |
43 #if defined(USE_AURA) | 153 #if defined(USE_AURA) |
44 // TODO(jamescook): If we want to port this feature to non-Aura views we'll | 154 // TODO(jamescook): If we want to port this feature to non-Aura views we'll |
45 // need a method to monitor incoming mouse move events without handling them. | 155 // need a method to monitor incoming mouse move events without handling them. |
46 // Currently views uses GetEventHandlerForPoint() to route events directly | 156 // Currently views uses GetEventHandlerForPoint() to route events directly |
47 // to either a tab or the caption area, bypassing pre-target handlers and | 157 // to either a tab or the caption area, bypassing pre-target handlers and |
48 // intermediate views. | 158 // intermediate views. |
49 if (enabled_) | 159 if (enabled_) |
50 browser_view_->GetNativeWindow()->AddPreTargetHandler(this); | 160 browser_view_->GetNativeWindow()->AddPreTargetHandler(this); |
51 else | 161 else |
52 browser_view_->GetNativeWindow()->RemovePreTargetHandler(this); | 162 browser_view_->GetNativeWindow()->RemovePreTargetHandler(this); |
53 #endif // defined(USE_AURA) | 163 #endif // defined(USE_AURA) |
54 | |
55 if (!enabled_) { | |
56 // Stop watching the mouse on disable. | |
57 mouse_watcher_.reset(); | |
58 top_timer_.Stop(); | |
59 } | |
60 } | |
61 | |
62 void ImmersiveModeController::RevealTopViews() { | |
63 if (!hide_top_views_) | |
64 return; | |
65 hide_top_views_ = false; | |
66 | |
67 // Recompute the bounds of the views when painted normally. | |
68 browser_view_->tabstrip()->SetImmersiveStyle(false); | |
69 browser_view_->Layout(); | |
70 | |
71 // Compute the top of the content area in order to find the bottom of all | |
72 // the top-of-window views like toolbar, bookmarks, etc. | |
73 gfx::Point content_origin(0, 0); | |
74 views::View::ConvertPointToTarget( | |
75 browser_view_->contents(), browser_view_, &content_origin); | |
76 | |
77 // Stop the immersive reveal when the mouse leaves the top-of-window area. | |
78 gfx::Insets insets(0, 0, content_origin.y() - browser_view_->height(), 0); | |
79 views::MouseWatcherViewHost* host = | |
80 new views::MouseWatcherViewHost(browser_view_, insets); | |
81 // MouseWatcher takes ownership of |host|. | |
82 mouse_watcher_.reset(new views::MouseWatcher(host, this)); | |
83 mouse_watcher_->set_notify_on_exit_time( | |
84 base::TimeDelta::FromMilliseconds(kHideDelayMs)); | |
85 mouse_watcher_->Start(); | |
86 } | 164 } |
87 | 165 |
88 // ui::EventHandler overrides: | 166 // ui::EventHandler overrides: |
89 ui::EventResult ImmersiveModeController::OnKeyEvent(ui::KeyEvent* event) { | 167 ui::EventResult ImmersiveModeController::OnKeyEvent(ui::KeyEvent* event) { |
90 return ui::ER_UNHANDLED; | 168 return ui::ER_UNHANDLED; |
91 } | 169 } |
92 | 170 |
93 ui::EventResult ImmersiveModeController::OnMouseEvent(ui::MouseEvent* event) { | 171 ui::EventResult ImmersiveModeController::OnMouseEvent(ui::MouseEvent* event) { |
| 172 if (event->type() != ui::ET_MOUSE_MOVED) |
| 173 return ui::ER_UNHANDLED; |
94 if (event->location().y() == 0) { | 174 if (event->location().y() == 0) { |
95 // Use a timer to detect if the cursor stays at the top past a delay. | 175 // Use a timer to detect if the cursor stays at the top past a delay. |
96 if (!top_timer_.IsRunning()) { | 176 if (!top_timer_.IsRunning()) { |
97 top_timer_.Start(FROM_HERE, | 177 top_timer_.Start(FROM_HERE, |
98 base::TimeDelta::FromMilliseconds(kRevealDelayMs), | 178 base::TimeDelta::FromMilliseconds(kRevealDelayMs), |
99 this, &ImmersiveModeController::RevealTopViews); | 179 this, &ImmersiveModeController::StartReveal); |
100 } | 180 } |
101 } else { | 181 } else { |
102 // Cursor left the top edge. | 182 // Cursor left the top edge. |
103 top_timer_.Stop(); | 183 top_timer_.Stop(); |
104 } | 184 } |
105 // Pass along event for further handling. | 185 // Pass along event for further handling. |
106 return ui::ER_UNHANDLED; | 186 return ui::ER_UNHANDLED; |
107 } | 187 } |
108 | 188 |
109 ui::EventResult ImmersiveModeController::OnScrollEvent(ui::ScrollEvent* event) { | 189 ui::EventResult ImmersiveModeController::OnScrollEvent(ui::ScrollEvent* event) { |
110 return ui::ER_UNHANDLED; | 190 return ui::ER_UNHANDLED; |
111 } | 191 } |
112 | 192 |
113 ui::EventResult ImmersiveModeController::OnTouchEvent(ui::TouchEvent* event) { | 193 ui::EventResult ImmersiveModeController::OnTouchEvent(ui::TouchEvent* event) { |
114 return ui::ER_UNHANDLED; | 194 return ui::ER_UNHANDLED; |
115 } | 195 } |
116 | 196 |
117 ui::EventResult ImmersiveModeController::OnGestureEvent( | 197 ui::EventResult ImmersiveModeController::OnGestureEvent( |
118 ui::GestureEvent* event) { | 198 ui::GestureEvent* event) { |
119 return ui::ER_UNHANDLED; | 199 return ui::ER_UNHANDLED; |
120 } | 200 } |
121 | 201 |
122 // views::MouseWatcherListener overrides: | 202 // views::MouseWatcherListener overrides: |
123 void ImmersiveModeController::MouseMovedOutOfHost() { | 203 void ImmersiveModeController::MouseMovedOutOfHost() { |
124 // Stop watching the mouse. | 204 EndReveal(true); |
| 205 } |
| 206 |
| 207 // ui::ImplicitAnimationObserver overrides: |
| 208 void ImmersiveModeController::OnImplicitAnimationsCompleted() { |
| 209 // Fired when the slide-out animation completes. |
| 210 ResetRevealView(); |
| 211 } |
| 212 |
| 213 // Testing interface: |
| 214 void ImmersiveModeController::StartRevealForTest() { |
| 215 StartReveal(); |
| 216 } |
| 217 |
| 218 void ImmersiveModeController::EndRevealForTest() { |
| 219 EndReveal(false); |
| 220 } |
| 221 |
| 222 //////////////////////////////////////////////////////////////////////////////// |
| 223 // private: |
| 224 |
| 225 void ImmersiveModeController::StartReveal() { |
| 226 if (revealed_) |
| 227 return; |
| 228 revealed_ = true; |
| 229 |
| 230 // Recompute the bounds of the views when painted normally. |
| 231 browser_view_->tabstrip()->SetImmersiveStyle(false); |
| 232 browser_view_->Layout(); |
| 233 |
| 234 // Place tabstrip, toolbar, and bookmarks bar in a new view at the end of |
| 235 // the BrowserView hierarchy so it paints over the web contents. |
| 236 reveal_view_.reset(new RevealView(browser_view_)); |
| 237 browser_view_->AddChildView(reveal_view_.get()); |
| 238 reveal_view_->AcquireTopViews(); |
| 239 |
| 240 // Slide in the reveal view. |
| 241 AnimateShowRevealView(); |
| 242 |
| 243 // Stop the immersive reveal when the mouse leaves the top-of-window area. |
| 244 StartMouseWatcher(); |
| 245 } |
| 246 |
| 247 void ImmersiveModeController::AnimateShowRevealView() { |
| 248 DCHECK(reveal_view_.get()); |
| 249 gfx::Transform transform; |
| 250 transform.Translate(0, -reveal_view_->height()); |
| 251 reveal_view_->SetTransform(transform); |
| 252 |
| 253 ui::ScopedLayerAnimationSettings settings( |
| 254 reveal_view_->layer()->GetAnimator()); |
| 255 settings.SetTweenType(ui::Tween::EASE_OUT); |
| 256 reveal_view_->SetTransform(gfx::Transform()); |
| 257 } |
| 258 |
| 259 void ImmersiveModeController::StartMouseWatcher() { |
| 260 DCHECK(reveal_view_.get()); |
| 261 views::MouseWatcherViewHost* host = |
| 262 new views::MouseWatcherViewHost(reveal_view_.get(), gfx::Insets()); |
| 263 // MouseWatcher takes ownership of |host|. |
| 264 mouse_watcher_.reset(new views::MouseWatcher(host, this)); |
| 265 mouse_watcher_->set_notify_on_exit_time( |
| 266 base::TimeDelta::FromMilliseconds(kHideDelayMs)); |
| 267 mouse_watcher_->Start(); |
| 268 } |
| 269 |
| 270 void ImmersiveModeController::EndReveal(bool animate) { |
| 271 revealed_ = false; |
125 mouse_watcher_.reset(); | 272 mouse_watcher_.reset(); |
126 // Stop showing the top views. | 273 |
127 hide_top_views_ = true; | 274 if (reveal_view_.get()) { |
128 browser_view_->tabstrip()->SetImmersiveStyle(true); | 275 reveal_view_->ReleaseTopViews(); |
| 276 if (animate) { |
| 277 // Animation resets the reveal view when complete. |
| 278 AnimateHideRevealView(); |
| 279 } else { |
| 280 ResetRevealView(); |
| 281 } |
| 282 } |
| 283 |
| 284 browser_view_->tabstrip()->SetImmersiveStyle(enabled_); |
129 browser_view_->Layout(); | 285 browser_view_->Layout(); |
130 } | 286 } |
| 287 |
| 288 void ImmersiveModeController::AnimateHideRevealView() { |
| 289 ui::Layer* layer = reveal_view_->layer(); |
| 290 ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); |
| 291 settings.SetTweenType(ui::Tween::EASE_OUT); |
| 292 settings.AddObserver(this); // Resets |reveal_view_| on completion. |
| 293 gfx::Transform transform; |
| 294 transform.Translate(0, -layer->bounds().height()); |
| 295 layer->SetTransform(transform); |
| 296 } |
| 297 |
| 298 void ImmersiveModeController::ResetRevealView() { |
| 299 browser_view_->RemoveChildView(reveal_view_.get()); |
| 300 reveal_view_.reset(); |
| 301 } |
| 302 |
OLD | NEW |