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_widget.h" 6 7 #include "ash/ash_switches.h" 8 #include "ash/focus_cycler.h" 9 #include "ash/root_window_controller.h" 10 #include "ash/session/session_state_delegate.h" 11 #include "ash/shelf/shelf_constants.h" 12 #include "ash/shelf/shelf_delegate.h" 13 #include "ash/shelf/shelf_layout_manager.h" 14 #include "ash/shelf/shelf_model.h" 15 #include "ash/shelf/shelf_navigator.h" 16 #include "ash/shelf/shelf_view.h" 17 #include "ash/shelf/shelf_widget.h" 18 #include "ash/shell.h" 19 #include "ash/shell_window_ids.h" 20 #include "ash/system/tray/system_tray_delegate.h" 21 #include "ash/wm/status_area_layout_manager.h" 22 #include "ash/wm/window_properties.h" 23 #include "ash/wm/workspace_controller.h" 24 #include "grit/ash_resources.h" 25 #include "ui/aura/window.h" 26 #include "ui/aura/window_event_dispatcher.h" 27 #include "ui/aura/window_observer.h" 28 #include "ui/base/resource/resource_bundle.h" 29 #include "ui/compositor/layer.h" 30 #include "ui/compositor/scoped_layer_animation_settings.h" 31 #include "ui/events/event_constants.h" 32 #include "ui/gfx/canvas.h" 33 #include "ui/gfx/image/image.h" 34 #include "ui/gfx/image/image_skia_operations.h" 35 #include "ui/gfx/skbitmap_operations.h" 36 #include "ui/views/accessible_pane_view.h" 37 #include "ui/views/widget/widget.h" 38 #include "ui/views/widget/widget_delegate.h" 39 #include "ui/wm/core/easy_resize_window_targeter.h" 40 #include "ui/wm/public/activation_client.h" 41 42 namespace { 43 // Size of black border at bottom (or side) of shelf. 44 const int kNumBlackPixels = 3; 45 // Alpha to paint dimming image with. 46 const int kDimAlpha = 128; 47 48 // The time to dim and un-dim. 49 const int kTimeToDimMs = 3000; // Slow in dimming. 50 const int kTimeToUnDimMs = 200; // Fast in activating. 51 52 // Class used to slightly dim shelf items when maximized and visible. 53 class DimmerView : public views::View, 54 public views::WidgetDelegate, 55 ash::BackgroundAnimatorDelegate { 56 public: 57 // If |disable_dimming_animations_for_test| is set, all alpha animations will 58 // be performed instantly. 59 DimmerView(ash::ShelfWidget* shelf_widget, 60 bool disable_dimming_animations_for_test); 61 virtual ~DimmerView(); 62 63 // Called by |DimmerEventFilter| when the mouse |hovered| state changes. 64 void SetHovered(bool hovered); 65 66 // Force the dimmer to be undimmed. 67 void ForceUndimming(bool force); 68 69 // views::WidgetDelegate overrides: 70 virtual views::Widget* GetWidget() OVERRIDE { 71 return View::GetWidget(); 72 } 73 virtual const views::Widget* GetWidget() const OVERRIDE { 74 return View::GetWidget(); 75 } 76 77 // ash::BackgroundAnimatorDelegate overrides: 78 virtual void UpdateBackground(int alpha) OVERRIDE { 79 alpha_ = alpha; 80 SchedulePaint(); 81 } 82 83 // views::View overrides: 84 virtual void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE; 85 86 // A function to test the current alpha used. 87 int get_dimming_alpha_for_test() { return alpha_; } 88 89 private: 90 // This class monitors mouse events to see if it is on top of the shelf. 91 class DimmerEventFilter : public ui::EventHandler { 92 public: 93 explicit DimmerEventFilter(DimmerView* owner); 94 virtual ~DimmerEventFilter(); 95 96 // Overridden from ui::EventHandler: 97 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; 98 virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE; 99 100 private: 101 // The owning class. 102 DimmerView* owner_; 103 104 // TRUE if the mouse is inside the shelf. 105 bool mouse_inside_; 106 107 // TRUE if a touch event is inside the shelf. 108 bool touch_inside_; 109 110 DISALLOW_COPY_AND_ASSIGN(DimmerEventFilter); 111 }; 112 113 // The owning shelf. 114 ash::ShelfWidget* shelf_; 115 116 // The alpha to use for covering the shelf. 117 int alpha_; 118 119 // True if the event filter claims that we should not be dimmed. 120 bool is_hovered_; 121 122 // True if someone forces us not to be dimmed (e.g. a menu is open). 123 bool force_hovered_; 124 125 // True if animations should be suppressed for a test. 126 bool disable_dimming_animations_for_test_; 127 128 // The animator for the background transitions. 129 ash::BackgroundAnimator background_animator_; 130 131 // Notification of entering / exiting of the shelf area by mouse. 132 scoped_ptr<DimmerEventFilter> event_filter_; 133 134 DISALLOW_COPY_AND_ASSIGN(DimmerView); 135 }; 136 137 DimmerView::DimmerView(ash::ShelfWidget* shelf_widget, 138 bool disable_dimming_animations_for_test) 139 : shelf_(shelf_widget), 140 alpha_(kDimAlpha), 141 is_hovered_(false), 142 force_hovered_(false), 143 disable_dimming_animations_for_test_(disable_dimming_animations_for_test), 144 background_animator_(this, 0, kDimAlpha) { 145 event_filter_.reset(new DimmerEventFilter(this)); 146 // Make sure it is undimmed at the beginning and then fire off the dimming 147 // animation. 148 background_animator_.SetPaintsBackground(false, 149 ash::BACKGROUND_CHANGE_IMMEDIATE); 150 SetHovered(false); 151 } 152 153 DimmerView::~DimmerView() { 154 } 155 156 void DimmerView::SetHovered(bool hovered) { 157 // Remember the hovered state so that we can correct the state once a 158 // possible force state has disappeared. 159 is_hovered_ = hovered; 160 // Undimm also if we were forced to by e.g. an open menu. 161 hovered |= force_hovered_; 162 background_animator_.SetDuration(hovered ? kTimeToUnDimMs : kTimeToDimMs); 163 background_animator_.SetPaintsBackground(!hovered, 164 disable_dimming_animations_for_test_ ? 165 ash::BACKGROUND_CHANGE_IMMEDIATE : ash::BACKGROUND_CHANGE_ANIMATE); 166 } 167 168 void DimmerView::ForceUndimming(bool force) { 169 bool previous = force_hovered_; 170 force_hovered_ = force; 171 // If the forced change does change the result we apply the change. 172 if (is_hovered_ || force_hovered_ != is_hovered_ || previous) 173 SetHovered(is_hovered_); 174 } 175 176 void DimmerView::OnPaintBackground(gfx::Canvas* canvas) { 177 SkPaint paint; 178 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 179 gfx::ImageSkia shelf_background = 180 *rb.GetImageNamed(IDR_ASH_SHELF_DIMMING).ToImageSkia(); 181 182 if (shelf_->GetAlignment() != ash::SHELF_ALIGNMENT_BOTTOM) { 183 shelf_background = gfx::ImageSkiaOperations::CreateRotatedImage( 184 shelf_background, 185 shelf_->shelf_layout_manager()->SelectValueForShelfAlignment( 186 SkBitmapOperations::ROTATION_90_CW, 187 SkBitmapOperations::ROTATION_90_CW, 188 SkBitmapOperations::ROTATION_270_CW, 189 SkBitmapOperations::ROTATION_180_CW)); 190 } 191 paint.setAlpha(alpha_); 192 canvas->DrawImageInt(shelf_background, 193 0, 194 0, 195 shelf_background.width(), 196 shelf_background.height(), 197 0, 198 0, 199 width(), 200 height(), 201 false, 202 paint); 203 } 204 205 DimmerView::DimmerEventFilter::DimmerEventFilter(DimmerView* owner) 206 : owner_(owner), 207 mouse_inside_(false), 208 touch_inside_(false) { 209 ash::Shell::GetInstance()->AddPreTargetHandler(this); 210 } 211 212 DimmerView::DimmerEventFilter::~DimmerEventFilter() { 213 ash::Shell::GetInstance()->RemovePreTargetHandler(this); 214 } 215 216 void DimmerView::DimmerEventFilter::OnMouseEvent(ui::MouseEvent* event) { 217 if (event->type() != ui::ET_MOUSE_MOVED && 218 event->type() != ui::ET_MOUSE_DRAGGED) 219 return; 220 bool inside = owner_->GetBoundsInScreen().Contains(event->root_location()); 221 if (mouse_inside_ || touch_inside_ != inside || touch_inside_) 222 owner_->SetHovered(inside || touch_inside_); 223 mouse_inside_ = inside; 224 } 225 226 void DimmerView::DimmerEventFilter::OnTouchEvent(ui::TouchEvent* event) { 227 bool touch_inside = false; 228 if (event->type() != ui::ET_TOUCH_RELEASED && 229 event->type() != ui::ET_TOUCH_CANCELLED) 230 touch_inside = owner_->GetBoundsInScreen().Contains(event->root_location()); 231 232 if (mouse_inside_ || touch_inside_ != mouse_inside_ || touch_inside) 233 owner_->SetHovered(mouse_inside_ || touch_inside); 234 touch_inside_ = touch_inside; 235 } 236 237 using ash::ShelfLayoutManager; 238 239 // ShelfWindowTargeter makes it easier to resize windows with the mouse when the 240 // window-edge slightly overlaps with the shelf edge. The targeter also makes it 241 // easier to drag the shelf out with touch while it is hidden. 242 class ShelfWindowTargeter : public wm::EasyResizeWindowTargeter, 243 public ash::ShelfLayoutManagerObserver { 244 public: 245 ShelfWindowTargeter(aura::Window* container, 246 ShelfLayoutManager* shelf) 247 : wm::EasyResizeWindowTargeter(container, gfx::Insets(), gfx::Insets()), 248 shelf_(shelf) { 249 WillChangeVisibilityState(shelf_->visibility_state()); 250 shelf_->AddObserver(this); 251 } 252 253 virtual ~ShelfWindowTargeter() { 254 // |shelf_| may have been destroyed by this time. 255 if (shelf_) 256 shelf_->RemoveObserver(this); 257 } 258 259 private: 260 gfx::Insets GetInsetsForAlignment(int distance, 261 ash::ShelfAlignment alignment) { 262 switch (alignment) { 263 case ash::SHELF_ALIGNMENT_BOTTOM: 264 return gfx::Insets(distance, 0, 0, 0); 265 case ash::SHELF_ALIGNMENT_LEFT: 266 return gfx::Insets(0, 0, 0, distance); 267 case ash::SHELF_ALIGNMENT_RIGHT: 268 return gfx::Insets(0, distance, 0, 0); 269 case ash::SHELF_ALIGNMENT_TOP: 270 return gfx::Insets(0, 0, distance, 0); 271 } 272 NOTREACHED(); 273 return gfx::Insets(); 274 } 275 276 // ash::ShelfLayoutManagerObserver: 277 virtual void WillDeleteShelf() OVERRIDE { 278 shelf_ = NULL; 279 } 280 281 virtual void WillChangeVisibilityState( 282 ash::ShelfVisibilityState new_state) OVERRIDE { 283 gfx::Insets mouse_insets; 284 gfx::Insets touch_insets; 285 if (new_state == ash::SHELF_VISIBLE) { 286 // Let clicks at the very top of the shelf through so windows can be 287 // resized with the bottom-right corner and bottom edge. 288 mouse_insets = GetInsetsForAlignment( 289 ShelfLayoutManager::kWorkspaceAreaVisibleInset, 290 shelf_->GetAlignment()); 291 } else if (new_state == ash::SHELF_AUTO_HIDE) { 292 // Extend the touch hit target out a bit to allow users to drag shelf out 293 // while hidden. 294 touch_insets = GetInsetsForAlignment( 295 -ShelfLayoutManager::kWorkspaceAreaAutoHideInset, 296 shelf_->GetAlignment()); 297 } 298 299 set_mouse_extend(mouse_insets); 300 set_touch_extend(touch_insets); 301 } 302 303 ShelfLayoutManager* shelf_; 304 305 DISALLOW_COPY_AND_ASSIGN(ShelfWindowTargeter); 306 }; 307 308 } // namespace 309 310 namespace ash { 311 312 // The contents view of the Shelf. This view contains ShelfView and 313 // sizes it to the width of the shelf minus the size of the status area. 314 class ShelfWidget::DelegateView : public views::WidgetDelegate, 315 public views::AccessiblePaneView, 316 public BackgroundAnimatorDelegate, 317 public aura::WindowObserver { 318 public: 319 explicit DelegateView(ShelfWidget* shelf); 320 virtual ~DelegateView(); 321 322 void set_focus_cycler(FocusCycler* focus_cycler) { 323 focus_cycler_ = focus_cycler; 324 } 325 FocusCycler* focus_cycler() { return focus_cycler_; } 326 327 ui::Layer* opaque_background() { return &opaque_background_; } 328 329 // Set if the shelf area is dimmed (eg when a window is maximized). 330 void SetDimmed(bool dimmed); 331 bool GetDimmed() const; 332 333 void SetParentLayer(ui::Layer* layer); 334 335 // views::View overrides: 336 virtual void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE; 337 338 // views::WidgetDelegateView overrides: 339 virtual views::Widget* GetWidget() OVERRIDE { 340 return View::GetWidget(); 341 } 342 virtual const views::Widget* GetWidget() const OVERRIDE { 343 return View::GetWidget(); 344 } 345 346 virtual bool CanActivate() const OVERRIDE; 347 virtual void Layout() OVERRIDE; 348 virtual void ReorderChildLayers(ui::Layer* parent_layer) OVERRIDE; 349 // This will be called when the parent local bounds change. 350 virtual void OnBoundsChanged(const gfx::Rect& old_bounds) OVERRIDE; 351 352 // aura::WindowObserver overrides: 353 // This will be called when the shelf itself changes its absolute position. 354 // Since the |dimmer_| panel needs to be placed in screen coordinates it needs 355 // to be repositioned. The difference to the OnBoundsChanged call above is 356 // that this gets also triggered when the shelf only moves. 357 virtual void OnWindowBoundsChanged(aura::Window* window, 358 const gfx::Rect& old_bounds, 359 const gfx::Rect& new_bounds) OVERRIDE; 360 361 // BackgroundAnimatorDelegate overrides: 362 virtual void UpdateBackground(int alpha) OVERRIDE; 363 364 // Force the shelf to be presented in an undimmed state. 365 void ForceUndimming(bool force); 366 367 // A function to test the current alpha used by the dimming bar. If there is 368 // no dimmer active, the function will return -1. 369 int GetDimmingAlphaForTest(); 370 371 // A function to test the bounds of the dimming bar. Returns gfx::Rect() if 372 // the dimmer is inactive. 373 gfx::Rect GetDimmerBoundsForTest(); 374 375 // Disable dimming animations for running tests. This needs to be called 376 // prior to the creation of of the |dimmer_|. 377 void disable_dimming_animations_for_test() { 378 disable_dimming_animations_for_test_ = true; 379 } 380 381 private: 382 ShelfWidget* shelf_; 383 scoped_ptr<views::Widget> dimmer_; 384 FocusCycler* focus_cycler_; 385 int alpha_; 386 ui::Layer opaque_background_; 387 388 // The view which does the dimming. 389 DimmerView* dimmer_view_; 390 391 // True if dimming animations should be turned off. 392 bool disable_dimming_animations_for_test_; 393 394 DISALLOW_COPY_AND_ASSIGN(DelegateView); 395 }; 396 397 ShelfWidget::DelegateView::DelegateView(ShelfWidget* shelf) 398 : shelf_(shelf), 399 focus_cycler_(NULL), 400 alpha_(0), 401 opaque_background_(ui::LAYER_SOLID_COLOR), 402 dimmer_view_(NULL), 403 disable_dimming_animations_for_test_(false) { 404 set_allow_deactivate_on_esc(true); 405 opaque_background_.SetColor(SK_ColorBLACK); 406 opaque_background_.SetBounds(GetLocalBounds()); 407 opaque_background_.SetOpacity(0.0f); 408 } 409 410 ShelfWidget::DelegateView::~DelegateView() { 411 // Make sure that the dimmer goes away since it might have set an observer. 412 SetDimmed(false); 413 } 414 415 void ShelfWidget::DelegateView::SetDimmed(bool value) { 416 if (value == (dimmer_.get() != NULL)) 417 return; 418 419 if (value) { 420 dimmer_.reset(new views::Widget); 421 views::Widget::InitParams params( 422 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); 423 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 424 params.activatable = views::Widget::InitParams::ACTIVATABLE_NO; 425 params.accept_events = false; 426 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 427 params.parent = shelf_->GetNativeView(); 428 dimmer_->Init(params); 429 dimmer_->GetNativeWindow()->SetName("ShelfDimmer"); 430 dimmer_->SetBounds(shelf_->GetWindowBoundsInScreen()); 431 // The shelf should not take focus when it is initially shown. 432 dimmer_->set_focus_on_creation(false); 433 dimmer_view_ = new DimmerView(shelf_, disable_dimming_animations_for_test_); 434 dimmer_->SetContentsView(dimmer_view_); 435 dimmer_->GetNativeView()->SetName("ShelfDimmerView"); 436 dimmer_->Show(); 437 shelf_->GetNativeView()->AddObserver(this); 438 } else { 439 // Some unit tests will come here with a destroyed window. 440 if (shelf_->GetNativeView()) 441 shelf_->GetNativeView()->RemoveObserver(this); 442 dimmer_view_ = NULL; 443 dimmer_.reset(NULL); 444 } 445 } 446 447 bool ShelfWidget::DelegateView::GetDimmed() const { 448 return dimmer_.get() && dimmer_->IsVisible(); 449 } 450 451 void ShelfWidget::DelegateView::SetParentLayer(ui::Layer* layer) { 452 layer->Add(&opaque_background_); 453 ReorderLayers(); 454 } 455 456 void ShelfWidget::DelegateView::OnPaintBackground(gfx::Canvas* canvas) { 457 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 458 gfx::ImageSkia shelf_background = 459 *rb.GetImageSkiaNamed(IDR_ASH_SHELF_BACKGROUND); 460 if (SHELF_ALIGNMENT_BOTTOM != shelf_->GetAlignment()) 461 shelf_background = gfx::ImageSkiaOperations::CreateRotatedImage( 462 shelf_background, 463 shelf_->shelf_layout_manager()->SelectValueForShelfAlignment( 464 SkBitmapOperations::ROTATION_90_CW, 465 SkBitmapOperations::ROTATION_90_CW, 466 SkBitmapOperations::ROTATION_270_CW, 467 SkBitmapOperations::ROTATION_180_CW)); 468 const gfx::Rect dock_bounds(shelf_->shelf_layout_manager()->dock_bounds()); 469 SkPaint paint; 470 paint.setAlpha(alpha_); 471 canvas->DrawImageInt(shelf_background, 472 0, 473 0, 474 shelf_background.width(), 475 shelf_background.height(), 476 (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() && 477 dock_bounds.x() == 0 && dock_bounds.width() > 0) 478 ? dock_bounds.width() 479 : 0, 480 0, 481 SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() 482 ? width() - dock_bounds.width() 483 : width(), 484 height(), 485 false, 486 paint); 487 if (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() && 488 dock_bounds.width() > 0) { 489 // The part of the shelf background that is in the corner below the docked 490 // windows close to the work area is an arched gradient that blends 491 // vertically oriented docked background and horizontal shelf. 492 gfx::ImageSkia shelf_corner = *rb.GetImageSkiaNamed(IDR_ASH_SHELF_CORNER); 493 if (dock_bounds.x() == 0) { 494 shelf_corner = gfx::ImageSkiaOperations::CreateRotatedImage( 495 shelf_corner, SkBitmapOperations::ROTATION_90_CW); 496 } 497 canvas->DrawImageInt( 498 shelf_corner, 499 0, 500 0, 501 shelf_corner.width(), 502 shelf_corner.height(), 503 dock_bounds.x() > 0 ? dock_bounds.x() : dock_bounds.width() - height(), 504 0, 505 height(), 506 height(), 507 false, 508 paint); 509 // The part of the shelf background that is just below the docked windows 510 // is drawn using the last (lowest) 1-pixel tall strip of the image asset. 511 // This avoids showing the border 3D shadow between the shelf and the dock. 512 canvas->DrawImageInt(shelf_background, 513 0, 514 shelf_background.height() - 1, 515 shelf_background.width(), 516 1, 517 dock_bounds.x() > 0 ? dock_bounds.x() + height() : 0, 518 0, 519 dock_bounds.width() - height(), 520 height(), 521 false, 522 paint); 523 } 524 gfx::Rect black_rect = 525 shelf_->shelf_layout_manager()->SelectValueForShelfAlignment( 526 gfx::Rect(0, height() - kNumBlackPixels, width(), kNumBlackPixels), 527 gfx::Rect(0, 0, kNumBlackPixels, height()), 528 gfx::Rect(width() - kNumBlackPixels, 0, kNumBlackPixels, height()), 529 gfx::Rect(0, 0, width(), kNumBlackPixels)); 530 canvas->FillRect(black_rect, SK_ColorBLACK); 531 } 532 533 bool ShelfWidget::DelegateView::CanActivate() const { 534 // Allow to activate as fallback. 535 if (shelf_->activating_as_fallback_) 536 return true; 537 // Allow to activate from the focus cycler. 538 if (focus_cycler_ && focus_cycler_->widget_activating() == GetWidget()) 539 return true; 540 // Disallow activating in other cases, especially when using mouse. 541 return false; 542 } 543 544 void ShelfWidget::DelegateView::Layout() { 545 for(int i = 0; i < child_count(); ++i) { 546 if (shelf_->shelf_layout_manager()->IsHorizontalAlignment()) { 547 child_at(i)->SetBounds(child_at(i)->x(), child_at(i)->y(), 548 child_at(i)->width(), height()); 549 } else { 550 child_at(i)->SetBounds(child_at(i)->x(), child_at(i)->y(), 551 width(), child_at(i)->height()); 552 } 553 } 554 } 555 556 void ShelfWidget::DelegateView::ReorderChildLayers(ui::Layer* parent_layer) { 557 views::View::ReorderChildLayers(parent_layer); 558 parent_layer->StackAtBottom(&opaque_background_); 559 } 560 561 void ShelfWidget::DelegateView::OnBoundsChanged(const gfx::Rect& old_bounds) { 562 opaque_background_.SetBounds(GetLocalBounds()); 563 if (dimmer_) 564 dimmer_->SetBounds(GetBoundsInScreen()); 565 } 566 567 void ShelfWidget::DelegateView::OnWindowBoundsChanged( 568 aura::Window* window, 569 const gfx::Rect& old_bounds, 570 const gfx::Rect& new_bounds) { 571 // Coming here the shelf got repositioned and since the |dimmer_| is placed 572 // in screen coordinates and not relative to the parent it needs to be 573 // repositioned accordingly. 574 dimmer_->SetBounds(GetBoundsInScreen()); 575 } 576 577 void ShelfWidget::DelegateView::ForceUndimming(bool force) { 578 if (GetDimmed()) 579 dimmer_view_->ForceUndimming(force); 580 } 581 582 int ShelfWidget::DelegateView::GetDimmingAlphaForTest() { 583 if (GetDimmed()) 584 return dimmer_view_->get_dimming_alpha_for_test(); 585 return -1; 586 } 587 588 gfx::Rect ShelfWidget::DelegateView::GetDimmerBoundsForTest() { 589 if (GetDimmed()) 590 return dimmer_view_->GetBoundsInScreen(); 591 return gfx::Rect(); 592 } 593 594 void ShelfWidget::DelegateView::UpdateBackground(int alpha) { 595 alpha_ = alpha; 596 SchedulePaint(); 597 } 598 599 ShelfWidget::ShelfWidget(aura::Window* shelf_container, 600 aura::Window* status_container, 601 WorkspaceController* workspace_controller) 602 : delegate_view_(new DelegateView(this)), 603 background_animator_(delegate_view_, 0, kShelfBackgroundAlpha), 604 activating_as_fallback_(false), 605 window_container_(shelf_container) { 606 views::Widget::InitParams params( 607 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); 608 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 609 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 610 params.parent = shelf_container; 611 params.delegate = delegate_view_; 612 Init(params); 613 614 // The shelf should not take focus when initially shown. 615 set_focus_on_creation(false); 616 SetContentsView(delegate_view_); 617 delegate_view_->SetParentLayer(GetLayer()); 618 619 status_area_widget_ = new StatusAreaWidget(status_container); 620 status_area_widget_->CreateTrayViews(); 621 if (Shell::GetInstance()->session_state_delegate()-> 622 IsActiveUserSessionStarted()) { 623 status_area_widget_->Show(); 624 } 625 Shell::GetInstance()->focus_cycler()->AddWidget(status_area_widget_); 626 627 shelf_layout_manager_ = new ShelfLayoutManager(this); 628 shelf_layout_manager_->AddObserver(this); 629 shelf_container->SetLayoutManager(shelf_layout_manager_); 630 shelf_layout_manager_->set_workspace_controller(workspace_controller); 631 workspace_controller->SetShelf(shelf_layout_manager_); 632 633 status_container->SetLayoutManager(new StatusAreaLayoutManager(this)); 634 635 shelf_container->SetEventTargeter(scoped_ptr<ui::EventTargeter>(new 636 ShelfWindowTargeter(shelf_container, shelf_layout_manager_))); 637 status_container->SetEventTargeter(scoped_ptr<ui::EventTargeter>(new 638 ShelfWindowTargeter(status_container, shelf_layout_manager_))); 639 640 views::Widget::AddObserver(this); 641 } 642 643 ShelfWidget::~ShelfWidget() { 644 RemoveObserver(this); 645 } 646 647 void ShelfWidget::SetPaintsBackground( 648 ShelfBackgroundType background_type, 649 BackgroundAnimatorChangeType change_type) { 650 ui::Layer* opaque_background = delegate_view_->opaque_background(); 651 float target_opacity = 652 (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f; 653 scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation; 654 if (change_type != BACKGROUND_CHANGE_IMMEDIATE) { 655 opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings( 656 opaque_background->GetAnimator())); 657 opaque_background_animation->SetTransitionDuration( 658 base::TimeDelta::FromMilliseconds(kTimeToSwitchBackgroundMs)); 659 } 660 opaque_background->SetOpacity(target_opacity); 661 662 // TODO(mukai): use ui::Layer on both opaque_background and normal background 663 // retire background_animator_ at all. It would be simpler. 664 // See also DockedBackgroundWidget::SetPaintsBackground. 665 background_animator_.SetPaintsBackground( 666 background_type != SHELF_BACKGROUND_DEFAULT, 667 change_type); 668 delegate_view_->SchedulePaint(); 669 } 670 671 ShelfBackgroundType ShelfWidget::GetBackgroundType() const { 672 if (delegate_view_->opaque_background()->GetTargetOpacity() == 1.0f) 673 return SHELF_BACKGROUND_MAXIMIZED; 674 if (background_animator_.paints_background()) 675 return SHELF_BACKGROUND_OVERLAP; 676 677 return SHELF_BACKGROUND_DEFAULT; 678 } 679 680 // static 681 bool ShelfWidget::ShelfAlignmentAllowed() { 682 user::LoginStatus login_status = 683 Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus(); 684 685 switch (login_status) { 686 case user::LOGGED_IN_USER: 687 case user::LOGGED_IN_OWNER: 688 return true; 689 case user::LOGGED_IN_LOCKED: 690 case user::LOGGED_IN_PUBLIC: 691 case user::LOGGED_IN_LOCALLY_MANAGED: 692 case user::LOGGED_IN_GUEST: 693 case user::LOGGED_IN_RETAIL_MODE: 694 case user::LOGGED_IN_KIOSK_APP: 695 case user::LOGGED_IN_NONE: 696 return false; 697 } 698 699 DCHECK(false); 700 return false; 701 } 702 703 ShelfAlignment ShelfWidget::GetAlignment() const { 704 return shelf_layout_manager_->GetAlignment(); 705 } 706 707 void ShelfWidget::SetAlignment(ShelfAlignment alignment) { 708 if (shelf_) 709 shelf_->SetAlignment(alignment); 710 status_area_widget_->SetShelfAlignment(alignment); 711 delegate_view_->SchedulePaint(); 712 } 713 714 void ShelfWidget::SetDimsShelf(bool dimming) { 715 delegate_view_->SetDimmed(dimming); 716 // Repaint all children, allowing updates to reflect dimmed state eg: 717 // status area background, app list button and overflow button. 718 if (shelf_) 719 shelf_->SchedulePaint(); 720 status_area_widget_->SchedulePaint(); 721 } 722 723 bool ShelfWidget::GetDimsShelf() const { 724 return delegate_view_->GetDimmed(); 725 } 726 727 void ShelfWidget::CreateShelf() { 728 if (shelf_) 729 return; 730 731 Shell* shell = Shell::GetInstance(); 732 // This needs to be called before shelf_model(). 733 ShelfDelegate* shelf_delegate = shell->GetShelfDelegate(); 734 if (!shelf_delegate) 735 return; // Not ready to create Shelf. 736 737 shelf_.reset( 738 new Shelf(shell->shelf_model(), shell->GetShelfDelegate(), this)); 739 SetFocusCycler(shell->focus_cycler()); 740 741 // Inform the root window controller. 742 RootWindowController::ForWindow(window_container_)->OnShelfCreated(); 743 744 shelf_->SetVisible( 745 shell->session_state_delegate()->IsActiveUserSessionStarted()); 746 shelf_layout_manager_->LayoutShelf(); 747 Show(); 748 } 749 750 bool ShelfWidget::IsShelfVisible() const { 751 return shelf_.get() && shelf_->IsVisible(); 752 } 753 754 void ShelfWidget::SetShelfVisibility(bool visible) { 755 if (shelf_) 756 shelf_->SetVisible(visible); 757 } 758 759 void ShelfWidget::SetFocusCycler(FocusCycler* focus_cycler) { 760 delegate_view_->set_focus_cycler(focus_cycler); 761 if (focus_cycler) 762 focus_cycler->AddWidget(this); 763 } 764 765 FocusCycler* ShelfWidget::GetFocusCycler() { 766 return delegate_view_->focus_cycler(); 767 } 768 769 void ShelfWidget::ShutdownStatusAreaWidget() { 770 if (status_area_widget_) 771 status_area_widget_->Shutdown(); 772 status_area_widget_ = NULL; 773 } 774 775 void ShelfWidget::ForceUndimming(bool force) { 776 delegate_view_->ForceUndimming(force); 777 } 778 779 void ShelfWidget::OnWidgetActivationChanged(views::Widget* widget, 780 bool active) { 781 activating_as_fallback_ = false; 782 if (active) 783 delegate_view_->SetPaneFocusAndFocusDefault(); 784 else 785 delegate_view_->GetFocusManager()->ClearFocus(); 786 } 787 788 int ShelfWidget::GetDimmingAlphaForTest() { 789 if (delegate_view_) 790 return delegate_view_->GetDimmingAlphaForTest(); 791 return -1; 792 } 793 794 gfx::Rect ShelfWidget::GetDimmerBoundsForTest() { 795 if (delegate_view_) 796 return delegate_view_->GetDimmerBoundsForTest(); 797 return gfx::Rect(); 798 } 799 800 void ShelfWidget::DisableDimmingAnimationsForTest() { 801 DCHECK(delegate_view_); 802 return delegate_view_->disable_dimming_animations_for_test(); 803 } 804 805 void ShelfWidget::WillDeleteShelf() { 806 shelf_layout_manager_->RemoveObserver(this); 807 shelf_layout_manager_ = NULL; 808 } 809 810 } // namespace ash 811