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); | |
sky
2012/11/29 19:30:23
Won't this try to paint all the children of the fr
James Cook
2012/11/29 21:20:59
I need all the children of the frame view to be pa
| |
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 |