Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(169)

Side by Side Diff: chrome/browser/ui/views/immersive_mode_controller.cc

Issue 12754010: Move immersive_mode_controller into c/b/ui/views/frame (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 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.
4
5 #include "chrome/browser/ui/views/immersive_mode_controller.h"
6
7 #include "chrome/browser/ui/views/frame/browser_view.h"
8 #include "chrome/browser/ui/views/frame/top_container_view.h"
9 #include "chrome/browser/ui/views/tabs/tab_strip.h"
10 #include "chrome/common/chrome_switches.h"
11 #include "ui/compositor/layer_animation_observer.h"
12 #include "ui/compositor/scoped_layer_animation_settings.h"
13 #include "ui/gfx/transform.h"
14 #include "ui/views/view.h"
15 #include "ui/views/widget/widget.h"
16 #include "ui/views/window/non_client_view.h"
17
18 #if defined(USE_ASH)
19 #include "ash/ash_switches.h"
20 #include "ash/shell.h"
21 #include "ash/wm/window_properties.h"
22 #include "base/command_line.h"
23 #endif
24
25 #if defined(USE_AURA)
26 #include "ui/aura/client/aura_constants.h"
27 #include "ui/aura/window.h"
28 #include "ui/aura/window_observer.h"
29 #endif
30
31 using views::View;
32
33 namespace {
34
35 // Time after which the edge trigger fires and top-chrome is revealed. This is
36 // after the mouse stops moving.
37 const int kTopEdgeRevealDelayMs = 200;
38
39 // Duration for the reveal show/hide slide animation. The slower duration is
40 // used for the initial slide out to give the user more change to see what
41 // happened.
42 const int kRevealSlowAnimationDurationMs = 400;
43 const int kRevealFastAnimationDurationMs = 200;
44
45 } // namespace
46
47 ////////////////////////////////////////////////////////////////////////////////
48
49 #if defined(USE_AURA)
50 // Observer to watch for window restore. views::Widget does not provide a hook
51 // to observe for window restore, so do this at the Aura level.
52 class ImmersiveModeController::WindowObserver : public aura::WindowObserver {
53 public:
54 explicit WindowObserver(ImmersiveModeController* controller)
55 : controller_(controller) {
56 controller_->native_window_->AddObserver(this);
57 }
58
59 virtual ~WindowObserver() {
60 controller_->native_window_->RemoveObserver(this);
61 }
62
63 // aura::WindowObserver overrides:
64 virtual void OnWindowPropertyChanged(aura::Window* window,
65 const void* key,
66 intptr_t old) OVERRIDE {
67 using aura::client::kShowStateKey;
68 if (key == kShowStateKey) {
69 // Disable immersive mode when leaving the fullscreen state.
70 if (window->GetProperty(kShowStateKey) != ui::SHOW_STATE_FULLSCREEN)
71 controller_->SetEnabled(false);
72 return;
73 }
74 #if defined(USE_ASH)
75 using ash::internal::kImmersiveModeKey;
76 if (key == kImmersiveModeKey) {
77 // Another component has toggled immersive mode.
78 controller_->SetEnabled(window->GetProperty(kImmersiveModeKey));
79 return;
80 }
81 #endif
82 }
83
84 private:
85 ImmersiveModeController* controller_; // Not owned.
86
87 DISALLOW_COPY_AND_ASSIGN(WindowObserver);
88 };
89 #endif // defined(USE_AURA)
90
91 ////////////////////////////////////////////////////////////////////////////////
92
93 class ImmersiveModeController::AnimationObserver
94 : public ui::ImplicitAnimationObserver {
95 public:
96 enum AnimationType {
97 SLIDE_OPEN,
98 SLIDE_CLOSED,
99 };
100
101 AnimationObserver(ImmersiveModeController* controller, AnimationType type)
102 : controller_(controller), animation_type_(type) {}
103 virtual ~AnimationObserver() {}
104
105 // ui::ImplicitAnimationObserver overrides:
106 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
107 if (animation_type_ == SLIDE_OPEN)
108 controller_->OnSlideOpenAnimationCompleted();
109 else if (animation_type_ == SLIDE_CLOSED)
110 controller_->OnSlideClosedAnimationCompleted();
111 else
112 NOTREACHED();
113 }
114
115 private:
116 ImmersiveModeController* controller_;
117 AnimationType animation_type_;
118
119 DISALLOW_COPY_AND_ASSIGN(AnimationObserver);
120 };
121
122 ////////////////////////////////////////////////////////////////////////////////
123
124 ImmersiveModeController::ImmersiveModeController()
125 : browser_view_(NULL),
126 enabled_(false),
127 reveal_state_(CLOSED),
128 reveal_locked_(false),
129 hide_tab_indicators_(false),
130 reveal_hovered_(false),
131 native_window_(NULL) {
132 }
133
134 ImmersiveModeController::~ImmersiveModeController() {
135 // The browser view is being destroyed so there's no need to update its
136 // layout or layers, even if the top views are revealed. But the window
137 // observers still need to be removed.
138 EnableWindowObservers(false);
139 }
140
141 void ImmersiveModeController::Init(BrowserView* browser_view) {
142 browser_view_ = browser_view;
143 // Browser view is detached from its widget during destruction. Cache the
144 // window pointer so |this| can stop observing during destruction.
145 native_window_ = browser_view_->GetNativeWindow();
146 DCHECK(native_window_);
147 EnableWindowObservers(true);
148
149 slide_open_observer_.reset(
150 new AnimationObserver(this, AnimationObserver::SLIDE_OPEN));
151 slide_closed_observer_.reset(
152 new AnimationObserver(this, AnimationObserver::SLIDE_CLOSED));
153
154 #if defined(USE_ASH)
155 // Optionally allow the tab indicators to be hidden.
156 hide_tab_indicators_ = CommandLine::ForCurrentProcess()->
157 HasSwitch(ash::switches::kAshImmersiveHideTabIndicators);
158 #endif
159 }
160
161 // static
162 bool ImmersiveModeController::UseImmersiveFullscreen() {
163 #if defined(OS_CHROMEOS)
164 // Kiosk mode needs the whole screen.
165 CommandLine* command_line = CommandLine::ForCurrentProcess();
166 return !command_line->HasSwitch(switches::kKioskMode) &&
167 !command_line->HasSwitch(ash::switches::kAshDisableImmersiveMode);
168 #endif
169 return false;
170 }
171
172 void ImmersiveModeController::SetEnabled(bool enabled) {
173 DCHECK(browser_view_) << "Must initialize before enabling";
174 if (enabled_ == enabled)
175 return;
176 enabled_ = enabled;
177
178 TopContainerView* top_container = browser_view_->top_container();
179 if (enabled_) {
180 // Slide closed the reveal view on enable by slamming it to revealed then
181 // triggering an end-reveal animation.
182 reveal_state_ = REVEALED;
183 top_container->SetPaintToLayer(true);
184 top_container->SetFillsBoundsOpaquely(true);
185 // Layout updates at the end of the slide closed.
186 EndReveal(ANIMATE_SLOW);
187 } else {
188 // Stop cursor-at-top tracking.
189 top_timer_.Stop();
190 // Snap immediately to the closed state.
191 reveal_state_ = CLOSED;
192 top_container->SetFillsBoundsOpaquely(false);
193 top_container->SetPaintToLayer(false);
194 browser_view_->GetWidget()->non_client_view()->frame_view()->
195 ResetWindowControls();
196 browser_view_->tabstrip()->SetImmersiveStyle(false);
197 // Don't need explicit layout because we're inside a fullscreen transition
198 // and it blocks layout calls.
199 }
200
201 #if defined(USE_ASH)
202 // This causes a no-op call to SetEnabled() since enabled_ is already set.
203 native_window_->SetProperty(ash::internal::kImmersiveModeKey, enabled_);
204 // Ash on Windows may not have a shell.
205 if (ash::Shell::HasInstance()) {
206 // Shelf auto-hides in immersive mode.
207 ash::Shell::GetInstance()->UpdateShelfVisibility();
208 }
209 #endif
210 }
211
212 void ImmersiveModeController::MaybeStackViewAtTop() {
213 #if defined(USE_AURA)
214 if (enabled_ && reveal_state_ != CLOSED) {
215 ui::Layer* reveal_layer = browser_view_->top_container()->layer();
216 if (reveal_layer)
217 reveal_layer->parent()->StackAtTop(reveal_layer);
218 }
219 #endif
220 }
221
222 void ImmersiveModeController::MaybeStartReveal() {
223 if (enabled_ && reveal_state_ != REVEALED)
224 StartReveal(ANIMATE_FAST);
225 }
226
227 void ImmersiveModeController::CancelReveal() {
228 EndReveal(ANIMATE_NO);
229 }
230
231 void ImmersiveModeController::RevealAndLock(bool reveal) {
232 if (!enabled_)
233 return;
234 reveal_locked_ = reveal;
235 if (reveal_locked_ && reveal_state_ != REVEALED)
236 StartReveal(ANIMATE_FAST);
237 else if (!reveal_locked_ && reveal_state_ != CLOSED)
238 EndReveal(ANIMATE_FAST);
239 }
240
241 void ImmersiveModeController::OnRevealViewLostFocus() {
242 // Stop the reveal if the mouse is outside the reveal view.
243 if (!reveal_locked_ && !reveal_hovered_)
244 EndReveal(ANIMATE_FAST);
245 }
246
247 ////////////////////////////////////////////////////////////////////////////////
248
249 // ui::EventHandler overrides:
250 void ImmersiveModeController::OnMouseEvent(ui::MouseEvent* event) {
251 if (!enabled_ || event->type() != ui::ET_MOUSE_MOVED)
252 return;
253 if (event->location().y() == 0) {
254 // Start a reveal if the mouse touches the top of the screen and then stops
255 // moving for a little while. This mirrors the Ash launcher behavior.
256 top_timer_.Stop();
257 // Timer is stopped when |this| is destroyed, hence Unretained() is safe.
258 top_timer_.Start(FROM_HERE,
259 base::TimeDelta::FromMilliseconds(kTopEdgeRevealDelayMs),
260 base::Bind(&ImmersiveModeController::StartReveal,
261 base::Unretained(this),
262 ANIMATE_FAST));
263 } else {
264 // Cursor left the top edge.
265 top_timer_.Stop();
266 }
267
268 if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) {
269 // Look for the mouse leaving the bottom edge of the revealed view.
270 int bottom_edge = browser_view_->top_container()->bounds().bottom();
271 if (event->location().y() > bottom_edge) {
272 reveal_hovered_ = false;
273 OnRevealViewLostMouse();
274 } else {
275 reveal_hovered_ = true;
276 }
277 }
278
279 // Pass along event for further handling.
280 }
281
282 ////////////////////////////////////////////////////////////////////////////////
283 // Testing interface:
284
285 void ImmersiveModeController::SetHideTabIndicatorsForTest(bool hide) {
286 hide_tab_indicators_ = hide;
287 }
288
289 void ImmersiveModeController::StartRevealForTest() {
290 StartReveal(ANIMATE_NO);
291 }
292
293 void ImmersiveModeController::OnRevealViewLostMouseForTest() {
294 OnRevealViewLostMouse();
295 }
296
297 ////////////////////////////////////////////////////////////////////////////////
298 // private:
299
300 void ImmersiveModeController::EnableWindowObservers(bool enable) {
301 if (!native_window_) {
302 NOTREACHED() << "ImmersiveModeController not initialized";
303 return;
304 }
305 #if defined(USE_AURA)
306 // TODO(jamescook): Porting immersive mode to non-Aura views will require
307 // a method to monitor incoming mouse move events without handling them.
308 // Currently views uses GetEventHandlerForPoint() to route events directly
309 // to either a tab or the caption area, bypassing pre-target handlers and
310 // intermediate views.
311 if (enable)
312 native_window_->AddPreTargetHandler(this);
313 else
314 native_window_->RemovePreTargetHandler(this);
315
316 // The window observer adds and removes itself from the native window.
317 // TODO(jamescook): Porting to non-Aura will also require a method to monitor
318 // for window restore, which is not provided by views Widget.
319 window_observer_.reset(enable ? new WindowObserver(this) : NULL);
320 #endif // defined(USE_AURA)
321 }
322
323 void ImmersiveModeController::StartReveal(Animate animate) {
324 DCHECK_NE(ANIMATE_SLOW, animate);
325 if (reveal_state_ == CLOSED) {
326 reveal_state_ = SLIDING_OPEN;
327 // Turn on layer painting so we can smoothly animate.
328 TopContainerView* top_container = browser_view_->top_container();
329 top_container->SetPaintToLayer(true);
330 top_container->SetFillsBoundsOpaquely(true);
331
332 // Ensure window caption buttons are updated and the view bounds are
333 // computed at normal (non-immersive-style) size.
334 LayoutBrowserView(false);
335
336 // Slide in the reveal view.
337 if (animate != ANIMATE_NO)
338 AnimateSlideOpen(); // Show is always fast.
339 } else if (reveal_state_ == SLIDING_CLOSED) {
340 reveal_state_ = SLIDING_OPEN;
341 // Reverse the animation.
342 AnimateSlideOpen();
343 }
344 }
345
346 void ImmersiveModeController::LayoutBrowserView(bool immersive_style) {
347 // Update the window caption buttons.
348 browser_view_->GetWidget()->non_client_view()->frame_view()->
349 ResetWindowControls();
350 browser_view_->tabstrip()->SetImmersiveStyle(immersive_style);
351 browser_view_->Layout();
352 }
353
354 void ImmersiveModeController::AnimateSlideOpen() {
355 ui::Layer* layer = browser_view_->top_container()->layer();
356 // Stop any slide closed animation in progress.
357 layer->GetAnimator()->AbortAllAnimations();
358
359 gfx::Transform transform;
360 transform.Translate(0, -layer->bounds().height());
361 layer->SetTransform(transform);
362
363 ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
364 settings.AddObserver(slide_open_observer_.get());
365 settings.SetTweenType(ui::Tween::EASE_OUT);
366 settings.SetTransitionDuration(
367 base::TimeDelta::FromMilliseconds(kRevealFastAnimationDurationMs));
368 layer->SetTransform(gfx::Transform());
369 }
370
371 void ImmersiveModeController::OnSlideOpenAnimationCompleted() {
372 if (reveal_state_ == SLIDING_OPEN)
373 reveal_state_ = REVEALED;
374 }
375
376 void ImmersiveModeController::OnRevealViewLostMouse() {
377 // Stop the reveal if the top view's children don't have focus.
378 views::View* focused = browser_view_->GetFocusManager()->GetFocusedView();
379 if (!reveal_locked_ &&
380 !browser_view_->top_container()->Contains(focused))
381 EndReveal(ANIMATE_FAST);
382 }
383
384 void ImmersiveModeController::EndReveal(Animate animate) {
385 if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) {
386 reveal_state_ = SLIDING_CLOSED;
387 if (animate == ANIMATE_FAST)
388 AnimateSlideClosed(kRevealFastAnimationDurationMs);
389 else if (animate == ANIMATE_SLOW)
390 AnimateSlideClosed(kRevealSlowAnimationDurationMs);
391 else
392 OnSlideClosedAnimationCompleted();
393 }
394 }
395
396 void ImmersiveModeController::AnimateSlideClosed(int duration_ms) {
397 // Stop any slide open animation in progress, but don't skip to the end. This
398 // avoids a visual "pop" when starting a hide in the middle of a show.
399 ui::Layer* layer = browser_view_->top_container()->layer();
400 layer->GetAnimator()->AbortAllAnimations();
401
402 ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
403 settings.SetTweenType(ui::Tween::EASE_OUT);
404 settings.SetTransitionDuration(
405 base::TimeDelta::FromMilliseconds(duration_ms));
406 settings.AddObserver(slide_closed_observer_.get());
407 gfx::Transform transform;
408 transform.Translate(0, -layer->bounds().height());
409 layer->SetTransform(transform);
410 }
411
412 void ImmersiveModeController::OnSlideClosedAnimationCompleted() {
413 if (reveal_state_ == SLIDING_CLOSED) {
414 reveal_state_ = CLOSED;
415 TopContainerView* top_container = browser_view_->top_container();
416 // Layer isn't needed after animation completes.
417 top_container->SetFillsBoundsOpaquely(false);
418 top_container->SetPaintToLayer(false);
419 // Update tabstrip for closed state.
420 LayoutBrowserView(true);
421 }
422 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/views/immersive_mode_controller.h ('k') | chrome/browser/ui/views/immersive_mode_controller_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698