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