| 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" | 17 #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" | 18 #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" | 19 #include "ui/views/view.h" |
| 30 #include "ui/views/widget/widget.h" | 20 #include "ui/views/widget/widget.h" |
| 31 #include "ui/views/window/non_client_view.h" | 21 #include "ui/views/window/non_client_view.h" |
| 32 | 22 |
| 33 using views::View; | |
| 34 | |
| 35 namespace { | 23 namespace { |
| 36 | 24 |
| 37 // The slide open/closed animation looks better if it starts and ends just a | 25 // Revealing the TopContainerView looks better if the animation starts and ends |
| 38 // few pixels before the view goes completely off the screen, which reduces | 26 // 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. | 27 // "pop" as the 3-pixel tall "light bar" style tab strip becomes visible. |
| 40 const int kAnimationOffsetY = 3; | 28 const int kAnimationOffsetY = 3; |
| 41 | 29 |
| 42 // Duration for the reveal show/hide slide animation. The slower duration is | 30 // 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 | 31 // steal touch events targetted at the web contents while in immersive |
| 44 // happened. | 32 // fullscreen. This region is used to allow us to get edge gestures even if the |
| 45 const int kRevealSlowAnimationDurationMs = 400; | 33 // web contents consumes all touch events. |
| 46 const int kRevealFastAnimationDurationMs = 200; | 34 const int kStealTouchEventsFromWebContentsRegionHeightPx = 8; |
| 47 | 35 |
| 48 // The delay in milliseconds between the mouse stopping at the top edge of the | 36 // Converts from ImmersiveModeController::AnimateReveal to |
| 49 // screen and the top-of-window views revealing. | 37 // ash::ImmersiveFullscreenController::AnimateReveal. |
| 50 const int kMouseRevealDelayMs = 200; | 38 ash::ImmersiveFullscreenController::AnimateReveal |
| 51 | 39 ToImmersiveFullscreenControllerAnimateReveal( |
| 52 // The maximum amount of pixels that the cursor can move for the cursor to be | 40 ImmersiveModeController::AnimateReveal animate_reveal) { |
| 53 // considered "stopped". This allows the user to reveal the top-of-window views | 41 switch (animate_reveal) { |
| 54 // without holding the cursor completely still. | 42 case ImmersiveModeController::ANIMATE_REVEAL_YES: |
| 55 const int kMouseRevealXThresholdPixels = 3; | 43 return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_YES; |
| 56 | 44 case ImmersiveModeController::ANIMATE_REVEAL_NO: |
| 57 // How many pixels a gesture can start away from |top_container_| when in | 45 return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO; |
| 58 // closed state and still be considered near it. This is needed to overcome | 46 } |
| 59 // issues with poor location values near the edge of the display. | 47 NOTREACHED(); |
| 60 const int kNearTopContainerDistance = 8; | 48 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 } | 49 } |
| 90 | 50 |
| 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 | 51 } // namespace |
| 138 | 52 |
| 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() | 53 ImmersiveModeControllerAsh::ImmersiveModeControllerAsh() |
| 256 : delegate_(NULL), | 54 : controller_(new ash::ImmersiveFullscreenController), |
| 257 widget_(NULL), | 55 browser_view_(NULL), |
| 258 top_container_(NULL), | |
| 259 observers_enabled_(false), | |
| 260 enabled_(false), | |
| 261 reveal_state_(CLOSED), | |
| 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), | 56 native_window_(NULL), |
| 267 animation_(new gfx::SlideAnimation(this)), | 57 use_tab_indicators_(false), |
| 268 animations_disabled_for_test_(false), | 58 visible_fraction_(1), |
| 269 weak_ptr_factory_(this) { | 59 observers_enabled_(false) { |
| 270 } | 60 } |
| 271 | 61 |
| 272 ImmersiveModeControllerAsh::~ImmersiveModeControllerAsh() { | 62 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); | 63 EnableWindowObservers(false); |
| 277 } | 64 } |
| 278 | 65 |
| 279 void ImmersiveModeControllerAsh::LockRevealedState( | 66 void ImmersiveModeControllerAsh::Init(BrowserView* browser_view) { |
| 280 AnimateReveal animate_reveal) { | 67 browser_view_ = browser_view; |
| 281 ++revealed_lock_count_; | 68 native_window_ = browser_view_->GetNativeWindow(); |
| 282 Animate animate = (animate_reveal == ANIMATE_REVEAL_YES) ? | 69 controller_->Init(this, browser_view_->frame(), |
| 283 ANIMATE_FAST : ANIMATE_NO; | 70 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 } | 71 } |
| 307 | 72 |
| 308 void ImmersiveModeControllerAsh::SetEnabled(bool enabled) { | 73 void ImmersiveModeControllerAsh::SetEnabled(bool enabled) { |
| 309 DCHECK(native_window_) << "Must initialize before enabling"; | 74 if (controller_->IsEnabled() == enabled) |
| 310 if (enabled_ == enabled) | |
| 311 return; | 75 return; |
| 312 enabled_ = enabled; | |
| 313 | 76 |
| 314 EnableWindowObservers(enabled_); | 77 EnableWindowObservers(enabled); |
| 315 | 78 |
| 316 UpdateUseMinimalChrome(LAYOUT_NO); | 79 // A short "light bar" version of the tab strip is painted when the |
| 80 // top-of-window views are closed. If the user additionally enters into |
| 81 // tab fullscreen, the tab indicators will be hidden. |
| 82 use_tab_indicators_ = enabled; |
| 317 | 83 |
| 318 if (enabled_) { | 84 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 } | 85 } |
| 355 | 86 |
| 356 bool ImmersiveModeControllerAsh::IsEnabled() const { | 87 bool ImmersiveModeControllerAsh::IsEnabled() const { |
| 357 return enabled_; | 88 return controller_->IsEnabled(); |
| 358 } | 89 } |
| 359 | 90 |
| 360 bool ImmersiveModeControllerAsh::ShouldHideTabIndicators() const { | 91 bool ImmersiveModeControllerAsh::ShouldHideTabIndicators() const { |
| 361 return tab_indicator_visibility_ != TAB_INDICATORS_SHOW; | 92 return !use_tab_indicators_; |
| 362 } | 93 } |
| 363 | 94 |
| 364 bool ImmersiveModeControllerAsh::ShouldHideTopViews() const { | 95 bool ImmersiveModeControllerAsh::ShouldHideTopViews() const { |
| 365 return enabled_ && reveal_state_ == CLOSED; | 96 return controller_->IsEnabled() && !controller_->IsRevealed(); |
| 366 } | 97 } |
| 367 | 98 |
| 368 bool ImmersiveModeControllerAsh::IsRevealed() const { | 99 bool ImmersiveModeControllerAsh::IsRevealed() const { |
| 369 return enabled_ && reveal_state_ != CLOSED; | 100 return controller_->IsRevealed(); |
| 370 } | 101 } |
| 371 | 102 |
| 372 int ImmersiveModeControllerAsh::GetTopContainerVerticalOffset( | 103 int ImmersiveModeControllerAsh::GetTopContainerVerticalOffset( |
| 373 const gfx::Size& top_container_size) const { | 104 const gfx::Size& top_container_size) const { |
| 374 if (!enabled_ || reveal_state_ == REVEALED || reveal_state_ == CLOSED) | 105 if (!IsEnabled()) |
| 375 return 0; | 106 return 0; |
| 376 | 107 |
| 377 return animation_->CurrentValueBetween( | 108 // The TopContainerView is flush with the top of |browser_view_| when the |
| 378 -top_container_size.height() + kAnimationOffsetY, 0); | 109 // top-of-window views are fully closed so that when the tab indicators are |
| 110 // used, the "light bar" style tab strip is flush with the top of |
| 111 // |browser_view_|. |
| 112 if (!IsRevealed()) |
| 113 return 0; |
| 114 |
| 115 int height = top_container_size.height() - kAnimationOffsetY; |
| 116 return static_cast<int>(height * (visible_fraction_ - 1)); |
| 379 } | 117 } |
| 380 | 118 |
| 381 ImmersiveRevealedLock* ImmersiveModeControllerAsh::GetRevealedLock( | 119 ImmersiveRevealedLock* ImmersiveModeControllerAsh::GetRevealedLock( |
| 382 AnimateReveal animate_reveal) { | 120 AnimateReveal animate_reveal) { |
| 383 return new RevealedLockAsh(weak_ptr_factory_.GetWeakPtr(), animate_reveal); | 121 return controller_->GetRevealedLock( |
| 122 ToImmersiveFullscreenControllerAnimateReveal(animate_reveal)); |
| 384 } | 123 } |
| 385 | 124 |
| 386 void ImmersiveModeControllerAsh::OnFindBarVisibleBoundsChanged( | 125 void ImmersiveModeControllerAsh::OnFindBarVisibleBoundsChanged( |
| 387 const gfx::Rect& new_visible_bounds_in_screen) { | 126 const gfx::Rect& new_visible_bounds_in_screen) { |
| 388 find_bar_visible_bounds_in_screen_ = new_visible_bounds_in_screen; | 127 find_bar_visible_bounds_in_screen_ = new_visible_bounds_in_screen; |
| 389 } | 128 } |
| 390 | 129 |
| 391 void ImmersiveModeControllerAsh::SetupForTest() { | 130 void ImmersiveModeControllerAsh::SetupForTest() { |
| 392 DCHECK(!enabled_); | 131 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 } | 132 } |
| 402 | 133 |
| 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) { | 134 void ImmersiveModeControllerAsh::EnableWindowObservers(bool enable) { |
| 591 if (observers_enabled_ == enable) | 135 if (observers_enabled_ == enable) |
| 592 return; | 136 return; |
| 593 observers_enabled_ = enable; | 137 observers_enabled_ = enable; |
| 594 | 138 |
| 595 if (!native_window_) { | 139 content::Source<FullscreenController> source( |
| 596 NOTREACHED() << "ImmersiveModeControllerAsh not initialized"; | 140 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) { | 141 if (enable) { |
| 604 widget->AddObserver(this); | 142 ash::wm::GetWindowState(native_window_)->AddObserver(this); |
| 605 focus_manager->AddFocusChangeListener(this); | 143 registrar_.Add(this, chrome::NOTIFICATION_FULLSCREEN_CHANGED, source); |
| 606 } else { | 144 } else { |
| 607 widget->RemoveObserver(this); | 145 ash::wm::GetWindowState(native_window_)->RemoveObserver(this); |
| 608 focus_manager->RemoveFocusChangeListener(this); | 146 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 } | 147 } |
| 769 } | 148 } |
| 770 | 149 |
| 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() { | 150 void ImmersiveModeControllerAsh::LayoutBrowserRootView() { |
| 151 views::Widget* widget = browser_view_->frame(); |
| 953 // Update the window caption buttons. | 152 // Update the window caption buttons. |
| 954 widget_->non_client_view()->frame_view()->ResetWindowControls(); | 153 widget->non_client_view()->frame_view()->ResetWindowControls(); |
| 955 // Layout all views, including BrowserView. | 154 // Layout all views, including BrowserView. |
| 956 widget_->GetRootView()->Layout(); | 155 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 } | 156 } |
| 1071 | 157 |
| 1072 void ImmersiveModeControllerAsh::SetRenderWindowTopInsetsForTouch( | 158 void ImmersiveModeControllerAsh::SetRenderWindowTopInsetsForTouch( |
| 1073 int top_inset) { | 159 int top_inset) { |
| 1074 content::WebContents* contents = delegate_->GetWebContents(); | 160 content::WebContents* contents = browser_view_->GetActiveWebContents(); |
| 1075 if (contents) { | 161 if (contents) { |
| 1076 aura::Window* window = contents->GetView()->GetContentNativeView(); | 162 aura::Window* window = contents->GetView()->GetContentNativeView(); |
| 1077 // |window| is NULL if the renderer crashed. | 163 // |window| is NULL if the renderer crashed. |
| 1078 if (window) { | 164 if (window) { |
| 1079 gfx::Insets inset(top_inset, 0, 0, 0); | 165 gfx::Insets inset(top_inset, 0, 0, 0); |
| 1080 window->SetHitTestBoundsOverrideOuter( | 166 window->SetHitTestBoundsOverrideOuter( |
| 1081 window->hit_test_bounds_override_outer_mouse(), | 167 window->hit_test_bounds_override_outer_mouse(), |
| 1082 inset); | 168 inset); |
| 1083 } | 169 } |
| 1084 } | 170 } |
| 1085 } | 171 } |
| 1086 | 172 |
| 1087 void ImmersiveModeControllerAsh::RecreateBubbleManager() { | 173 void ImmersiveModeControllerAsh::SetTabIndicatorsVisible(bool visible) { |
| 1088 bubble_manager_.reset(new BubbleManager(this)); | 174 DCHECK(!visible || use_tab_indicators_); |
| 1089 const std::vector<aura::Window*> transient_children = | 175 if (browser_view_->tabstrip()) |
| 1090 native_window_->transient_children(); | 176 browser_view_->tabstrip()->SetImmersiveStyle(visible); |
| 1091 for (size_t i = 0; i < transient_children.size(); ++i) { | 177 } |
| 1092 aura::Window* transient_child = transient_children[i]; | 178 |
| 1093 views::BubbleDelegateView* bubble_delegate = | 179 void ImmersiveModeControllerAsh::OnImmersiveRevealStarted() { |
| 1094 AsBubbleDelegate(transient_child); | 180 visible_fraction_ = 0; |
| 1095 if (bubble_delegate && | 181 browser_view_->top_container()->SetPaintToLayer(true); |
| 1096 bubble_delegate->GetAnchorView() && | 182 SetTabIndicatorsVisible(false); |
| 1097 top_container_->Contains(bubble_delegate->GetAnchorView())) { | 183 SetRenderWindowTopInsetsForTouch(0); |
| 1098 bubble_manager_->StartObserving(transient_child); | 184 LayoutBrowserRootView(); |
| 1099 } | 185 FOR_EACH_OBSERVER(Observer, observers_, OnImmersiveRevealStarted()); |
| 186 } |
| 187 |
| 188 void ImmersiveModeControllerAsh::OnImmersiveRevealEnded() { |
| 189 visible_fraction_ = 0; |
| 190 browser_view_->top_container()->SetPaintToLayer(false); |
| 191 SetTabIndicatorsVisible(use_tab_indicators_); |
| 192 SetRenderWindowTopInsetsForTouch( |
| 193 kStealTouchEventsFromWebContentsRegionHeightPx); |
| 194 LayoutBrowserRootView(); |
| 195 } |
| 196 |
| 197 void ImmersiveModeControllerAsh::OnImmersiveFullscreenExited() { |
| 198 browser_view_->top_container()->SetPaintToLayer(false); |
| 199 SetTabIndicatorsVisible(false); |
| 200 SetRenderWindowTopInsetsForTouch(0); |
| 201 LayoutBrowserRootView(); |
| 202 } |
| 203 |
| 204 void ImmersiveModeControllerAsh::SetVisibility(double visible_fraction) { |
| 205 if (visible_fraction_ != visible_fraction) { |
| 206 visible_fraction_ = visible_fraction; |
| 207 browser_view_->Layout(); |
| 1100 } | 208 } |
| 1101 } | 209 } |
| 210 |
| 211 std::vector<gfx::Rect> |
| 212 ImmersiveModeControllerAsh::GetVisibleBoundsInScreen() { |
| 213 views::View* top_container_view = browser_view_->top_container(); |
| 214 gfx::Rect top_container_view_bounds = top_container_view->GetVisibleBounds(); |
| 215 // TODO(tdanderson): Implement View::ConvertRectToScreen(). |
| 216 gfx::Point top_container_view_bounds_in_screen_origin( |
| 217 top_container_view_bounds.origin()); |
| 218 views::View::ConvertPointToScreen(top_container_view, |
| 219 &top_container_view_bounds_in_screen_origin); |
| 220 gfx::Rect top_container_view_bounds_in_screen( |
| 221 top_container_view_bounds_in_screen_origin, |
| 222 top_container_view_bounds.size()); |
| 223 |
| 224 std::vector<gfx::Rect> bounds_in_screen; |
| 225 bounds_in_screen.push_back(top_container_view_bounds_in_screen); |
| 226 bounds_in_screen.push_back(find_bar_visible_bounds_in_screen_); |
| 227 return bounds_in_screen; |
| 228 } |
| 229 |
| 230 void ImmersiveModeControllerAsh::OnWindowShowTypeChanged( |
| 231 ash::wm::WindowState* window_state, |
| 232 ash::wm::WindowShowType old_type) { |
| 233 // Disable immersive fullscreen when the user exits fullscreen without going |
| 234 // through FullscreenController::ToggleFullscreenMode(). This is the case if |
| 235 // the user exits fullscreen via the restore button. |
| 236 if (controller_->IsEnabled() && |
| 237 !window_state->IsFullscreen() && |
| 238 !window_state->IsMinimized()) { |
| 239 browser_view_->FullscreenStateChanged(); |
| 240 } |
| 241 } |
| 242 |
| 243 void ImmersiveModeControllerAsh::Observe( |
| 244 int type, |
| 245 const content::NotificationSource& source, |
| 246 const content::NotificationDetails& details) { |
| 247 DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type); |
| 248 if (!controller_->IsEnabled()) |
| 249 return; |
| 250 |
| 251 bool in_tab_fullscreen = content::Source<FullscreenController>(source)-> |
| 252 IsFullscreenForTabOrPending(); |
| 253 |
| 254 bool used_tab_indicators = use_tab_indicators_; |
| 255 use_tab_indicators_ = !in_tab_fullscreen; |
| 256 |
| 257 // Auto hide the shelf in immersive browser fullscren. When auto hidden, the |
| 258 // shelf displays a 3px 'light bar'. When in immersive browser fullscreen and |
| 259 // tab fullscreen, hide the shelf completely and prevent it from being |
| 260 // 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 // If the top-of-window views are revealed or are animating, the change will |
| 267 // take effect with the layout once the top-of-window views are closed. |
| 268 if (!controller_->IsRevealed()) |
| 269 LayoutBrowserRootView(); |
| 270 } |
| 271 } |
| OLD | NEW |