1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ash/shelf/shelf_layout_manager.h" 6 7 #include <algorithm> 8 #include <cmath> 9 #include <cstring> 10 #include <string> 11 #include <vector> 12 13 #include "ash/accelerators/accelerator_commands.h" 14 #include "ash/ash_switches.h" 15 #include "ash/root_window_controller.h" 16 #include "ash/screen_util.h" 17 #include "ash/session/session_state_delegate.h" 18 #include "ash/shelf/shelf.h" 19 #include "ash/shelf/shelf_bezel_event_filter.h" 20 #include "ash/shelf/shelf_constants.h" 21 #include "ash/shelf/shelf_layout_manager_observer.h" 22 #include "ash/shelf/shelf_widget.h" 23 #include "ash/shell.h" 24 #include "ash/shell_window_ids.h" 25 #include "ash/system/status_area_widget.h" 26 #include "ash/wm/gestures/shelf_gesture_handler.h" 27 #include "ash/wm/lock_state_controller.h" 28 #include "ash/wm/mru_window_tracker.h" 29 #include "ash/wm/window_animations.h" 30 #include "ash/wm/window_state.h" 31 #include "ash/wm/window_util.h" 32 #include "ash/wm/workspace_controller.h" 33 #include "base/auto_reset.h" 34 #include "base/command_line.h" 35 #include "base/command_line.h" 36 #include "base/i18n/rtl.h" 37 #include "base/strings/string_number_conversions.h" 38 #include "base/strings/string_util.h" 39 #include "ui/aura/client/cursor_client.h" 40 #include "ui/aura/window_event_dispatcher.h" 41 #include "ui/base/ui_base_switches.h" 42 #include "ui/compositor/layer.h" 43 #include "ui/compositor/layer_animation_observer.h" 44 #include "ui/compositor/layer_animator.h" 45 #include "ui/compositor/scoped_layer_animation_settings.h" 46 #include "ui/events/event.h" 47 #include "ui/events/event_handler.h" 48 #include "ui/gfx/screen.h" 49 #include "ui/keyboard/keyboard_util.h" 50 #include "ui/views/widget/widget.h" 51 #include "ui/wm/public/activation_client.h" 52 53 namespace ash { 54 namespace { 55 56 // Delay before showing the shelf. This is after the mouse stops moving. 57 const int kAutoHideDelayMS = 200; 58 59 // To avoid hiding the shelf when the mouse transitions from a message bubble 60 // into the shelf, the hit test area is enlarged by this amount of pixels to 61 // keep the shelf from hiding. 62 const int kNotificationBubbleGapHeight = 6; 63 64 // The maximum size of the region on the display opposing the shelf managed by 65 // this ShelfLayoutManager which can trigger showing the shelf. 66 // For instance: 67 // - Primary display is left of secondary display. 68 // - Shelf is left aligned 69 // - This ShelfLayoutManager manages the shelf for the secondary display. 70 // |kMaxAutoHideShowShelfRegionSize| refers to the maximum size of the region 71 // from the right edge of the primary display which can trigger showing the 72 // auto hidden shelf. The region is used to make it easier to trigger showing 73 // the auto hidden shelf when the shelf is on the boundary between displays. 74 const int kMaxAutoHideShowShelfRegionSize = 10; 75 76 ui::Layer* GetLayer(views::Widget* widget) { 77 return widget->GetNativeView()->layer(); 78 } 79 80 bool IsDraggingTrayEnabled() { 81 static bool dragging_tray_allowed = CommandLine::ForCurrentProcess()-> 82 HasSwitch(ash::switches::kAshEnableTrayDragging); 83 return dragging_tray_allowed; 84 } 85 86 } // namespace 87 88 // static 89 const int ShelfLayoutManager::kWorkspaceAreaVisibleInset = 2; 90 91 // static 92 const int ShelfLayoutManager::kWorkspaceAreaAutoHideInset = 5; 93 94 // static 95 const int ShelfLayoutManager::kAutoHideSize = 3; 96 97 // static 98 const int ShelfLayoutManager::kShelfItemInset = 3; 99 100 // ShelfLayoutManager::AutoHideEventFilter ------------------------------------- 101 102 // Notifies ShelfLayoutManager any time the mouse moves. 103 class ShelfLayoutManager::AutoHideEventFilter : public ui::EventHandler { 104 public: 105 explicit AutoHideEventFilter(ShelfLayoutManager* shelf); 106 virtual ~AutoHideEventFilter(); 107 108 // Returns true if the last mouse event was a mouse drag. 109 bool in_mouse_drag() const { return in_mouse_drag_; } 110 111 // Overridden from ui::EventHandler: 112 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; 113 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; 114 115 private: 116 ShelfLayoutManager* shelf_; 117 bool in_mouse_drag_; 118 ShelfGestureHandler gesture_handler_; 119 DISALLOW_COPY_AND_ASSIGN(AutoHideEventFilter); 120 }; 121 122 ShelfLayoutManager::AutoHideEventFilter::AutoHideEventFilter( 123 ShelfLayoutManager* shelf) 124 : shelf_(shelf), 125 in_mouse_drag_(false) { 126 Shell::GetInstance()->AddPreTargetHandler(this); 127 } 128 129 ShelfLayoutManager::AutoHideEventFilter::~AutoHideEventFilter() { 130 Shell::GetInstance()->RemovePreTargetHandler(this); 131 } 132 133 void ShelfLayoutManager::AutoHideEventFilter::OnMouseEvent( 134 ui::MouseEvent* event) { 135 // This also checks IsShelfWindow() to make sure we don't attempt to hide the 136 // shelf if the mouse down occurs on the shelf. 137 in_mouse_drag_ = (event->type() == ui::ET_MOUSE_DRAGGED || 138 (in_mouse_drag_ && event->type() != ui::ET_MOUSE_RELEASED && 139 event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)) && 140 !shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target())); 141 if (event->type() == ui::ET_MOUSE_MOVED) 142 shelf_->UpdateAutoHideState(); 143 return; 144 } 145 146 void ShelfLayoutManager::AutoHideEventFilter::OnGestureEvent( 147 ui::GestureEvent* event) { 148 if (shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target()))) { 149 if (gesture_handler_.ProcessGestureEvent(*event)) 150 event->StopPropagation(); 151 } 152 } 153 154 // ShelfLayoutManager:UpdateShelfObserver -------------------------------------- 155 156 // UpdateShelfObserver is used to delay updating the background until the 157 // animation completes. 158 class ShelfLayoutManager::UpdateShelfObserver 159 : public ui::ImplicitAnimationObserver { 160 public: 161 explicit UpdateShelfObserver(ShelfLayoutManager* shelf) : shelf_(shelf) { 162 shelf_->update_shelf_observer_ = this; 163 } 164 165 void Detach() { 166 shelf_ = NULL; 167 } 168 169 virtual void OnImplicitAnimationsCompleted() OVERRIDE { 170 if (shelf_) 171 shelf_->UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE); 172 delete this; 173 } 174 175 private: 176 virtual ~UpdateShelfObserver() { 177 if (shelf_) 178 shelf_->update_shelf_observer_ = NULL; 179 } 180 181 // Shelf we're in. NULL if deleted before we're deleted. 182 ShelfLayoutManager* shelf_; 183 184 DISALLOW_COPY_AND_ASSIGN(UpdateShelfObserver); 185 }; 186 187 // ShelfLayoutManager ---------------------------------------------------------- 188 189 ShelfLayoutManager::ShelfLayoutManager(ShelfWidget* shelf) 190 : root_window_(shelf->GetNativeView()->GetRootWindow()), 191 updating_bounds_(false), 192 auto_hide_behavior_(SHELF_AUTO_HIDE_BEHAVIOR_NEVER), 193 alignment_(SHELF_ALIGNMENT_BOTTOM), 194 shelf_(shelf), 195 workspace_controller_(NULL), 196 window_overlaps_shelf_(false), 197 mouse_over_shelf_when_auto_hide_timer_started_(false), 198 bezel_event_filter_(new ShelfBezelEventFilter(this)), 199 gesture_drag_status_(GESTURE_DRAG_NONE), 200 gesture_drag_amount_(0.f), 201 gesture_drag_auto_hide_state_(SHELF_AUTO_HIDE_SHOWN), 202 update_shelf_observer_(NULL), 203 duration_override_in_ms_(0) { 204 Shell::GetInstance()->AddShellObserver(this); 205 Shell::GetInstance()->lock_state_controller()->AddObserver(this); 206 aura::client::GetActivationClient(root_window_)->AddObserver(this); 207 Shell::GetInstance()->session_state_delegate()->AddSessionStateObserver(this); 208 } 209 210 ShelfLayoutManager::~ShelfLayoutManager() { 211 if (update_shelf_observer_) 212 update_shelf_observer_->Detach(); 213 214 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, WillDeleteShelf()); 215 Shell::GetInstance()->RemoveShellObserver(this); 216 Shell::GetInstance()->lock_state_controller()->RemoveObserver(this); 217 aura::client::GetActivationClient(root_window_)->RemoveObserver(this); 218 Shell::GetInstance()-> 219 session_state_delegate()->RemoveSessionStateObserver(this); 220 } 221 222 void ShelfLayoutManager::SetAutoHideBehavior(ShelfAutoHideBehavior behavior) { 223 if (auto_hide_behavior_ == behavior) 224 return; 225 auto_hide_behavior_ = behavior; 226 UpdateVisibilityState(); 227 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, 228 OnAutoHideBehaviorChanged(root_window_, 229 auto_hide_behavior_)); 230 } 231 232 void ShelfLayoutManager::PrepareForShutdown() { 233 // Clear all event filters, otherwise sometimes those filters may catch 234 // synthesized mouse event and cause crashes during the shutdown. 235 set_workspace_controller(NULL); 236 auto_hide_event_filter_.reset(); 237 bezel_event_filter_.reset(); 238 } 239 240 bool ShelfLayoutManager::IsVisible() const { 241 // status_area_widget() may be NULL during the shutdown. 242 return shelf_->status_area_widget() && 243 shelf_->status_area_widget()->IsVisible() && 244 (state_.visibility_state == SHELF_VISIBLE || 245 (state_.visibility_state == SHELF_AUTO_HIDE && 246 state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN)); 247 } 248 249 bool ShelfLayoutManager::SetAlignment(ShelfAlignment alignment) { 250 if (alignment_ == alignment) 251 return false; 252 253 // This should not be called during the lock screen transitions. 254 DCHECK(!Shell::GetInstance()->session_state_delegate()->IsScreenLocked()); 255 alignment_ = alignment; 256 shelf_->SetAlignment(alignment); 257 LayoutShelf(); 258 return true; 259 } 260 261 ShelfAlignment ShelfLayoutManager::GetAlignment() const { 262 // When the screen is locked, the shelf is forced into bottom alignment. 263 if (Shell::GetInstance()->session_state_delegate()->IsScreenLocked()) 264 return SHELF_ALIGNMENT_BOTTOM; 265 return alignment_; 266 } 267 268 gfx::Rect ShelfLayoutManager::GetIdealBounds() { 269 gfx::Rect bounds( 270 ScreenUtil::GetDisplayBoundsInParent(shelf_->GetNativeView())); 271 int width = 0, height = 0; 272 GetShelfSize(&width, &height); 273 return SelectValueForShelfAlignment( 274 gfx::Rect(bounds.x(), bounds.bottom() - height, bounds.width(), height), 275 gfx::Rect(bounds.x(), bounds.y(), width, bounds.height()), 276 gfx::Rect(bounds.right() - width, bounds.y(), width, bounds.height()), 277 gfx::Rect(bounds.x(), bounds.y(), bounds.width(), height)); 278 } 279 280 void ShelfLayoutManager::LayoutShelf() { 281 TargetBounds target_bounds; 282 CalculateTargetBounds(state_, &target_bounds); 283 UpdateBoundsAndOpacity(target_bounds, false, NULL); 284 285 if (shelf_->shelf()) { 286 // This is not part of UpdateBoundsAndOpacity() because 287 // SetShelfViewBounds() sets the bounds immediately and does not animate. 288 // The height of the ShelfView for a horizontal shelf and the width of 289 // the ShelfView for a vertical shelf are set when |shelf_|'s bounds 290 // are changed via UpdateBoundsAndOpacity(). This sets the origin and the 291 // dimension in the other direction. 292 shelf_->shelf()->SetShelfViewBounds( 293 target_bounds.shelf_bounds_in_shelf); 294 } 295 } 296 297 ShelfVisibilityState ShelfLayoutManager::CalculateShelfVisibility() { 298 switch(auto_hide_behavior_) { 299 case SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS: 300 return SHELF_AUTO_HIDE; 301 case SHELF_AUTO_HIDE_BEHAVIOR_NEVER: 302 return SHELF_VISIBLE; 303 case SHELF_AUTO_HIDE_ALWAYS_HIDDEN: 304 return SHELF_HIDDEN; 305 } 306 return SHELF_VISIBLE; 307 } 308 309 void ShelfLayoutManager::UpdateVisibilityState() { 310 // Bail out early when there is no |workspace_controller_|, which happens 311 // during shutdown after PrepareForShutdown. 312 if (!workspace_controller_) 313 return; 314 315 if (Shell::GetInstance()->session_state_delegate()->IsScreenLocked()) { 316 SetState(SHELF_VISIBLE); 317 } else { 318 // TODO(zelidrag): Verify shelf drag animation still shows on the device 319 // when we are in SHELF_AUTO_HIDE_ALWAYS_HIDDEN. 320 WorkspaceWindowState window_state(workspace_controller_->GetWindowState()); 321 switch (window_state) { 322 case WORKSPACE_WINDOW_STATE_FULL_SCREEN: { 323 const aura::Window* fullscreen_window = GetRootWindowController( 324 root_window_)->GetWindowForFullscreenMode(); 325 if (fullscreen_window && wm::GetWindowState(fullscreen_window)-> 326 hide_shelf_when_fullscreen()) { 327 SetState(SHELF_HIDDEN); 328 } else { 329 // The shelf is sometimes not hidden when in immersive fullscreen. 330 // Force the shelf to be auto hidden in this case. 331 SetState(SHELF_AUTO_HIDE); 332 } 333 break; 334 } 335 336 case WORKSPACE_WINDOW_STATE_MAXIMIZED: 337 SetState(CalculateShelfVisibility()); 338 break; 339 340 case WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF: 341 case WORKSPACE_WINDOW_STATE_DEFAULT: 342 SetState(CalculateShelfVisibility()); 343 SetWindowOverlapsShelf(window_state == 344 WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF); 345 break; 346 } 347 } 348 } 349 350 void ShelfLayoutManager::UpdateAutoHideState() { 351 ShelfAutoHideState auto_hide_state = 352 CalculateAutoHideState(state_.visibility_state); 353 if (auto_hide_state != state_.auto_hide_state) { 354 if (auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) { 355 // Hides happen immediately. 356 SetState(state_.visibility_state); 357 } else { 358 if (!auto_hide_timer_.IsRunning()) { 359 mouse_over_shelf_when_auto_hide_timer_started_ = 360 shelf_->GetWindowBoundsInScreen().Contains( 361 Shell::GetScreen()->GetCursorScreenPoint()); 362 } 363 auto_hide_timer_.Start( 364 FROM_HERE, 365 base::TimeDelta::FromMilliseconds(kAutoHideDelayMS), 366 this, &ShelfLayoutManager::UpdateAutoHideStateNow); 367 } 368 } else { 369 StopAutoHideTimer(); 370 } 371 } 372 373 void ShelfLayoutManager::SetWindowOverlapsShelf(bool value) { 374 window_overlaps_shelf_ = value; 375 UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE); 376 } 377 378 void ShelfLayoutManager::AddObserver(ShelfLayoutManagerObserver* observer) { 379 observers_.AddObserver(observer); 380 } 381 382 void ShelfLayoutManager::RemoveObserver(ShelfLayoutManagerObserver* observer) { 383 observers_.RemoveObserver(observer); 384 } 385 386 //////////////////////////////////////////////////////////////////////////////// 387 // ShelfLayoutManager, Gesture functions: 388 389 void ShelfLayoutManager::OnGestureEdgeSwipe(const ui::GestureEvent& gesture) { 390 if (visibility_state() == SHELF_AUTO_HIDE) { 391 gesture_drag_auto_hide_state_ = SHELF_AUTO_HIDE_SHOWN; 392 gesture_drag_status_ = GESTURE_DRAG_COMPLETE_IN_PROGRESS; 393 UpdateVisibilityState(); 394 gesture_drag_status_ = GESTURE_DRAG_NONE; 395 } 396 } 397 398 void ShelfLayoutManager::StartGestureDrag(const ui::GestureEvent& gesture) { 399 gesture_drag_status_ = GESTURE_DRAG_IN_PROGRESS; 400 gesture_drag_amount_ = 0.f; 401 gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE ? 402 auto_hide_state() : SHELF_AUTO_HIDE_SHOWN; 403 UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE); 404 } 405 406 ShelfLayoutManager::DragState ShelfLayoutManager::UpdateGestureDrag( 407 const ui::GestureEvent& gesture) { 408 bool horizontal = IsHorizontalAlignment(); 409 gesture_drag_amount_ += horizontal ? gesture.details().scroll_y() : 410 gesture.details().scroll_x(); 411 LayoutShelf(); 412 413 // Start reveling the status menu when: 414 // - dragging up on an already visible shelf 415 // - dragging up on a hidden shelf, but it is currently completely visible. 416 if (horizontal && gesture.details().scroll_y() < 0) { 417 int min_height = 0; 418 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN && shelf_) 419 min_height = shelf_->GetContentsView()->GetPreferredSize().height(); 420 421 if (min_height < shelf_->GetWindowBoundsInScreen().height() && 422 gesture.root_location().x() >= 423 shelf_->status_area_widget()->GetWindowBoundsInScreen().x() && 424 IsDraggingTrayEnabled()) 425 return DRAG_TRAY; 426 } 427 428 return DRAG_SHELF; 429 } 430 431 void ShelfLayoutManager::CompleteGestureDrag(const ui::GestureEvent& gesture) { 432 bool horizontal = IsHorizontalAlignment(); 433 bool should_change = false; 434 if (gesture.type() == ui::ET_GESTURE_SCROLL_END) { 435 // The visibility of the shelf changes only if the shelf was dragged X% 436 // along the correct axis. If the shelf was already visible, then the 437 // direction of the drag does not matter. 438 const float kDragHideThreshold = 0.4f; 439 gfx::Rect bounds = GetIdealBounds(); 440 float drag_ratio = fabs(gesture_drag_amount_) / 441 (horizontal ? bounds.height() : bounds.width()); 442 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) { 443 should_change = drag_ratio > kDragHideThreshold; 444 } else { 445 bool correct_direction = false; 446 switch (GetAlignment()) { 447 case SHELF_ALIGNMENT_BOTTOM: 448 case SHELF_ALIGNMENT_RIGHT: 449 correct_direction = gesture_drag_amount_ < 0; 450 break; 451 case SHELF_ALIGNMENT_LEFT: 452 case SHELF_ALIGNMENT_TOP: 453 correct_direction = gesture_drag_amount_ > 0; 454 break; 455 } 456 should_change = correct_direction && drag_ratio > kDragHideThreshold; 457 } 458 } else if (gesture.type() == ui::ET_SCROLL_FLING_START) { 459 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) { 460 should_change = horizontal ? fabs(gesture.details().velocity_y()) > 0 : 461 fabs(gesture.details().velocity_x()) > 0; 462 } else { 463 should_change = SelectValueForShelfAlignment( 464 gesture.details().velocity_y() < 0, 465 gesture.details().velocity_x() > 0, 466 gesture.details().velocity_x() < 0, 467 gesture.details().velocity_y() > 0); 468 } 469 } else { 470 NOTREACHED(); 471 } 472 473 if (!should_change) { 474 CancelGestureDrag(); 475 return; 476 } 477 if (shelf_) { 478 shelf_->Deactivate(); 479 shelf_->status_area_widget()->Deactivate(); 480 } 481 gesture_drag_auto_hide_state_ = 482 gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ? 483 SHELF_AUTO_HIDE_HIDDEN : SHELF_AUTO_HIDE_SHOWN; 484 ShelfAutoHideBehavior new_auto_hide_behavior = 485 gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ? 486 SHELF_AUTO_HIDE_BEHAVIOR_NEVER : SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS; 487 488 // When in fullscreen and the shelf is forced to be auto hidden, the auto hide 489 // behavior affects neither the visibility state nor the auto hide state. Set 490 // |gesture_drag_status_| to GESTURE_DRAG_COMPLETE_IN_PROGRESS to set the auto 491 // hide state to |gesture_drag_auto_hide_state_|. 492 gesture_drag_status_ = GESTURE_DRAG_COMPLETE_IN_PROGRESS; 493 if (auto_hide_behavior_ != new_auto_hide_behavior) 494 SetAutoHideBehavior(new_auto_hide_behavior); 495 else 496 UpdateVisibilityState(); 497 gesture_drag_status_ = GESTURE_DRAG_NONE; 498 } 499 500 void ShelfLayoutManager::CancelGestureDrag() { 501 gesture_drag_status_ = GESTURE_DRAG_CANCEL_IN_PROGRESS; 502 UpdateVisibilityState(); 503 gesture_drag_status_ = GESTURE_DRAG_NONE; 504 } 505 506 void ShelfLayoutManager::SetAnimationDurationOverride( 507 int duration_override_in_ms) { 508 duration_override_in_ms_ = duration_override_in_ms; 509 } 510 511 //////////////////////////////////////////////////////////////////////////////// 512 // ShelfLayoutManager, aura::LayoutManager implementation: 513 514 void ShelfLayoutManager::OnWindowResized() { 515 LayoutShelf(); 516 } 517 518 void ShelfLayoutManager::OnWindowAddedToLayout(aura::Window* child) { 519 } 520 521 void ShelfLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) { 522 } 523 524 void ShelfLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) { 525 } 526 527 void ShelfLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child, 528 bool visible) { 529 } 530 531 void ShelfLayoutManager::SetChildBounds(aura::Window* child, 532 const gfx::Rect& requested_bounds) { 533 SetChildBoundsDirect(child, requested_bounds); 534 // We may contain other widgets (such as frame maximize bubble) but they don't 535 // effect the layout in anyway. 536 if (!updating_bounds_ && 537 ((shelf_->GetNativeView() == child) || 538 (shelf_->status_area_widget()->GetNativeView() == child))) { 539 LayoutShelf(); 540 } 541 } 542 543 void ShelfLayoutManager::OnLockStateChanged(bool locked) { 544 // Force the shelf to layout for alignment (bottom if locked, restore 545 // the previous alignment otherwise). 546 state_.is_screen_locked = locked; 547 shelf_->SetAlignment(locked ? SHELF_ALIGNMENT_BOTTOM : alignment_); 548 UpdateVisibilityState(); 549 LayoutShelf(); 550 } 551 552 void ShelfLayoutManager::OnMaximizeModeStarted() { 553 SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); 554 } 555 556 void ShelfLayoutManager::OnWindowActivated(aura::Window* gained_active, 557 aura::Window* lost_active) { 558 UpdateAutoHideStateNow(); 559 } 560 561 bool ShelfLayoutManager::IsHorizontalAlignment() const { 562 return GetAlignment() == SHELF_ALIGNMENT_BOTTOM || 563 GetAlignment() == SHELF_ALIGNMENT_TOP; 564 } 565 566 // static 567 ShelfLayoutManager* ShelfLayoutManager::ForShelf(aura::Window* window) { 568 ShelfWidget* shelf = RootWindowController::ForShelf(window)->shelf(); 569 return shelf ? shelf->shelf_layout_manager() : NULL; 570 } 571 572 //////////////////////////////////////////////////////////////////////////////// 573 // ShelfLayoutManager, private: 574 575 ShelfLayoutManager::TargetBounds::TargetBounds() : opacity(0.0f) {} 576 ShelfLayoutManager::TargetBounds::~TargetBounds() {} 577 578 void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) { 579 if (!shelf_->GetNativeView()) 580 return; 581 582 State state; 583 state.visibility_state = visibility_state; 584 state.auto_hide_state = CalculateAutoHideState(visibility_state); 585 state.window_state = workspace_controller_ ? 586 workspace_controller_->GetWindowState() : WORKSPACE_WINDOW_STATE_DEFAULT; 587 588 // Force an update because gesture drags affect the shelf bounds and we 589 // should animate back to the normal bounds at the end of a gesture. 590 bool force_update = 591 (gesture_drag_status_ == GESTURE_DRAG_CANCEL_IN_PROGRESS || 592 gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS); 593 594 if (!force_update && state_.Equals(state)) 595 return; // Nothing changed. 596 597 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, 598 WillChangeVisibilityState(visibility_state)); 599 600 if (state.visibility_state == SHELF_AUTO_HIDE) { 601 // When state is SHELF_AUTO_HIDE we need to track when the mouse is over the 602 // shelf to unhide it. AutoHideEventFilter does that for us. 603 if (!auto_hide_event_filter_) 604 auto_hide_event_filter_.reset(new AutoHideEventFilter(this)); 605 } else { 606 auto_hide_event_filter_.reset(NULL); 607 } 608 609 StopAutoHideTimer(); 610 611 State old_state = state_; 612 state_ = state; 613 614 BackgroundAnimatorChangeType change_type = BACKGROUND_CHANGE_ANIMATE; 615 bool delay_background_change = false; 616 617 // Do not animate the background when: 618 // - Going from a hidden / auto hidden shelf in fullscreen to a visible shelf 619 // in maximized mode. 620 // - Going from an auto hidden shelf in maximized mode to a visible shelf in 621 // maximized mode. 622 if (state.visibility_state == SHELF_VISIBLE && 623 state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED && 624 old_state.visibility_state != SHELF_VISIBLE) { 625 change_type = BACKGROUND_CHANGE_IMMEDIATE; 626 } else { 627 // Delay the animation when the shelf was hidden, and has just been made 628 // visible (e.g. using a gesture-drag). 629 if (state.visibility_state == SHELF_VISIBLE && 630 old_state.visibility_state == SHELF_AUTO_HIDE && 631 old_state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) { 632 delay_background_change = true; 633 } 634 } 635 636 if (delay_background_change) { 637 if (update_shelf_observer_) 638 update_shelf_observer_->Detach(); 639 // UpdateShelfBackground deletes itself when the animation is done. 640 update_shelf_observer_ = new UpdateShelfObserver(this); 641 } else { 642 UpdateShelfBackground(change_type); 643 } 644 645 shelf_->SetDimsShelf( 646 state.visibility_state == SHELF_VISIBLE && 647 state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED); 648 649 TargetBounds target_bounds; 650 CalculateTargetBounds(state_, &target_bounds); 651 UpdateBoundsAndOpacity(target_bounds, true, 652 delay_background_change ? update_shelf_observer_ : NULL); 653 654 // OnAutoHideStateChanged Should be emitted when: 655 // - firstly state changed to auto-hide from other state 656 // - or, auto_hide_state has changed 657 if ((old_state.visibility_state != state_.visibility_state && 658 state_.visibility_state == SHELF_AUTO_HIDE) || 659 old_state.auto_hide_state != state_.auto_hide_state) { 660 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, 661 OnAutoHideStateChanged(state_.auto_hide_state)); 662 } 663 } 664 665 void ShelfLayoutManager::UpdateBoundsAndOpacity( 666 const TargetBounds& target_bounds, 667 bool animate, 668 ui::ImplicitAnimationObserver* observer) { 669 base::AutoReset<bool> auto_reset_updating_bounds(&updating_bounds_, true); 670 671 ui::ScopedLayerAnimationSettings shelf_animation_setter( 672 GetLayer(shelf_)->GetAnimator()); 673 ui::ScopedLayerAnimationSettings status_animation_setter( 674 GetLayer(shelf_->status_area_widget())->GetAnimator()); 675 if (animate) { 676 int duration = duration_override_in_ms_ ? duration_override_in_ms_ : 677 kCrossFadeDurationMS; 678 shelf_animation_setter.SetTransitionDuration( 679 base::TimeDelta::FromMilliseconds(duration)); 680 shelf_animation_setter.SetTweenType(gfx::Tween::EASE_OUT); 681 shelf_animation_setter.SetPreemptionStrategy( 682 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 683 status_animation_setter.SetTransitionDuration( 684 base::TimeDelta::FromMilliseconds(duration)); 685 status_animation_setter.SetTweenType(gfx::Tween::EASE_OUT); 686 status_animation_setter.SetPreemptionStrategy( 687 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 688 } else { 689 StopAnimating(); 690 shelf_animation_setter.SetTransitionDuration(base::TimeDelta()); 691 status_animation_setter.SetTransitionDuration(base::TimeDelta()); 692 } 693 if (observer) 694 status_animation_setter.AddObserver(observer); 695 696 GetLayer(shelf_)->SetOpacity(target_bounds.opacity); 697 shelf_->SetBounds(ScreenUtil::ConvertRectToScreen( 698 shelf_->GetNativeView()->parent(), 699 target_bounds.shelf_bounds_in_root)); 700 701 GetLayer(shelf_->status_area_widget())->SetOpacity( 702 target_bounds.status_opacity); 703 704 // Having a window which is visible but does not have an opacity is an illegal 705 // state. We therefore show / hide the shelf here if required. 706 if (!target_bounds.status_opacity) 707 shelf_->status_area_widget()->Hide(); 708 else if (target_bounds.status_opacity) 709 shelf_->status_area_widget()->Show(); 710 711 // TODO(harrym): Once status area widget is a child view of shelf 712 // this can be simplified. 713 gfx::Rect status_bounds = target_bounds.status_bounds_in_shelf; 714 status_bounds.set_x(status_bounds.x() + 715 target_bounds.shelf_bounds_in_root.x()); 716 status_bounds.set_y(status_bounds.y() + 717 target_bounds.shelf_bounds_in_root.y()); 718 shelf_->status_area_widget()->SetBounds( 719 ScreenUtil::ConvertRectToScreen( 720 shelf_->status_area_widget()->GetNativeView()->parent(), 721 status_bounds)); 722 SessionStateDelegate* session_state_delegate = 723 Shell::GetInstance()->session_state_delegate(); 724 if (!state_.is_screen_locked) { 725 gfx::Insets insets; 726 // If user session is blocked (login to new user session or add user to 727 // the existing session - multi-profile) then give 100% of work area only if 728 // keyboard is not shown. 729 if (!session_state_delegate->IsUserSessionBlocked() || 730 !keyboard_bounds_.IsEmpty()) { 731 insets = target_bounds.work_area_insets; 732 } 733 Shell::GetInstance()->SetDisplayWorkAreaInsets(root_window_, insets); 734 } 735 } 736 737 void ShelfLayoutManager::StopAnimating() { 738 GetLayer(shelf_)->GetAnimator()->StopAnimating(); 739 GetLayer(shelf_->status_area_widget())->GetAnimator()->StopAnimating(); 740 } 741 742 void ShelfLayoutManager::GetShelfSize(int* width, int* height) { 743 *width = *height = 0; 744 gfx::Size status_size( 745 shelf_->status_area_widget()->GetWindowBoundsInScreen().size()); 746 if (IsHorizontalAlignment()) 747 *height = kShelfSize; 748 else 749 *width = kShelfSize; 750 } 751 752 void ShelfLayoutManager::AdjustBoundsBasedOnAlignment(int inset, 753 gfx::Rect* bounds) const { 754 bounds->Inset(SelectValueForShelfAlignment( 755 gfx::Insets(0, 0, inset, 0), 756 gfx::Insets(0, inset, 0, 0), 757 gfx::Insets(0, 0, 0, inset), 758 gfx::Insets(inset, 0, 0, 0))); 759 } 760 761 void ShelfLayoutManager::CalculateTargetBounds( 762 const State& state, 763 TargetBounds* target_bounds) { 764 const gfx::Rect available_bounds(root_window_->bounds()); 765 gfx::Rect status_size( 766 shelf_->status_area_widget()->GetWindowBoundsInScreen().size()); 767 int shelf_width = 0, shelf_height = 0; 768 GetShelfSize(&shelf_width, &shelf_height); 769 if (IsHorizontalAlignment()) 770 shelf_width = available_bounds.width(); 771 else 772 shelf_height = available_bounds.height(); 773 774 if (state.visibility_state == SHELF_AUTO_HIDE && 775 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) { 776 // Auto-hidden shelf always starts with the default size. If a gesture-drag 777 // is in progress, then the call to UpdateTargetBoundsForGesture() below 778 // takes care of setting the height properly. 779 if (IsHorizontalAlignment()) 780 shelf_height = kAutoHideSize; 781 else 782 shelf_width = kAutoHideSize; 783 } else if (state.visibility_state == SHELF_HIDDEN || 784 (!keyboard_bounds_.IsEmpty() && !keyboard::IsKeyboardOverscrollEnabled())) 785 { 786 if (IsHorizontalAlignment()) 787 shelf_height = 0; 788 else 789 shelf_width = 0; 790 } 791 792 int bottom_shelf_vertical_offset = available_bounds.bottom(); 793 if (keyboard_bounds_.IsEmpty()) 794 bottom_shelf_vertical_offset -= shelf_height; 795 else 796 bottom_shelf_vertical_offset -= keyboard_bounds_.height(); 797 798 target_bounds->shelf_bounds_in_root = SelectValueForShelfAlignment( 799 gfx::Rect(available_bounds.x(), bottom_shelf_vertical_offset, 800 available_bounds.width(), shelf_height), 801 gfx::Rect(available_bounds.x(), available_bounds.y(), 802 shelf_width, available_bounds.height()), 803 gfx::Rect(available_bounds.right() - shelf_width, available_bounds.y(), 804 shelf_width, available_bounds.height()), 805 gfx::Rect(available_bounds.x(), available_bounds.y(), 806 available_bounds.width(), shelf_height)); 807 808 if (IsHorizontalAlignment()) 809 status_size.set_height(kShelfSize); 810 else 811 status_size.set_width(kShelfSize); 812 813 target_bounds->status_bounds_in_shelf = SelectValueForShelfAlignment( 814 gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(), 815 0, status_size.width(), status_size.height()), 816 gfx::Rect(shelf_width - status_size.width(), 817 shelf_height - status_size.height(), status_size.width(), 818 status_size.height()), 819 gfx::Rect(0, shelf_height - status_size.height(), 820 status_size.width(), status_size.height()), 821 gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(), 822 shelf_height - status_size.height(), 823 status_size.width(), status_size.height())); 824 825 target_bounds->work_area_insets = SelectValueForShelfAlignment( 826 gfx::Insets(0, 0, GetWorkAreaSize(state, shelf_height), 0), 827 gfx::Insets(0, GetWorkAreaSize(state, shelf_width), 0, 0), 828 gfx::Insets(0, 0, 0, GetWorkAreaSize(state, shelf_width)), 829 gfx::Insets(GetWorkAreaSize(state, shelf_height), 0, 0, 0)); 830 831 // TODO(varkha): The functionality of managing insets for display areas 832 // should probably be pushed to a separate component. This would simplify or 833 // remove entirely the dependency on keyboard and dock. 834 835 if (!keyboard_bounds_.IsEmpty() && !keyboard::IsKeyboardOverscrollEnabled()) { 836 // Also push in the work area inset for the keyboard if it is visible. 837 gfx::Insets keyboard_insets(0, 0, keyboard_bounds_.height(), 0); 838 target_bounds->work_area_insets += keyboard_insets; 839 } 840 841 // Also push in the work area inset for the dock if it is visible. 842 if (!dock_bounds_.IsEmpty()) { 843 gfx::Insets dock_insets( 844 0, (dock_bounds_.x() > 0 ? 0 : dock_bounds_.width()), 845 0, (dock_bounds_.x() > 0 ? dock_bounds_.width() : 0)); 846 target_bounds->work_area_insets += dock_insets; 847 } 848 849 target_bounds->opacity = 850 (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS || 851 state.visibility_state == SHELF_VISIBLE || 852 state.visibility_state == SHELF_AUTO_HIDE) ? 1.0f : 0.0f; 853 target_bounds->status_opacity = 854 (state.visibility_state == SHELF_AUTO_HIDE && 855 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN && 856 gesture_drag_status_ != GESTURE_DRAG_IN_PROGRESS) ? 857 0.0f : target_bounds->opacity; 858 859 if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS) 860 UpdateTargetBoundsForGesture(target_bounds); 861 862 // This needs to happen after calling UpdateTargetBoundsForGesture(), because 863 // that can change the size of the shelf. 864 target_bounds->shelf_bounds_in_shelf = SelectValueForShelfAlignment( 865 gfx::Rect(0, 0, 866 shelf_width - status_size.width(), 867 target_bounds->shelf_bounds_in_root.height()), 868 gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(), 869 shelf_height - status_size.height()), 870 gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(), 871 shelf_height - status_size.height()), 872 gfx::Rect(0, 0, 873 shelf_width - status_size.width(), 874 target_bounds->shelf_bounds_in_root.height())); 875 } 876 877 void ShelfLayoutManager::UpdateTargetBoundsForGesture( 878 TargetBounds* target_bounds) const { 879 CHECK_EQ(GESTURE_DRAG_IN_PROGRESS, gesture_drag_status_); 880 bool horizontal = IsHorizontalAlignment(); 881 const gfx::Rect& available_bounds(root_window_->bounds()); 882 int resistance_free_region = 0; 883 884 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN && 885 visibility_state() == SHELF_AUTO_HIDE && 886 auto_hide_state() != SHELF_AUTO_HIDE_SHOWN) { 887 // If the shelf was hidden when the drag started (and the state hasn't 888 // changed since then, e.g. because the tray-menu was shown because of the 889 // drag), then allow the drag some resistance-free region at first to make 890 // sure the shelf sticks with the finger until the shelf is visible. 891 resistance_free_region = kShelfSize - kAutoHideSize; 892 } 893 894 bool resist = SelectValueForShelfAlignment( 895 gesture_drag_amount_ < -resistance_free_region, 896 gesture_drag_amount_ > resistance_free_region, 897 gesture_drag_amount_ < -resistance_free_region, 898 gesture_drag_amount_ > resistance_free_region); 899 900 float translate = 0.f; 901 if (resist) { 902 float diff = fabsf(gesture_drag_amount_) - resistance_free_region; 903 diff = std::min(diff, sqrtf(diff)); 904 if (gesture_drag_amount_ < 0) 905 translate = -resistance_free_region - diff; 906 else 907 translate = resistance_free_region + diff; 908 } else { 909 translate = gesture_drag_amount_; 910 } 911 912 if (horizontal) { 913 // Move and size the shelf with the gesture. 914 int shelf_height = target_bounds->shelf_bounds_in_root.height() - translate; 915 shelf_height = std::max(shelf_height, kAutoHideSize); 916 target_bounds->shelf_bounds_in_root.set_height(shelf_height); 917 if (GetAlignment() == SHELF_ALIGNMENT_BOTTOM) { 918 target_bounds->shelf_bounds_in_root.set_y( 919 available_bounds.bottom() - shelf_height); 920 } 921 922 target_bounds->status_bounds_in_shelf.set_y(0); 923 } else { 924 // Move and size the shelf with the gesture. 925 int shelf_width = target_bounds->shelf_bounds_in_root.width(); 926 bool right_aligned = GetAlignment() == SHELF_ALIGNMENT_RIGHT; 927 if (right_aligned) 928 shelf_width -= translate; 929 else 930 shelf_width += translate; 931 shelf_width = std::max(shelf_width, kAutoHideSize); 932 target_bounds->shelf_bounds_in_root.set_width(shelf_width); 933 if (right_aligned) { 934 target_bounds->shelf_bounds_in_root.set_x( 935 available_bounds.right() - shelf_width); 936 } 937 938 if (right_aligned) 939 target_bounds->status_bounds_in_shelf.set_x(0); 940 else 941 target_bounds->status_bounds_in_shelf.set_x( 942 target_bounds->shelf_bounds_in_root.width() - 943 kShelfSize); 944 } 945 } 946 947 void ShelfLayoutManager::UpdateShelfBackground( 948 BackgroundAnimatorChangeType type) { 949 const ShelfBackgroundType background_type(GetShelfBackgroundType()); 950 shelf_->SetPaintsBackground(background_type, type); 951 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, 952 OnBackgroundUpdated(background_type, type)); 953 } 954 955 ShelfBackgroundType ShelfLayoutManager::GetShelfBackgroundType() const { 956 if (state_.visibility_state != SHELF_AUTO_HIDE && 957 state_.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED) { 958 return SHELF_BACKGROUND_MAXIMIZED; 959 } 960 961 if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS || 962 (!state_.is_screen_locked && window_overlaps_shelf_) || 963 (state_.visibility_state == SHELF_AUTO_HIDE)) { 964 return SHELF_BACKGROUND_OVERLAP; 965 } 966 967 return SHELF_BACKGROUND_DEFAULT; 968 } 969 970 void ShelfLayoutManager::UpdateAutoHideStateNow() { 971 SetState(state_.visibility_state); 972 973 // If the state did not change, the auto hide timer may still be running. 974 StopAutoHideTimer(); 975 } 976 977 void ShelfLayoutManager::StopAutoHideTimer() { 978 auto_hide_timer_.Stop(); 979 mouse_over_shelf_when_auto_hide_timer_started_ = false; 980 } 981 982 gfx::Rect ShelfLayoutManager::GetAutoHideShowShelfRegionInScreen() const { 983 gfx::Rect shelf_bounds_in_screen = shelf_->GetWindowBoundsInScreen(); 984 gfx::Vector2d offset = SelectValueForShelfAlignment( 985 gfx::Vector2d(0, shelf_bounds_in_screen.height()), 986 gfx::Vector2d(-kMaxAutoHideShowShelfRegionSize, 0), 987 gfx::Vector2d(shelf_bounds_in_screen.width(), 0), 988 gfx::Vector2d(0, -kMaxAutoHideShowShelfRegionSize)); 989 990 gfx::Rect show_shelf_region_in_screen = shelf_bounds_in_screen; 991 show_shelf_region_in_screen += offset; 992 if (IsHorizontalAlignment()) 993 show_shelf_region_in_screen.set_height(kMaxAutoHideShowShelfRegionSize); 994 else 995 show_shelf_region_in_screen.set_width(kMaxAutoHideShowShelfRegionSize); 996 997 // TODO: Figure out if we need any special handling when the keyboard is 998 // visible. 999 return show_shelf_region_in_screen; 1000 } 1001 1002 ShelfAutoHideState ShelfLayoutManager::CalculateAutoHideState( 1003 ShelfVisibilityState visibility_state) const { 1004 if (visibility_state != SHELF_AUTO_HIDE || !shelf_) 1005 return SHELF_AUTO_HIDE_HIDDEN; 1006 1007 Shell* shell = Shell::GetInstance(); 1008 if (shell->GetAppListTargetVisibility()) 1009 return SHELF_AUTO_HIDE_SHOWN; 1010 1011 if (shelf_->status_area_widget() && 1012 shelf_->status_area_widget()->ShouldShowShelf()) 1013 return SHELF_AUTO_HIDE_SHOWN; 1014 1015 if (shelf_->shelf() && shelf_->shelf()->IsShowingMenu()) 1016 return SHELF_AUTO_HIDE_SHOWN; 1017 1018 if (shelf_->shelf() && shelf_->shelf()->IsShowingOverflowBubble()) 1019 return SHELF_AUTO_HIDE_SHOWN; 1020 1021 if (shelf_->IsActive() || 1022 (shelf_->status_area_widget() && 1023 shelf_->status_area_widget()->IsActive())) 1024 return SHELF_AUTO_HIDE_SHOWN; 1025 1026 const std::vector<aura::Window*> windows = 1027 ash::MruWindowTracker::BuildWindowList(false); 1028 1029 // Process the window list and check if there are any visible windows. 1030 bool visible_window = false; 1031 for (size_t i = 0; i < windows.size(); ++i) { 1032 if (windows[i] && windows[i]->IsVisible() && 1033 !wm::GetWindowState(windows[i])->IsMinimized() && 1034 root_window_ == windows[i]->GetRootWindow()) { 1035 visible_window = true; 1036 break; 1037 } 1038 } 1039 // If there are no visible windows do not hide the shelf. 1040 if (!visible_window) 1041 return SHELF_AUTO_HIDE_SHOWN; 1042 1043 if (gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS) 1044 return gesture_drag_auto_hide_state_; 1045 1046 // Don't show if the user is dragging the mouse. 1047 if (auto_hide_event_filter_.get() && auto_hide_event_filter_->in_mouse_drag()) 1048 return SHELF_AUTO_HIDE_HIDDEN; 1049 1050 // Ignore the mouse position if mouse events are disabled. 1051 aura::client::CursorClient* cursor_client = aura::client::GetCursorClient( 1052 shelf_->GetNativeWindow()->GetRootWindow()); 1053 if (!cursor_client->IsMouseEventsEnabled()) 1054 return SHELF_AUTO_HIDE_HIDDEN; 1055 1056 gfx::Rect shelf_region = shelf_->GetWindowBoundsInScreen(); 1057 if (shelf_->status_area_widget() && 1058 shelf_->status_area_widget()->IsMessageBubbleShown() && 1059 IsVisible()) { 1060 // Increase the the hit test area to prevent the shelf from disappearing 1061 // when the mouse is over the bubble gap. 1062 ShelfAlignment alignment = GetAlignment(); 1063 shelf_region.Inset(alignment == SHELF_ALIGNMENT_RIGHT ? 1064 -kNotificationBubbleGapHeight : 0, 1065 alignment == SHELF_ALIGNMENT_BOTTOM ? 1066 -kNotificationBubbleGapHeight : 0, 1067 alignment == SHELF_ALIGNMENT_LEFT ? 1068 -kNotificationBubbleGapHeight : 0, 1069 alignment == SHELF_ALIGNMENT_TOP ? 1070 -kNotificationBubbleGapHeight : 0); 1071 } 1072 1073 gfx::Point cursor_position_in_screen = 1074 Shell::GetScreen()->GetCursorScreenPoint(); 1075 if (shelf_region.Contains(cursor_position_in_screen)) 1076 return SHELF_AUTO_HIDE_SHOWN; 1077 1078 // When the shelf is auto hidden and the shelf is on the boundary between two 1079 // displays, it is hard to trigger showing the shelf. For instance, if a 1080 // user's primary display is left of their secondary display, it is hard to 1081 // unautohide a left aligned shelf on the secondary display. 1082 // It is hard because: 1083 // - It is hard to stop the cursor in the shelf "light bar" and not overshoot. 1084 // - The cursor is warped to the other display if the cursor gets to the edge 1085 // of the display. 1086 // Show the shelf if the cursor started on the shelf and the user overshot the 1087 // shelf slightly to make it easier to show the shelf in this situation. We 1088 // do not check |auto_hide_timer_|.IsRunning() because it returns false when 1089 // the timer's task is running. 1090 if ((state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN || 1091 mouse_over_shelf_when_auto_hide_timer_started_) && 1092 GetAutoHideShowShelfRegionInScreen().Contains( 1093 cursor_position_in_screen)) { 1094 return SHELF_AUTO_HIDE_SHOWN; 1095 } 1096 1097 return SHELF_AUTO_HIDE_HIDDEN; 1098 } 1099 1100 bool ShelfLayoutManager::IsShelfWindow(aura::Window* window) { 1101 if (!window) 1102 return false; 1103 return (shelf_ && shelf_->GetNativeWindow()->Contains(window)) || 1104 (shelf_->status_area_widget() && 1105 shelf_->status_area_widget()->GetNativeWindow()->Contains(window)); 1106 } 1107 1108 int ShelfLayoutManager::GetWorkAreaSize(const State& state, int size) const { 1109 if (state.visibility_state == SHELF_VISIBLE) 1110 return size; 1111 if (state.visibility_state == SHELF_AUTO_HIDE) 1112 return kAutoHideSize; 1113 return 0; 1114 } 1115 1116 void ShelfLayoutManager::OnKeyboardBoundsChanging(const gfx::Rect& new_bounds) { 1117 bool keyboard_is_about_to_hide = false; 1118 if (new_bounds.IsEmpty() && !keyboard_bounds_.IsEmpty()) 1119 keyboard_is_about_to_hide = true; 1120 1121 keyboard_bounds_ = new_bounds; 1122 OnWindowResized(); 1123 1124 SessionStateDelegate* session_state_delegate = 1125 Shell::GetInstance()->session_state_delegate(); 1126 1127 // On login screen if keyboard has been just hidden, update bounds just once 1128 // but ignore target_bounds.work_area_insets since shelf overlaps with login 1129 // window. 1130 if (session_state_delegate->IsUserSessionBlocked() && 1131 keyboard_is_about_to_hide) { 1132 Shell::GetInstance()->SetDisplayWorkAreaInsets(root_window_, gfx::Insets()); 1133 } 1134 } 1135 1136 void ShelfLayoutManager::OnDockBoundsChanging( 1137 const gfx::Rect& dock_bounds, 1138 DockedWindowLayoutManagerObserver::Reason reason) { 1139 // Skip shelf layout in case docked notification originates from this class. 1140 if (reason == DISPLAY_INSETS_CHANGED) 1141 return; 1142 if (dock_bounds_ != dock_bounds) { 1143 dock_bounds_ = dock_bounds; 1144 OnWindowResized(); 1145 UpdateVisibilityState(); 1146 UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE); 1147 } 1148 } 1149 1150 void ShelfLayoutManager::OnLockStateEvent(LockStateObserver::EventType event) { 1151 if (event == EVENT_LOCK_ANIMATION_STARTED) { 1152 // Enter the screen locked state. 1153 state_.is_screen_locked = true; 1154 } 1155 } 1156 1157 void ShelfLayoutManager::SessionStateChanged( 1158 SessionStateDelegate::SessionState state) { 1159 TargetBounds target_bounds; 1160 CalculateTargetBounds(state_, &target_bounds); 1161 UpdateBoundsAndOpacity(target_bounds, true, NULL); 1162 UpdateVisibilityState(); 1163 } 1164 1165 } // namespace ash 1166