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