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