OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 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 | 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/frame/immersive_mode_controller_ash.h" | 5 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h" |
6 | 6 |
7 #include <set> | |
8 #include <vector> | |
9 | |
10 #include "ash/shell.h" | 7 #include "ash/shell.h" |
11 #include "ash/wm/window_state.h" | 8 #include "ash/wm/window_state.h" |
12 #include "chrome/browser/chrome_notification_types.h" | 9 #include "chrome/browser/chrome_notification_types.h" |
13 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h" | 10 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h" |
14 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h" | 11 #include "chrome/browser/ui/views/frame/browser_view.h" |
15 #include "chrome/browser/ui/views/frame/top_container_view.h" | 12 #include "chrome/browser/ui/views/frame/top_container_view.h" |
| 13 #include "chrome/browser/ui/views/tabs/tab_strip.h" |
16 #include "content/public/browser/notification_service.h" | 14 #include "content/public/browser/notification_service.h" |
17 #include "content/public/browser/web_contents.h" | 15 #include "content/public/browser/web_contents.h" |
18 #include "content/public/browser/web_contents_view.h" | 16 #include "content/public/browser/web_contents_view.h" |
19 #include "ui/aura/client/activation_client.h" | |
20 #include "ui/aura/client/aura_constants.h" | |
21 #include "ui/aura/client/capture_client.h" | |
22 #include "ui/aura/client/cursor_client.h" | |
23 #include "ui/aura/client/screen_position_client.h" | |
24 #include "ui/aura/env.h" | |
25 #include "ui/aura/root_window.h" | |
26 #include "ui/aura/window.h" | 17 #include "ui/aura/window.h" |
27 #include "ui/gfx/animation/slide_animation.h" | |
28 #include "ui/views/bubble/bubble_delegate.h" | |
29 #include "ui/views/view.h" | 18 #include "ui/views/view.h" |
30 #include "ui/views/widget/widget.h" | 19 #include "ui/views/widget/widget.h" |
31 #include "ui/views/window/non_client_view.h" | 20 #include "ui/views/window/non_client_view.h" |
32 | 21 |
33 using views::View; | |
34 | |
35 namespace { | 22 namespace { |
36 | 23 |
37 // The slide open/closed animation looks better if it starts and ends just a | 24 // Revealing the TopContainerView looks better if the animation starts and ends |
38 // few pixels before the view goes completely off the screen, which reduces | 25 // just a few pixels before the view goes offscreen, which reduces the visual |
39 // the visual "pop" as the 2-pixel tall immersive-style tabs become visible. | 26 // "pop" as the 3-pixel tall "light bar" style tab strip becomes visible. |
40 const int kAnimationOffsetY = 3; | 27 const int kAnimationOffsetY = 3; |
41 | 28 |
42 // Duration for the reveal show/hide slide animation. The slower duration is | 29 // The height of the region in pixels at the top edge of the screen in which to |
43 // used for the initial slide out to give the user more change to see what | 30 // steal touch events targetted at the web contents while in immersive |
44 // happened. | 31 // fullscreen. This region is used to allow us to get edge gestures even if the |
45 const int kRevealSlowAnimationDurationMs = 400; | 32 // web contents consumes all touch events. |
46 const int kRevealFastAnimationDurationMs = 200; | 33 const int kStealTouchEventsFromWebContentsRegionHeightPx = 8; |
47 | 34 |
48 // The delay in milliseconds between the mouse stopping at the top edge of the | 35 // Converts from ImmersiveModeController::AnimateReveal to |
49 // screen and the top-of-window views revealing. | 36 // ash::ImmersiveFullscreenController::AnimateReveal. |
50 const int kMouseRevealDelayMs = 200; | 37 ash::ImmersiveFullscreenController::AnimateReveal |
51 | 38 ToImmersiveFullscreenControllerAnimateReveal( |
52 // The maximum amount of pixels that the cursor can move for the cursor to be | 39 ImmersiveModeController::AnimateReveal animate_reveal) { |
53 // considered "stopped". This allows the user to reveal the top-of-window views | 40 switch (animate_reveal) { |
54 // without holding the cursor completely still. | 41 case ImmersiveModeController::ANIMATE_REVEAL_YES: |
55 const int kMouseRevealXThresholdPixels = 3; | 42 return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_YES; |
56 | 43 case ImmersiveModeController::ANIMATE_REVEAL_NO: |
57 // How many pixels a gesture can start away from |top_container_| when in | 44 return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO; |
58 // closed state and still be considered near it. This is needed to overcome | 45 } |
59 // issues with poor location values near the edge of the display. | 46 NOTREACHED(); |
60 const int kNearTopContainerDistance = 8; | 47 return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO; |
61 | |
62 // Used to multiply x value of an update in check to determine if gesture is | |
63 // vertical. This is used to make sure that gesture is close to vertical instead | |
64 // of just more vertical then horizontal. | |
65 const int kSwipeVerticalThresholdMultiplier = 3; | |
66 | |
67 // The height in pixels of the region above the top edge of the display which | |
68 // hosts the immersive fullscreen window in which mouse events are ignored | |
69 // (cannot reveal or unreveal the top-of-window views). | |
70 // See ShouldIgnoreMouseEventAtLocation() for more details. | |
71 const int kHeightOfDeadRegionAboveTopContainer = 10; | |
72 | |
73 // The height in pixels of the region below the top edge of the display in which | |
74 // the mouse can trigger revealing the top-of-window views. The height must be | |
75 // greater than 1px because the top pixel is used to trigger moving the cursor | |
76 // between displays if the user has a vertical display layout (primary display | |
77 // above/below secondary display). | |
78 const int kMouseRevealBoundsHeight = 3; | |
79 | |
80 // Returns the BubbleDelegateView corresponding to |maybe_bubble| if | |
81 // |maybe_bubble| is a bubble. | |
82 views::BubbleDelegateView* AsBubbleDelegate(aura::Window* maybe_bubble) { | |
83 if (!maybe_bubble) | |
84 return NULL; | |
85 views::Widget* widget = views::Widget::GetWidgetForNativeView(maybe_bubble); | |
86 if (!widget) | |
87 return NULL; | |
88 return widget->widget_delegate()->AsBubbleDelegate(); | |
89 } | 48 } |
90 | 49 |
91 // Returns true if |maybe_transient| is a transient child of |toplevel|. | |
92 bool IsWindowTransientChildOf(aura::Window* maybe_transient, | |
93 aura::Window* toplevel) { | |
94 if (!maybe_transient || !toplevel) | |
95 return false; | |
96 | |
97 for (aura::Window* window = maybe_transient; window; | |
98 window = window->transient_parent()) { | |
99 if (window == toplevel) | |
100 return true; | |
101 } | |
102 return false; | |
103 } | |
104 | |
105 // Returns the location of |event| in screen coordinates. | |
106 gfx::Point GetEventLocationInScreen(const ui::LocatedEvent& event) { | |
107 gfx::Point location_in_screen = event.location(); | |
108 aura::Window* target = static_cast<aura::Window*>(event.target()); | |
109 aura::client::ScreenPositionClient* screen_position_client = | |
110 aura::client::GetScreenPositionClient(target->GetRootWindow()); | |
111 screen_position_client->ConvertPointToScreen(target, &location_in_screen); | |
112 return location_in_screen; | |
113 } | |
114 | |
115 //////////////////////////////////////////////////////////////////////////////// | |
116 | |
117 class RevealedLockAsh : public ImmersiveRevealedLock { | |
118 public: | |
119 RevealedLockAsh(const base::WeakPtr<ImmersiveModeControllerAsh>& controller, | |
120 ImmersiveModeController::AnimateReveal animate_reveal) | |
121 : controller_(controller) { | |
122 DCHECK(controller_); | |
123 controller_->LockRevealedState(animate_reveal); | |
124 } | |
125 | |
126 virtual ~RevealedLockAsh() { | |
127 if (controller_) | |
128 controller_->UnlockRevealedState(); | |
129 } | |
130 | |
131 private: | |
132 base::WeakPtr<ImmersiveModeControllerAsh> controller_; | |
133 | |
134 DISALLOW_COPY_AND_ASSIGN(RevealedLockAsh); | |
135 }; | |
136 | |
137 } // namespace | 50 } // namespace |
138 | 51 |
139 //////////////////////////////////////////////////////////////////////////////// | |
140 | |
141 // Class which keeps the top-of-window views revealed as long as one of the | |
142 // bubbles it is observing is visible. The logic to keep the top-of-window | |
143 // views revealed based on the visibility of bubbles anchored to | |
144 // children of |ImmersiveModeController::top_container_| is separate from | |
145 // the logic related to |ImmersiveModeControllerAsh::focus_revealed_lock_| | |
146 // so that bubbles which are not activatable and bubbles which do not close | |
147 // upon deactivation also keep the top-of-window views revealed for the | |
148 // duration of their visibility. | |
149 class ImmersiveModeControllerAsh::BubbleManager : public aura::WindowObserver { | |
150 public: | |
151 explicit BubbleManager(ImmersiveModeControllerAsh* controller); | |
152 virtual ~BubbleManager(); | |
153 | |
154 // Start / stop observing changes to |bubble|'s visibility. | |
155 void StartObserving(aura::Window* bubble); | |
156 void StopObserving(aura::Window* bubble); | |
157 | |
158 private: | |
159 // Updates |revealed_lock_| based on whether any of |bubbles_| is visible. | |
160 void UpdateRevealedLock(); | |
161 | |
162 // aura::WindowObserver overrides: | |
163 virtual void OnWindowVisibilityChanged(aura::Window* window, | |
164 bool visible) OVERRIDE; | |
165 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; | |
166 | |
167 ImmersiveModeControllerAsh* controller_; | |
168 | |
169 std::set<aura::Window*> bubbles_; | |
170 | |
171 // Lock which keeps the top-of-window views revealed based on whether any of | |
172 // |bubbles_| is visible. | |
173 scoped_ptr<ImmersiveRevealedLock> revealed_lock_; | |
174 | |
175 DISALLOW_COPY_AND_ASSIGN(BubbleManager); | |
176 }; | |
177 | |
178 ImmersiveModeControllerAsh::BubbleManager::BubbleManager( | |
179 ImmersiveModeControllerAsh* controller) | |
180 : controller_(controller) { | |
181 } | |
182 | |
183 ImmersiveModeControllerAsh::BubbleManager::~BubbleManager() { | |
184 for (std::set<aura::Window*>::const_iterator it = bubbles_.begin(); | |
185 it != bubbles_.end(); ++it) { | |
186 (*it)->RemoveObserver(this); | |
187 } | |
188 } | |
189 | |
190 void ImmersiveModeControllerAsh::BubbleManager::StartObserving( | |
191 aura::Window* bubble) { | |
192 if (bubbles_.insert(bubble).second) { | |
193 bubble->AddObserver(this); | |
194 UpdateRevealedLock(); | |
195 } | |
196 } | |
197 | |
198 void ImmersiveModeControllerAsh::BubbleManager::StopObserving( | |
199 aura::Window* bubble) { | |
200 if (bubbles_.erase(bubble)) { | |
201 bubble->RemoveObserver(this); | |
202 UpdateRevealedLock(); | |
203 } | |
204 } | |
205 | |
206 void ImmersiveModeControllerAsh::BubbleManager::UpdateRevealedLock() { | |
207 bool has_visible_bubble = false; | |
208 for (std::set<aura::Window*>::const_iterator it = bubbles_.begin(); | |
209 it != bubbles_.end(); ++it) { | |
210 if ((*it)->IsVisible()) { | |
211 has_visible_bubble = true; | |
212 break; | |
213 } | |
214 } | |
215 | |
216 bool was_revealed = controller_->IsRevealed(); | |
217 if (has_visible_bubble) { | |
218 if (!revealed_lock_.get()) { | |
219 // Reveal the top-of-window views without animating because it looks | |
220 // weird for the top-of-window views to animate and the bubble not to | |
221 // animate along with the top-of-window views. | |
222 revealed_lock_.reset(controller_->GetRevealedLock( | |
223 ImmersiveModeController::ANIMATE_REVEAL_NO)); | |
224 } | |
225 } else { | |
226 revealed_lock_.reset(); | |
227 } | |
228 | |
229 if (!was_revealed && revealed_lock_.get()) { | |
230 // Currently, there is no nice way for bubbles to reposition themselves | |
231 // whenever the anchor view moves. Tell the bubbles to reposition themselves | |
232 // explicitly instead. The hidden bubbles are also repositioned because | |
233 // BubbleDelegateView does not reposition its widget as a result of a | |
234 // visibility change. | |
235 for (std::set<aura::Window*>::const_iterator it = bubbles_.begin(); | |
236 it != bubbles_.end(); ++it) { | |
237 AsBubbleDelegate(*it)->OnAnchorViewBoundsChanged(); | |
238 } | |
239 } | |
240 } | |
241 | |
242 void ImmersiveModeControllerAsh::BubbleManager::OnWindowVisibilityChanged( | |
243 aura::Window*, | |
244 bool visible) { | |
245 UpdateRevealedLock(); | |
246 } | |
247 | |
248 void ImmersiveModeControllerAsh::BubbleManager::OnWindowDestroying( | |
249 aura::Window* window) { | |
250 StopObserving(window); | |
251 } | |
252 | |
253 //////////////////////////////////////////////////////////////////////////////// | |
254 | |
255 ImmersiveModeControllerAsh::ImmersiveModeControllerAsh() | 52 ImmersiveModeControllerAsh::ImmersiveModeControllerAsh() |
256 : delegate_(NULL), | 53 : controller_(new ash::ImmersiveFullscreenController), |
257 widget_(NULL), | 54 browser_view_(NULL), |
258 top_container_(NULL), | 55 native_window_(NULL), |
259 observers_enabled_(false), | 56 observers_enabled_(false), |
260 enabled_(false), | 57 use_tab_indicators_(false), |
261 reveal_state_(CLOSED), | 58 visible_fraction_(1) { |
262 revealed_lock_count_(0), | |
263 tab_indicator_visibility_(TAB_INDICATORS_HIDE), | |
264 mouse_x_when_hit_top_in_screen_(-1), | |
265 gesture_begun_(false), | |
266 native_window_(NULL), | |
267 animation_(new gfx::SlideAnimation(this)), | |
268 animations_disabled_for_test_(false), | |
269 weak_ptr_factory_(this) { | |
270 } | 59 } |
271 | 60 |
272 ImmersiveModeControllerAsh::~ImmersiveModeControllerAsh() { | 61 ImmersiveModeControllerAsh::~ImmersiveModeControllerAsh() { |
273 // The browser view is being destroyed so there's no need to update its | |
274 // layout or layers, even if the top views are revealed. But the window | |
275 // observers still need to be removed. | |
276 EnableWindowObservers(false); | 62 EnableWindowObservers(false); |
277 } | 63 } |
278 | 64 |
279 void ImmersiveModeControllerAsh::LockRevealedState( | 65 void ImmersiveModeControllerAsh::Init(BrowserView* browser_view) { |
280 AnimateReveal animate_reveal) { | 66 browser_view_ = browser_view; |
281 ++revealed_lock_count_; | 67 native_window_ = browser_view_->GetNativeWindow(); |
282 Animate animate = (animate_reveal == ANIMATE_REVEAL_YES) ? | 68 controller_->Init(this, browser_view_->frame(), |
283 ANIMATE_FAST : ANIMATE_NO; | 69 browser_view_->top_container()); |
284 MaybeStartReveal(animate); | |
285 } | |
286 | |
287 void ImmersiveModeControllerAsh::UnlockRevealedState() { | |
288 --revealed_lock_count_; | |
289 DCHECK_GE(revealed_lock_count_, 0); | |
290 if (revealed_lock_count_ == 0) { | |
291 // Always animate ending the reveal fast. | |
292 MaybeEndReveal(ANIMATE_FAST); | |
293 } | |
294 } | |
295 | |
296 void ImmersiveModeControllerAsh::Init( | |
297 Delegate* delegate, | |
298 views::Widget* widget, | |
299 views::View* top_container) { | |
300 delegate_ = delegate; | |
301 widget_ = widget; | |
302 // Browser view is detached from its widget during destruction. Cache the | |
303 // window pointer so |this| can stop observing during destruction. | |
304 native_window_ = widget_->GetNativeWindow(); | |
305 top_container_ = top_container; | |
306 } | 70 } |
307 | 71 |
308 void ImmersiveModeControllerAsh::SetEnabled(bool enabled) { | 72 void ImmersiveModeControllerAsh::SetEnabled(bool enabled) { |
309 DCHECK(native_window_) << "Must initialize before enabling"; | 73 if (controller_->IsEnabled() == enabled) |
310 if (enabled_ == enabled) | |
311 return; | 74 return; |
312 enabled_ = enabled; | |
313 | 75 |
314 EnableWindowObservers(enabled_); | 76 EnableWindowObservers(enabled); |
315 | 77 |
316 UpdateUseMinimalChrome(LAYOUT_NO); | 78 // Use a short "light bar" version of the tab strip when the top-of-window |
| 79 // views are closed. If the user additionally enters into tab fullscreen, |
| 80 // the tab indicators will be hidden. |
| 81 use_tab_indicators_ = enabled; |
317 | 82 |
318 if (enabled_) { | 83 controller_->SetEnabled(enabled); |
319 // Animate enabling immersive mode by sliding out the top-of-window views. | |
320 // No animation occurs if a lock is holding the top-of-window views open. | |
321 | |
322 // Do a reveal to set the initial state for the animation. (And any | |
323 // required state in case the animation cannot run because of a lock holding | |
324 // the top-of-window views open.) This call has the side effect of relaying | |
325 // out |browser_view_|'s root view. | |
326 MaybeStartReveal(ANIMATE_NO); | |
327 | |
328 // Reset the located event and the focus revealed locks so that they do not | |
329 // affect whether the top-of-window views are hidden. | |
330 located_event_revealed_lock_.reset(); | |
331 focus_revealed_lock_.reset(); | |
332 | |
333 // Try doing the animation. | |
334 MaybeEndReveal(ANIMATE_SLOW); | |
335 | |
336 if (reveal_state_ == REVEALED) { | |
337 // Reveal was unsuccessful. Reacquire the revealed locks if appropriate. | |
338 UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO); | |
339 UpdateFocusRevealedLock(); | |
340 } | |
341 } else { | |
342 // Stop cursor-at-top tracking. | |
343 top_edge_hover_timer_.Stop(); | |
344 // Snap immediately to the closed state. | |
345 reveal_state_ = CLOSED; | |
346 top_container_->SetPaintToLayer(false); | |
347 delegate_->SetImmersiveStyle(false); | |
348 SetRenderWindowTopInsetsForTouch(0); | |
349 | |
350 // Layout the root view so that incognito avatar icon, if any, gets laid | |
351 // out. | |
352 LayoutBrowserRootView(); | |
353 } | |
354 } | 84 } |
355 | 85 |
356 bool ImmersiveModeControllerAsh::IsEnabled() const { | 86 bool ImmersiveModeControllerAsh::IsEnabled() const { |
357 return enabled_; | 87 return controller_->IsEnabled(); |
358 } | 88 } |
359 | 89 |
360 bool ImmersiveModeControllerAsh::ShouldHideTabIndicators() const { | 90 bool ImmersiveModeControllerAsh::ShouldHideTabIndicators() const { |
361 return tab_indicator_visibility_ != TAB_INDICATORS_SHOW; | 91 return !use_tab_indicators_; |
362 } | 92 } |
363 | 93 |
364 bool ImmersiveModeControllerAsh::ShouldHideTopViews() const { | 94 bool ImmersiveModeControllerAsh::ShouldHideTopViews() const { |
365 return enabled_ && reveal_state_ == CLOSED; | 95 return controller_->IsEnabled() && !controller_->IsRevealed(); |
366 } | 96 } |
367 | 97 |
368 bool ImmersiveModeControllerAsh::IsRevealed() const { | 98 bool ImmersiveModeControllerAsh::IsRevealed() const { |
369 return enabled_ && reveal_state_ != CLOSED; | 99 return controller_->IsRevealed(); |
370 } | 100 } |
371 | 101 |
372 int ImmersiveModeControllerAsh::GetTopContainerVerticalOffset( | 102 int ImmersiveModeControllerAsh::GetTopContainerVerticalOffset( |
373 const gfx::Size& top_container_size) const { | 103 const gfx::Size& top_container_size) const { |
374 if (!enabled_ || reveal_state_ == REVEALED || reveal_state_ == CLOSED) | 104 if (!IsEnabled()) |
375 return 0; | 105 return 0; |
376 | 106 |
377 return animation_->CurrentValueBetween( | 107 // The TopContainerView is flush with the top of |browser_view_| when the |
378 -top_container_size.height() + kAnimationOffsetY, 0); | 108 // top-of-window views are fully closed so that when the tab indicators are |
| 109 // used, the "light bar" style tab strip is flush with the top of |
| 110 // |browser_view_|. |
| 111 if (!IsRevealed()) |
| 112 return 0; |
| 113 |
| 114 int height = top_container_size.height() - kAnimationOffsetY; |
| 115 return static_cast<int>(height * (visible_fraction_ - 1)); |
379 } | 116 } |
380 | 117 |
381 ImmersiveRevealedLock* ImmersiveModeControllerAsh::GetRevealedLock( | 118 ImmersiveRevealedLock* ImmersiveModeControllerAsh::GetRevealedLock( |
382 AnimateReveal animate_reveal) { | 119 AnimateReveal animate_reveal) { |
383 return new RevealedLockAsh(weak_ptr_factory_.GetWeakPtr(), animate_reveal); | 120 return controller_->GetRevealedLock( |
| 121 ToImmersiveFullscreenControllerAnimateReveal(animate_reveal)); |
384 } | 122 } |
385 | 123 |
386 void ImmersiveModeControllerAsh::OnFindBarVisibleBoundsChanged( | 124 void ImmersiveModeControllerAsh::OnFindBarVisibleBoundsChanged( |
387 const gfx::Rect& new_visible_bounds_in_screen) { | 125 const gfx::Rect& new_visible_bounds_in_screen) { |
388 find_bar_visible_bounds_in_screen_ = new_visible_bounds_in_screen; | 126 find_bar_visible_bounds_in_screen_ = new_visible_bounds_in_screen; |
389 } | 127 } |
390 | 128 |
391 void ImmersiveModeControllerAsh::SetupForTest() { | 129 void ImmersiveModeControllerAsh::SetupForTest() { |
392 DCHECK(!enabled_); | 130 controller_->SetupForTest(); |
393 animations_disabled_for_test_ = true; | |
394 | |
395 // Move the mouse off of the top-of-window views so that it does not keep | |
396 // the top-of-window views revealed. | |
397 gfx::Point cursor_pos(0, top_container_->bounds().bottom() + 100); | |
398 views::View::ConvertPointToScreen(top_container_, &cursor_pos); | |
399 aura::Env::GetInstance()->set_last_mouse_location(cursor_pos); | |
400 UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO); | |
401 } | 131 } |
402 | 132 |
403 //////////////////////////////////////////////////////////////////////////////// | |
404 // Observers: | |
405 | |
406 void ImmersiveModeControllerAsh::Observe( | |
407 int type, | |
408 const content::NotificationSource& source, | |
409 const content::NotificationDetails& details) { | |
410 DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type); | |
411 if (enabled_) | |
412 UpdateUseMinimalChrome(LAYOUT_YES); | |
413 } | |
414 | |
415 void ImmersiveModeControllerAsh::OnMouseEvent(ui::MouseEvent* event) { | |
416 if (!enabled_) | |
417 return; | |
418 | |
419 if (event->type() != ui::ET_MOUSE_MOVED && | |
420 event->type() != ui::ET_MOUSE_PRESSED && | |
421 event->type() != ui::ET_MOUSE_RELEASED && | |
422 event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) { | |
423 return; | |
424 } | |
425 | |
426 // Mouse hover should not initiate revealing the top-of-window views while | |
427 // |native_window_| is inactive. | |
428 if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive()) | |
429 return; | |
430 | |
431 // Mouse hover should not initiate revealing the top-of-window views while | |
432 // a window has mouse capture. | |
433 if (aura::client::GetCaptureWindow(native_window_)) | |
434 return; | |
435 | |
436 if (IsRevealed()) | |
437 UpdateLocatedEventRevealedLock(event, ALLOW_REVEAL_WHILE_CLOSING_NO); | |
438 | |
439 // Trigger a reveal if the cursor pauses at the top of the screen for a | |
440 // while. | |
441 if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) | |
442 UpdateTopEdgeHoverTimer(event); | |
443 } | |
444 | |
445 void ImmersiveModeControllerAsh::OnTouchEvent(ui::TouchEvent* event) { | |
446 if (!enabled_ || event->type() != ui::ET_TOUCH_PRESSED) | |
447 return; | |
448 | |
449 UpdateLocatedEventRevealedLock(event, ALLOW_REVEAL_WHILE_CLOSING_NO); | |
450 } | |
451 | |
452 void ImmersiveModeControllerAsh::OnGestureEvent(ui::GestureEvent* event) { | |
453 if (!enabled_) | |
454 return; | |
455 | |
456 // Touch gestures should not initiate revealing the top-of-window views while | |
457 // |native_window_| is inactive. | |
458 if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive()) | |
459 return; | |
460 | |
461 switch (event->type()) { | |
462 case ui::ET_GESTURE_SCROLL_BEGIN: | |
463 if (ShouldHandleGestureEvent(GetEventLocationInScreen(*event))) { | |
464 gesture_begun_ = true; | |
465 event->SetHandled(); | |
466 } | |
467 break; | |
468 case ui::ET_GESTURE_SCROLL_UPDATE: | |
469 if (gesture_begun_) { | |
470 if (UpdateRevealedLocksForSwipe(GetSwipeType(event))) | |
471 event->SetHandled(); | |
472 gesture_begun_ = false; | |
473 } | |
474 break; | |
475 case ui::ET_GESTURE_SCROLL_END: | |
476 case ui::ET_SCROLL_FLING_START: | |
477 gesture_begun_ = false; | |
478 break; | |
479 default: | |
480 break; | |
481 } | |
482 } | |
483 | |
484 void ImmersiveModeControllerAsh::OnWillChangeFocus(views::View* focused_before, | |
485 views::View* focused_now) { | |
486 } | |
487 | |
488 void ImmersiveModeControllerAsh::OnDidChangeFocus(views::View* focused_before, | |
489 views::View* focused_now) { | |
490 UpdateFocusRevealedLock(); | |
491 } | |
492 | |
493 void ImmersiveModeControllerAsh::OnWidgetDestroying(views::Widget* widget) { | |
494 EnableWindowObservers(false); | |
495 native_window_ = NULL; | |
496 | |
497 // Set |enabled_| to false such that any calls to MaybeStartReveal() and | |
498 // MaybeEndReveal() have no effect. | |
499 enabled_ = false; | |
500 } | |
501 | |
502 void ImmersiveModeControllerAsh::OnWidgetActivationChanged( | |
503 views::Widget* widget, | |
504 bool active) { | |
505 // Mouse hover should not initiate revealing the top-of-window views while | |
506 // |native_window_| is inactive. | |
507 top_edge_hover_timer_.Stop(); | |
508 | |
509 UpdateFocusRevealedLock(); | |
510 | |
511 // Allow the top-of-window views to stay revealed if all of the revealed locks | |
512 // were released in the process of activating |widget| but the mouse is still | |
513 // hovered above the top-of-window views. For instance, if the bubble which | |
514 // has been keeping the top-of-window views revealed is hidden but the mouse | |
515 // is hovered above the top-of-window views, the top-of-window views should | |
516 // stay revealed. We cannot call UpdateLocatedEventRevealedLock() from | |
517 // BubbleManager::UpdateRevealedLock() because |widget| is not yet active | |
518 // at that time. | |
519 UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_YES); | |
520 } | |
521 | |
522 //////////////////////////////////////////////////////////////////////////////// | |
523 // Animation delegate: | |
524 | |
525 void ImmersiveModeControllerAsh::AnimationEnded( | |
526 const gfx::Animation* animation) { | |
527 if (reveal_state_ == SLIDING_OPEN) { | |
528 // AnimationProgressed() is called immediately before AnimationEnded() | |
529 // and does a layout. | |
530 OnSlideOpenAnimationCompleted(LAYOUT_NO); | |
531 } else if (reveal_state_ == SLIDING_CLOSED) { | |
532 OnSlideClosedAnimationCompleted(); | |
533 } | |
534 } | |
535 | |
536 void ImmersiveModeControllerAsh::AnimationProgressed( | |
537 const gfx::Animation* animation) { | |
538 // Relayout. This will also move any views whose position depends on the | |
539 // top container position such as the find bar. | |
540 // We do not call LayoutBrowserRootView() here because we are not toggling | |
541 // the tab strip's immersive style so relaying out the non client view is not | |
542 // necessary. | |
543 top_container_->parent()->Layout(); | |
544 } | |
545 | |
546 //////////////////////////////////////////////////////////////////////////////// | |
547 // aura::WindowObserver overrides: | |
548 | |
549 void ImmersiveModeControllerAsh::OnWindowPropertyChanged(aura::Window* window, | |
550 const void* key, | |
551 intptr_t old) { | |
552 if (!enabled_) | |
553 return; | |
554 | |
555 if (key == aura::client::kShowStateKey) { | |
556 // Disable immersive mode when the user exits fullscreen without going | |
557 // through FullscreenController::ToggleFullscreenMode(). This is the case | |
558 // if the user exits fullscreen via the restore button. | |
559 ui::WindowShowState show_state = static_cast<ui::WindowShowState>( | |
560 native_window_->GetProperty(aura::client::kShowStateKey)); | |
561 if (show_state != ui::SHOW_STATE_FULLSCREEN && | |
562 show_state != ui::SHOW_STATE_MINIMIZED) { | |
563 delegate_->FullscreenStateChanged(); | |
564 } | |
565 } | |
566 } | |
567 | |
568 void ImmersiveModeControllerAsh::OnAddTransientChild(aura::Window* window, | |
569 aura::Window* transient) { | |
570 views::BubbleDelegateView* bubble_delegate = AsBubbleDelegate(transient); | |
571 if (bubble_delegate && | |
572 bubble_delegate->GetAnchorView() && | |
573 top_container_->Contains(bubble_delegate->GetAnchorView())) { | |
574 // Observe the aura::Window because the BubbleDelegateView may not be | |
575 // parented to the widget's root view yet so |bubble_delegate->GetWidget()| | |
576 // may still return NULL. | |
577 bubble_manager_->StartObserving(transient); | |
578 } | |
579 } | |
580 | |
581 void ImmersiveModeControllerAsh::OnRemoveTransientChild( | |
582 aura::Window* window, | |
583 aura::Window* transient) { | |
584 bubble_manager_->StopObserving(transient); | |
585 } | |
586 | |
587 //////////////////////////////////////////////////////////////////////////////// | |
588 // private: | |
589 | |
590 void ImmersiveModeControllerAsh::EnableWindowObservers(bool enable) { | 133 void ImmersiveModeControllerAsh::EnableWindowObservers(bool enable) { |
591 if (observers_enabled_ == enable) | 134 if (observers_enabled_ == enable) |
592 return; | 135 return; |
593 observers_enabled_ = enable; | 136 observers_enabled_ = enable; |
594 | 137 |
595 if (!native_window_) { | 138 content::Source<FullscreenController> source( |
596 NOTREACHED() << "ImmersiveModeControllerAsh not initialized"; | 139 browser_view_->browser()->fullscreen_controller()); |
597 return; | |
598 } | |
599 | |
600 views::Widget* widget = | |
601 views::Widget::GetWidgetForNativeWindow(native_window_); | |
602 views::FocusManager* focus_manager = widget->GetFocusManager(); | |
603 if (enable) { | 140 if (enable) { |
604 widget->AddObserver(this); | 141 ash::wm::GetWindowState(native_window_)->AddObserver(this); |
605 focus_manager->AddFocusChangeListener(this); | 142 registrar_.Add(this, chrome::NOTIFICATION_FULLSCREEN_CHANGED, source); |
606 } else { | 143 } else { |
607 widget->RemoveObserver(this); | 144 ash::wm::GetWindowState(native_window_)->RemoveObserver(this); |
608 focus_manager->RemoveFocusChangeListener(this); | 145 registrar_.Remove(this, chrome::NOTIFICATION_FULLSCREEN_CHANGED, source); |
609 } | |
610 | |
611 if (enable) | |
612 ash::Shell::GetInstance()->AddPreTargetHandler(this); | |
613 else | |
614 ash::Shell::GetInstance()->RemovePreTargetHandler(this); | |
615 | |
616 if (enable) { | |
617 native_window_->AddObserver(this); | |
618 } else { | |
619 native_window_->RemoveObserver(this); | |
620 } | |
621 | |
622 if (enable) { | |
623 RecreateBubbleManager(); | |
624 } else { | |
625 // We have stopped observing whether transient children are added or removed | |
626 // to |native_window_|. The set of bubbles that BubbleManager is observing | |
627 // will become stale really quickly. Destroy BubbleManager and recreate it | |
628 // when we start observing |native_window_| again. | |
629 bubble_manager_.reset(); | |
630 } | |
631 | |
632 if (enable) { | |
633 registrar_.Add( | |
634 this, | |
635 chrome::NOTIFICATION_FULLSCREEN_CHANGED, | |
636 content::Source<FullscreenController>( | |
637 delegate_->GetFullscreenController())); | |
638 } else { | |
639 registrar_.Remove( | |
640 this, | |
641 chrome::NOTIFICATION_FULLSCREEN_CHANGED, | |
642 content::Source<FullscreenController>( | |
643 delegate_->GetFullscreenController())); | |
644 } | |
645 | |
646 if (!enable) | |
647 animation_->Stop(); | |
648 } | |
649 | |
650 void ImmersiveModeControllerAsh::UpdateTopEdgeHoverTimer( | |
651 ui::MouseEvent* event) { | |
652 DCHECK(enabled_); | |
653 // Stop the timer if the top-of-window views are already revealed. | |
654 if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) { | |
655 top_edge_hover_timer_.Stop(); | |
656 return; | |
657 } | |
658 | |
659 gfx::Point location_in_screen = GetEventLocationInScreen(*event); | |
660 if (ShouldIgnoreMouseEventAtLocation(location_in_screen)) | |
661 return; | |
662 | |
663 // Stop the timer if the cursor left the top edge or is on a different | |
664 // display. The bounds of |top_container_|'s parent are used to infer the hit | |
665 // bounds because |top_container_| will be partially offscreen if it is | |
666 // animating closed. | |
667 gfx::Rect hit_bounds_in_screen = | |
668 top_container_->parent()->GetBoundsInScreen(); | |
669 hit_bounds_in_screen.set_height(kMouseRevealBoundsHeight); | |
670 if (!hit_bounds_in_screen.Contains(location_in_screen)) { | |
671 top_edge_hover_timer_.Stop(); | |
672 return; | |
673 } | |
674 | |
675 // The cursor is now at the top of the screen. Consider the cursor "not | |
676 // moving" even if it moves a little bit because users don't have perfect | |
677 // pointing precision. (The y position is not tested because | |
678 // |hit_bounds_in_screen| is short.) | |
679 if (top_edge_hover_timer_.IsRunning() && | |
680 abs(location_in_screen.x() - mouse_x_when_hit_top_in_screen_) <= | |
681 kMouseRevealXThresholdPixels) | |
682 return; | |
683 | |
684 // Start the reveal if the cursor doesn't move for some amount of time. | |
685 mouse_x_when_hit_top_in_screen_ = location_in_screen.x(); | |
686 top_edge_hover_timer_.Stop(); | |
687 // Timer is stopped when |this| is destroyed, hence Unretained() is safe. | |
688 top_edge_hover_timer_.Start( | |
689 FROM_HERE, | |
690 base::TimeDelta::FromMilliseconds(kMouseRevealDelayMs), | |
691 base::Bind(&ImmersiveModeControllerAsh::AcquireLocatedEventRevealedLock, | |
692 base::Unretained(this))); | |
693 } | |
694 | |
695 void ImmersiveModeControllerAsh::UpdateLocatedEventRevealedLock( | |
696 ui::LocatedEvent* event, | |
697 AllowRevealWhileClosing allow_reveal_while_closing) { | |
698 if (!enabled_) | |
699 return; | |
700 DCHECK(!event || event->IsMouseEvent() || event->IsTouchEvent()); | |
701 | |
702 // Neither the mouse nor touch can initiate a reveal when the top-of-window | |
703 // views are sliding closed or are closed with the following exceptions: | |
704 // - Hovering at y = 0 which is handled in OnMouseEvent(). | |
705 // - Doing a SWIPE_OPEN edge gesture which is handled in OnGestureEvent(). | |
706 if (reveal_state_ == CLOSED || | |
707 (reveal_state_ == SLIDING_CLOSED && | |
708 allow_reveal_while_closing == ALLOW_REVEAL_WHILE_CLOSING_NO)) { | |
709 return; | |
710 } | |
711 | |
712 // Neither the mouse nor touch should keep the top-of-window views revealed if | |
713 // |native_window_| is not active. | |
714 if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive()) { | |
715 located_event_revealed_lock_.reset(); | |
716 return; | |
717 } | |
718 | |
719 // Ignore all events while a window has capture. This keeps the top-of-window | |
720 // views revealed during a drag. | |
721 if (aura::client::GetCaptureWindow(native_window_)) | |
722 return; | |
723 | |
724 gfx::Point location_in_screen; | |
725 if (event && event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) { | |
726 location_in_screen = GetEventLocationInScreen(*event); | |
727 } else { | |
728 aura::client::CursorClient* cursor_client = aura::client::GetCursorClient( | |
729 native_window_->GetRootWindow()); | |
730 if (!cursor_client->IsMouseEventsEnabled()) { | |
731 // If mouse events are disabled, the user's last interaction was probably | |
732 // via touch. Do no do further processing in this case as there is no easy | |
733 // way of retrieving the position of the user's last touch. | |
734 return; | |
735 } | |
736 location_in_screen = aura::Env::GetInstance()->last_mouse_location(); | |
737 } | |
738 | |
739 if ((!event || event->IsMouseEvent()) && | |
740 ShouldIgnoreMouseEventAtLocation(location_in_screen)) { | |
741 return; | |
742 } | |
743 | |
744 gfx::Rect hit_bounds_in_top_container = top_container_->GetVisibleBounds(); | |
745 // TODO(tdanderson): Implement View::ConvertRectToScreen(); | |
746 gfx::Point hit_bounds_in_screen_origin = hit_bounds_in_top_container.origin(); | |
747 views::View::ConvertPointToScreen(top_container_, | |
748 &hit_bounds_in_screen_origin); | |
749 gfx::Rect hit_bounds_in_screen(hit_bounds_in_screen_origin, | |
750 hit_bounds_in_top_container.size()); | |
751 | |
752 gfx::Rect find_bar_hit_bounds_in_screen = find_bar_visible_bounds_in_screen_; | |
753 | |
754 // Allow the cursor to move slightly off the top-of-window views before | |
755 // sliding closed. This helps when the user is attempting to click on the | |
756 // bookmark bar and overshoots slightly. | |
757 if (event && event->type() == ui::ET_MOUSE_MOVED) { | |
758 const int kBoundsOffsetY = 8; | |
759 hit_bounds_in_screen.Inset(0, 0, 0, -kBoundsOffsetY); | |
760 find_bar_hit_bounds_in_screen.Inset(0, 0, 0, -kBoundsOffsetY); | |
761 } | |
762 | |
763 if (hit_bounds_in_screen.Contains(location_in_screen) || | |
764 find_bar_hit_bounds_in_screen.Contains(location_in_screen)) { | |
765 AcquireLocatedEventRevealedLock(); | |
766 } else { | |
767 located_event_revealed_lock_.reset(); | |
768 } | 146 } |
769 } | 147 } |
770 | 148 |
771 void ImmersiveModeControllerAsh::AcquireLocatedEventRevealedLock() { | |
772 // CAUTION: Acquiring the lock results in a reentrant call to | |
773 // AcquireLocatedEventRevealedLock() when | |
774 // |ImmersiveModeControllerAsh::animations_disabled_for_test_| is true. | |
775 if (!located_event_revealed_lock_.get()) | |
776 located_event_revealed_lock_.reset(GetRevealedLock(ANIMATE_REVEAL_YES)); | |
777 } | |
778 | |
779 void ImmersiveModeControllerAsh::UpdateFocusRevealedLock() { | |
780 if (!enabled_) | |
781 return; | |
782 | |
783 bool hold_lock = false; | |
784 views::Widget* widget = | |
785 views::Widget::GetWidgetForNativeWindow(native_window_); | |
786 if (widget->IsActive()) { | |
787 views::View* focused_view = widget->GetFocusManager()->GetFocusedView(); | |
788 if (top_container_->Contains(focused_view)) | |
789 hold_lock = true; | |
790 } else { | |
791 aura::Window* active_window = aura::client::GetActivationClient( | |
792 native_window_->GetRootWindow())->GetActiveWindow(); | |
793 views::BubbleDelegateView* bubble_delegate = | |
794 AsBubbleDelegate(active_window); | |
795 if (bubble_delegate && bubble_delegate->anchor_widget()) { | |
796 // BubbleManager will already have locked the top-of-window views if the | |
797 // bubble is anchored to a child of |top_container_|. Don't acquire | |
798 // |focus_revealed_lock_| here for the sake of simplicity. | |
799 // Note: Instead of checking for the existence of the |anchor_view|, | |
800 // the existence of the |anchor_widget| is performed to avoid the case | |
801 // where the view is already gone (and the widget is still running). | |
802 } else { | |
803 // The currently active window is not |native_window_| and it is not a | |
804 // bubble with an anchor view. The top-of-window views should be revealed | |
805 // if: | |
806 // 1) The active window is a transient child of |native_window_|. | |
807 // 2) The top-of-window views are already revealed. This restriction | |
808 // prevents a transient window opened by the web contents while the | |
809 // top-of-window views are hidden from from initiating a reveal. | |
810 // The top-of-window views will stay revealed till |native_window_| is | |
811 // reactivated. | |
812 if (IsRevealed() && | |
813 IsWindowTransientChildOf(active_window, native_window_)) { | |
814 hold_lock = true; | |
815 } | |
816 } | |
817 } | |
818 | |
819 if (hold_lock) { | |
820 if (!focus_revealed_lock_.get()) | |
821 focus_revealed_lock_.reset(GetRevealedLock(ANIMATE_REVEAL_YES)); | |
822 } else { | |
823 focus_revealed_lock_.reset(); | |
824 } | |
825 } | |
826 | |
827 bool ImmersiveModeControllerAsh::UpdateRevealedLocksForSwipe( | |
828 SwipeType swipe_type) { | |
829 if (!enabled_ || swipe_type == SWIPE_NONE) | |
830 return false; | |
831 | |
832 // Swipes while |native_window_| is inactive should have been filtered out in | |
833 // OnGestureEvent(). | |
834 DCHECK(views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive()); | |
835 | |
836 if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) { | |
837 if (swipe_type == SWIPE_OPEN && !located_event_revealed_lock_.get()) { | |
838 located_event_revealed_lock_.reset(GetRevealedLock(ANIMATE_REVEAL_YES)); | |
839 return true; | |
840 } | |
841 } else { | |
842 if (swipe_type == SWIPE_CLOSE) { | |
843 // Attempt to end the reveal. If other code is holding onto a lock, the | |
844 // attempt will be unsuccessful. | |
845 located_event_revealed_lock_.reset(); | |
846 focus_revealed_lock_.reset(); | |
847 | |
848 if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) | |
849 return true; | |
850 | |
851 // Ending the reveal was unsuccessful. Reaquire the locks if appropriate. | |
852 UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO); | |
853 UpdateFocusRevealedLock(); | |
854 } | |
855 } | |
856 return false; | |
857 } | |
858 | |
859 void ImmersiveModeControllerAsh::UpdateUseMinimalChrome(Layout layout) { | |
860 // May be NULL in tests. | |
861 FullscreenController* fullscreen_controller = | |
862 delegate_->GetFullscreenController(); | |
863 bool in_tab_fullscreen = fullscreen_controller ? | |
864 fullscreen_controller->IsFullscreenForTabOrPending() : false; | |
865 bool use_minimal_chrome = !in_tab_fullscreen && enabled_; | |
866 | |
867 // When using minimal chrome, the shelf is auto-hidden. The auto-hidden shelf | |
868 // displays a 3px 'light bar' when it is closed. Otherwise, the shelf is | |
869 // hidden completely and cannot be revealed. | |
870 ash::wm::GetWindowState(native_window_)->set_hide_shelf_when_fullscreen( | |
871 !use_minimal_chrome); | |
872 | |
873 TabIndicatorVisibility previous_tab_indicator_visibility = | |
874 tab_indicator_visibility_; | |
875 if (tab_indicator_visibility_ != TAB_INDICATORS_FORCE_HIDE) { | |
876 tab_indicator_visibility_ = use_minimal_chrome ? | |
877 TAB_INDICATORS_SHOW : TAB_INDICATORS_HIDE; | |
878 } | |
879 | |
880 ash::Shell::GetInstance()->UpdateShelfVisibility(); | |
881 | |
882 if (tab_indicator_visibility_ != previous_tab_indicator_visibility) { | |
883 // If the top-of-window views are revealed or animating, the change will | |
884 // take effect with the layout once the top-of-window views are closed. | |
885 if (layout == LAYOUT_YES && reveal_state_ == CLOSED) | |
886 LayoutBrowserRootView(); | |
887 } | |
888 } | |
889 | |
890 int ImmersiveModeControllerAsh::GetAnimationDuration(Animate animate) const { | |
891 switch (animate) { | |
892 case ANIMATE_NO: | |
893 return 0; | |
894 case ANIMATE_SLOW: | |
895 return kRevealSlowAnimationDurationMs; | |
896 case ANIMATE_FAST: | |
897 return kRevealFastAnimationDurationMs; | |
898 } | |
899 NOTREACHED(); | |
900 return 0; | |
901 } | |
902 | |
903 void ImmersiveModeControllerAsh::MaybeStartReveal(Animate animate) { | |
904 if (!enabled_) | |
905 return; | |
906 | |
907 if (animations_disabled_for_test_) | |
908 animate = ANIMATE_NO; | |
909 | |
910 // Callers with ANIMATE_NO expect this function to synchronously reveal the | |
911 // top-of-window views. | |
912 if (reveal_state_ == REVEALED || | |
913 (reveal_state_ == SLIDING_OPEN && animate != ANIMATE_NO)) { | |
914 return; | |
915 } | |
916 | |
917 RevealState previous_reveal_state = reveal_state_; | |
918 reveal_state_ = SLIDING_OPEN; | |
919 if (previous_reveal_state == CLOSED) { | |
920 // Turn on layer painting so that we can overlap the web contents. | |
921 top_container_->SetPaintToLayer(true); | |
922 | |
923 // Ensure window caption buttons are updated and the view bounds are | |
924 // computed at normal (non-immersive-style) size. The layout call moves the | |
925 // top-of-window views to their initial offscreen position for the | |
926 // animation. | |
927 delegate_->SetImmersiveStyle(false); | |
928 SetRenderWindowTopInsetsForTouch(0); | |
929 LayoutBrowserRootView(); | |
930 | |
931 // Do not do any more processing if LayoutBrowserView() changed | |
932 // |reveal_state_|. | |
933 if (reveal_state_ != SLIDING_OPEN) { | |
934 if (reveal_state_ == REVEALED) | |
935 FOR_EACH_OBSERVER(Observer, observers_, OnImmersiveRevealStarted()); | |
936 return; | |
937 } | |
938 } | |
939 // Slide in the reveal view. | |
940 if (animate == ANIMATE_NO) { | |
941 animation_->Reset(1); | |
942 OnSlideOpenAnimationCompleted(LAYOUT_YES); | |
943 } else { | |
944 animation_->SetSlideDuration(GetAnimationDuration(animate)); | |
945 animation_->Show(); | |
946 } | |
947 | |
948 if (previous_reveal_state == CLOSED) | |
949 FOR_EACH_OBSERVER(Observer, observers_, OnImmersiveRevealStarted()); | |
950 } | |
951 | |
952 void ImmersiveModeControllerAsh::LayoutBrowserRootView() { | 149 void ImmersiveModeControllerAsh::LayoutBrowserRootView() { |
| 150 views::Widget* widget = browser_view_->frame(); |
953 // Update the window caption buttons. | 151 // Update the window caption buttons. |
954 widget_->non_client_view()->frame_view()->ResetWindowControls(); | 152 widget->non_client_view()->frame_view()->ResetWindowControls(); |
955 // Layout all views, including BrowserView. | 153 // Layout all views, including BrowserView. |
956 widget_->GetRootView()->Layout(); | 154 widget->GetRootView()->Layout(); |
957 } | |
958 | |
959 void ImmersiveModeControllerAsh::OnSlideOpenAnimationCompleted(Layout layout) { | |
960 DCHECK_EQ(SLIDING_OPEN, reveal_state_); | |
961 reveal_state_ = REVEALED; | |
962 | |
963 if (layout == LAYOUT_YES) | |
964 top_container_->parent()->Layout(); | |
965 | |
966 // The user may not have moved the mouse since the reveal was initiated. | |
967 // Update the revealed lock to reflect the mouse's current state. | |
968 UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO); | |
969 } | |
970 | |
971 void ImmersiveModeControllerAsh::MaybeEndReveal(Animate animate) { | |
972 if (!enabled_ || revealed_lock_count_ != 0) | |
973 return; | |
974 | |
975 if (animations_disabled_for_test_) | |
976 animate = ANIMATE_NO; | |
977 | |
978 // Callers with ANIMATE_NO expect this function to synchronously close the | |
979 // top-of-window views. | |
980 if (reveal_state_ == CLOSED || | |
981 (reveal_state_ == SLIDING_CLOSED && animate != ANIMATE_NO)) { | |
982 return; | |
983 } | |
984 | |
985 reveal_state_ = SLIDING_CLOSED; | |
986 int duration_ms = GetAnimationDuration(animate); | |
987 if (duration_ms > 0) { | |
988 animation_->SetSlideDuration(duration_ms); | |
989 animation_->Hide(); | |
990 } else { | |
991 animation_->Reset(0); | |
992 OnSlideClosedAnimationCompleted(); | |
993 } | |
994 } | |
995 | |
996 void ImmersiveModeControllerAsh::OnSlideClosedAnimationCompleted() { | |
997 DCHECK_EQ(SLIDING_CLOSED, reveal_state_); | |
998 reveal_state_ = CLOSED; | |
999 // Layers aren't needed after animation completes. | |
1000 top_container_->SetPaintToLayer(false); | |
1001 // Update tabstrip for closed state. | |
1002 delegate_->SetImmersiveStyle(true); | |
1003 SetRenderWindowTopInsetsForTouch(kNearTopContainerDistance); | |
1004 LayoutBrowserRootView(); | |
1005 } | |
1006 | |
1007 ImmersiveModeControllerAsh::SwipeType ImmersiveModeControllerAsh::GetSwipeType( | |
1008 ui::GestureEvent* event) const { | |
1009 if (event->type() != ui::ET_GESTURE_SCROLL_UPDATE) | |
1010 return SWIPE_NONE; | |
1011 // Make sure that it is a clear vertical gesture. | |
1012 if (abs(event->details().scroll_y()) <= | |
1013 kSwipeVerticalThresholdMultiplier * abs(event->details().scroll_x())) | |
1014 return SWIPE_NONE; | |
1015 if (event->details().scroll_y() < 0) | |
1016 return SWIPE_CLOSE; | |
1017 else if (event->details().scroll_y() > 0) | |
1018 return SWIPE_OPEN; | |
1019 return SWIPE_NONE; | |
1020 } | |
1021 | |
1022 bool ImmersiveModeControllerAsh::ShouldIgnoreMouseEventAtLocation( | |
1023 const gfx::Point& location) const { | |
1024 // Ignore mouse events in the region immediately above the top edge of the | |
1025 // display. This is to handle the case of a user with a vertical display | |
1026 // layout (primary display above/below secondary display) and the immersive | |
1027 // fullscreen window on the bottom display. It is really hard to trigger a | |
1028 // reveal in this case because: | |
1029 // - It is hard to stop the cursor in the top |kMouseRevealBoundsHeight| | |
1030 // pixels of the bottom display. | |
1031 // - The cursor is warped to the top display if the cursor gets to the top | |
1032 // edge of the bottom display. | |
1033 // Mouse events are ignored in the bottom few pixels of the top display | |
1034 // (Mouse events in this region cannot start or end a reveal). This allows a | |
1035 // user to overshoot the top of the bottom display and still reveal the | |
1036 // top-of-window views. | |
1037 gfx::Rect dead_region = top_container_->parent()->GetBoundsInScreen(); | |
1038 dead_region.set_y(dead_region.y() - kHeightOfDeadRegionAboveTopContainer); | |
1039 dead_region.set_height(kHeightOfDeadRegionAboveTopContainer); | |
1040 return dead_region.Contains(location); | |
1041 } | |
1042 | |
1043 bool ImmersiveModeControllerAsh::ShouldHandleGestureEvent( | |
1044 const gfx::Point& location) const { | |
1045 gfx::Rect top_container_bounds_in_screen = | |
1046 top_container_->GetBoundsInScreen(); | |
1047 | |
1048 // All of the gestures that are of interest start in a region with left & | |
1049 // right edges agreeing with |top_container_|. When CLOSED it is difficult to | |
1050 // hit the bounds due to small size of the tab strip, so the hit target needs | |
1051 // to be extended on the bottom, thus the inset call. | |
1052 gfx::Rect near_bounds = top_container_bounds_in_screen; | |
1053 if (reveal_state_ == CLOSED) | |
1054 near_bounds.Inset(gfx::Insets(0, 0, -kNearTopContainerDistance, 0)); | |
1055 if (near_bounds.Contains(location)) | |
1056 return true; | |
1057 | |
1058 // There may be a bezel sensor off screen logically above |top_container_| | |
1059 // thus the test needs to include gestures starting above, but this needs to | |
1060 // be distinguished from events originating on another screen from | |
1061 // (potentially) an extended desktop. The check for the event not contained by | |
1062 // the closest screen ensures that the event is from a valid bezel and can be | |
1063 // interpreted as such. | |
1064 gfx::Rect screen_bounds = | |
1065 ash::Shell::GetScreen()->GetDisplayNearestPoint(location).bounds(); | |
1066 return (!screen_bounds.Contains(location) && | |
1067 location.y() < top_container_bounds_in_screen.y() && | |
1068 location.x() >= top_container_bounds_in_screen.x() && | |
1069 location.x() < top_container_bounds_in_screen.right()); | |
1070 } | 155 } |
1071 | 156 |
1072 void ImmersiveModeControllerAsh::SetRenderWindowTopInsetsForTouch( | 157 void ImmersiveModeControllerAsh::SetRenderWindowTopInsetsForTouch( |
1073 int top_inset) { | 158 int top_inset) { |
1074 content::WebContents* contents = delegate_->GetWebContents(); | 159 content::WebContents* contents = browser_view_->GetActiveWebContents(); |
1075 if (contents) { | 160 if (contents) { |
1076 aura::Window* window = contents->GetView()->GetContentNativeView(); | 161 aura::Window* window = contents->GetView()->GetContentNativeView(); |
1077 // |window| is NULL if the renderer crashed. | 162 // |window| is NULL if the renderer crashed. |
1078 if (window) { | 163 if (window) { |
1079 gfx::Insets inset(top_inset, 0, 0, 0); | 164 gfx::Insets inset(top_inset, 0, 0, 0); |
1080 window->SetHitTestBoundsOverrideOuter( | 165 window->SetHitTestBoundsOverrideOuter( |
1081 window->hit_test_bounds_override_outer_mouse(), | 166 window->hit_test_bounds_override_outer_mouse(), |
1082 inset); | 167 inset); |
1083 } | 168 } |
1084 } | 169 } |
1085 } | 170 } |
1086 | 171 |
1087 void ImmersiveModeControllerAsh::RecreateBubbleManager() { | 172 void ImmersiveModeControllerAsh::SetTabIndicatorsVisible(bool visible) { |
1088 bubble_manager_.reset(new BubbleManager(this)); | 173 DCHECK(!visible || use_tab_indicators_); |
1089 const std::vector<aura::Window*> transient_children = | 174 if (browser_view_->tabstrip()) |
1090 native_window_->transient_children(); | 175 browser_view_->tabstrip()->SetImmersiveStyle(visible); |
1091 for (size_t i = 0; i < transient_children.size(); ++i) { | 176 } |
1092 aura::Window* transient_child = transient_children[i]; | 177 |
1093 views::BubbleDelegateView* bubble_delegate = | 178 void ImmersiveModeControllerAsh::OnImmersiveRevealStarted() { |
1094 AsBubbleDelegate(transient_child); | 179 visible_fraction_ = 0; |
1095 if (bubble_delegate && | 180 browser_view_->top_container()->SetPaintToLayer(true); |
1096 bubble_delegate->GetAnchorView() && | 181 SetTabIndicatorsVisible(false); |
1097 top_container_->Contains(bubble_delegate->GetAnchorView())) { | 182 SetRenderWindowTopInsetsForTouch(0); |
1098 bubble_manager_->StartObserving(transient_child); | 183 LayoutBrowserRootView(); |
1099 } | 184 FOR_EACH_OBSERVER(Observer, observers_, OnImmersiveRevealStarted()); |
| 185 } |
| 186 |
| 187 void ImmersiveModeControllerAsh::OnImmersiveRevealEnded() { |
| 188 visible_fraction_ = 0; |
| 189 browser_view_->top_container()->SetPaintToLayer(false); |
| 190 SetTabIndicatorsVisible(use_tab_indicators_); |
| 191 SetRenderWindowTopInsetsForTouch( |
| 192 kStealTouchEventsFromWebContentsRegionHeightPx); |
| 193 LayoutBrowserRootView(); |
| 194 } |
| 195 |
| 196 void ImmersiveModeControllerAsh::OnImmersiveFullscreenExited() { |
| 197 browser_view_->top_container()->SetPaintToLayer(false); |
| 198 SetTabIndicatorsVisible(false); |
| 199 SetRenderWindowTopInsetsForTouch(0); |
| 200 LayoutBrowserRootView(); |
| 201 } |
| 202 |
| 203 void ImmersiveModeControllerAsh::SetVisibleFraction(double visible_fraction) { |
| 204 if (visible_fraction_ != visible_fraction) { |
| 205 visible_fraction_ = visible_fraction; |
| 206 browser_view_->Layout(); |
1100 } | 207 } |
1101 } | 208 } |
| 209 |
| 210 std::vector<gfx::Rect> |
| 211 ImmersiveModeControllerAsh::GetVisibleBoundsInScreen() { |
| 212 views::View* top_container_view = browser_view_->top_container(); |
| 213 gfx::Rect top_container_view_bounds = top_container_view->GetVisibleBounds(); |
| 214 // TODO(tdanderson): Implement View::ConvertRectToScreen(). |
| 215 gfx::Point top_container_view_bounds_in_screen_origin( |
| 216 top_container_view_bounds.origin()); |
| 217 views::View::ConvertPointToScreen(top_container_view, |
| 218 &top_container_view_bounds_in_screen_origin); |
| 219 gfx::Rect top_container_view_bounds_in_screen( |
| 220 top_container_view_bounds_in_screen_origin, |
| 221 top_container_view_bounds.size()); |
| 222 |
| 223 std::vector<gfx::Rect> bounds_in_screen; |
| 224 bounds_in_screen.push_back(top_container_view_bounds_in_screen); |
| 225 bounds_in_screen.push_back(find_bar_visible_bounds_in_screen_); |
| 226 return bounds_in_screen; |
| 227 } |
| 228 |
| 229 void ImmersiveModeControllerAsh::OnWindowShowTypeChanged( |
| 230 ash::wm::WindowState* window_state, |
| 231 ash::wm::WindowShowType old_type) { |
| 232 // Disable immersive fullscreen when the user exits fullscreen without going |
| 233 // through FullscreenController::ToggleFullscreenMode(). This is the case if |
| 234 // the user exits fullscreen via the restore button. |
| 235 if (controller_->IsEnabled() && |
| 236 !window_state->IsFullscreen() && |
| 237 !window_state->IsMinimized()) { |
| 238 browser_view_->FullscreenStateChanged(); |
| 239 } |
| 240 } |
| 241 |
| 242 void ImmersiveModeControllerAsh::Observe( |
| 243 int type, |
| 244 const content::NotificationSource& source, |
| 245 const content::NotificationDetails& details) { |
| 246 DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type); |
| 247 if (!controller_->IsEnabled()) |
| 248 return; |
| 249 |
| 250 bool in_tab_fullscreen = content::Source<FullscreenController>(source)-> |
| 251 IsFullscreenForTabOrPending(); |
| 252 |
| 253 bool used_tab_indicators = use_tab_indicators_; |
| 254 use_tab_indicators_ = !in_tab_fullscreen; |
| 255 SetTabIndicatorsVisible(use_tab_indicators_ && !controller_->IsRevealed()); |
| 256 |
| 257 // Auto hide the shelf in immersive browser fullscreen. When auto hidden, the |
| 258 // shelf displays a 3px 'light bar' when it is closed. When in immersive |
| 259 // browser fullscreen and tab fullscreen, hide the shelf completely and |
| 260 // prevent it from being revealed. |
| 261 ash::wm::GetWindowState(native_window_)->set_hide_shelf_when_fullscreen( |
| 262 in_tab_fullscreen); |
| 263 ash::Shell::GetInstance()->UpdateShelfVisibility(); |
| 264 |
| 265 if (use_tab_indicators_ != used_tab_indicators) |
| 266 LayoutBrowserRootView(); |
| 267 } |
OLD | NEW |