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