Home | History | Annotate | Download | only in shelf
      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   ui::Layer* opaque_foreground() { return &opaque_foreground_; }
    329 
    330   // Set if the shelf area is dimmed (eg when a window is maximized).
    331   void SetDimmed(bool dimmed);
    332   bool GetDimmed() const;
    333 
    334   void SetParentLayer(ui::Layer* layer);
    335 
    336   // views::View overrides:
    337   virtual void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE;
    338 
    339   // views::WidgetDelegateView overrides:
    340   virtual views::Widget* GetWidget() OVERRIDE {
    341     return View::GetWidget();
    342   }
    343   virtual const views::Widget* GetWidget() const OVERRIDE {
    344     return View::GetWidget();
    345   }
    346 
    347   virtual bool CanActivate() const OVERRIDE;
    348   virtual void Layout() OVERRIDE;
    349   virtual void ReorderChildLayers(ui::Layer* parent_layer) OVERRIDE;
    350   // This will be called when the parent local bounds change.
    351   virtual void OnBoundsChanged(const gfx::Rect& old_bounds) OVERRIDE;
    352 
    353   // aura::WindowObserver overrides:
    354   // This will be called when the shelf itself changes its absolute position.
    355   // Since the |dimmer_| panel needs to be placed in screen coordinates it needs
    356   // to be repositioned. The difference to the OnBoundsChanged call above is
    357   // that this gets also triggered when the shelf only moves.
    358   virtual void OnWindowBoundsChanged(aura::Window* window,
    359                                      const gfx::Rect& old_bounds,
    360                                      const gfx::Rect& new_bounds) OVERRIDE;
    361 
    362   // BackgroundAnimatorDelegate overrides:
    363   virtual void UpdateBackground(int alpha) OVERRIDE;
    364 
    365   // Force the shelf to be presented in an undimmed state.
    366   void ForceUndimming(bool force);
    367 
    368   // A function to test the current alpha used by the dimming bar. If there is
    369   // no dimmer active, the function will return -1.
    370   int GetDimmingAlphaForTest();
    371 
    372   // A function to test the bounds of the dimming bar. Returns gfx::Rect() if
    373   // the dimmer is inactive.
    374   gfx::Rect GetDimmerBoundsForTest();
    375 
    376   // Disable dimming animations for running tests. This needs to be called
    377   // prior to the creation of of the |dimmer_|.
    378   void disable_dimming_animations_for_test() {
    379     disable_dimming_animations_for_test_ = true;
    380   }
    381 
    382  private:
    383   ShelfWidget* shelf_;
    384   scoped_ptr<views::Widget> dimmer_;
    385   FocusCycler* focus_cycler_;
    386   int alpha_;
    387   // A black background layer which is shown when a maximized window is visible.
    388   ui::Layer opaque_background_;
    389   // A black foreground layer which is shown while transitioning between users.
    390   // Note: Since the back- and foreground layers have different functions they
    391   // can be used simultaneously - so no repurposing possible.
    392   ui::Layer opaque_foreground_;
    393 
    394   // The view which does the dimming.
    395   DimmerView* dimmer_view_;
    396 
    397   // True if dimming animations should be turned off.
    398   bool disable_dimming_animations_for_test_;
    399 
    400   DISALLOW_COPY_AND_ASSIGN(DelegateView);
    401 };
    402 
    403 ShelfWidget::DelegateView::DelegateView(ShelfWidget* shelf)
    404     : shelf_(shelf),
    405       focus_cycler_(NULL),
    406       alpha_(0),
    407       opaque_background_(ui::LAYER_SOLID_COLOR),
    408       opaque_foreground_(ui::LAYER_SOLID_COLOR),
    409       dimmer_view_(NULL),
    410       disable_dimming_animations_for_test_(false) {
    411   set_allow_deactivate_on_esc(true);
    412   opaque_background_.SetColor(SK_ColorBLACK);
    413   opaque_background_.SetBounds(GetLocalBounds());
    414   opaque_background_.SetOpacity(0.0f);
    415   opaque_foreground_.SetColor(SK_ColorBLACK);
    416   opaque_foreground_.SetBounds(GetLocalBounds());
    417   opaque_foreground_.SetOpacity(0.0f);
    418 }
    419 
    420 ShelfWidget::DelegateView::~DelegateView() {
    421   // Make sure that the dimmer goes away since it might have set an observer.
    422   SetDimmed(false);
    423 }
    424 
    425 void ShelfWidget::DelegateView::SetDimmed(bool value) {
    426   if (value == (dimmer_.get() != NULL))
    427     return;
    428 
    429   if (value) {
    430     dimmer_.reset(new views::Widget);
    431     views::Widget::InitParams params(
    432         views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    433     params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
    434     params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
    435     params.accept_events = false;
    436     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    437     params.parent = shelf_->GetNativeView();
    438     dimmer_->Init(params);
    439     dimmer_->GetNativeWindow()->SetName("ShelfDimmer");
    440     dimmer_->SetBounds(shelf_->GetWindowBoundsInScreen());
    441     // The shelf should not take focus when it is initially shown.
    442     dimmer_->set_focus_on_creation(false);
    443     dimmer_view_ = new DimmerView(shelf_, disable_dimming_animations_for_test_);
    444     dimmer_->SetContentsView(dimmer_view_);
    445     dimmer_->GetNativeView()->SetName("ShelfDimmerView");
    446     dimmer_->Show();
    447     shelf_->GetNativeView()->AddObserver(this);
    448   } else {
    449     // Some unit tests will come here with a destroyed window.
    450     if (shelf_->GetNativeView())
    451       shelf_->GetNativeView()->RemoveObserver(this);
    452     dimmer_view_ = NULL;
    453     dimmer_.reset(NULL);
    454   }
    455 }
    456 
    457 bool ShelfWidget::DelegateView::GetDimmed() const {
    458   return dimmer_.get() && dimmer_->IsVisible();
    459 }
    460 
    461 void ShelfWidget::DelegateView::SetParentLayer(ui::Layer* layer) {
    462   layer->Add(&opaque_background_);
    463   layer->Add(&opaque_foreground_);
    464   ReorderLayers();
    465 }
    466 
    467 void ShelfWidget::DelegateView::OnPaintBackground(gfx::Canvas* canvas) {
    468   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    469   gfx::ImageSkia shelf_background =
    470       *rb.GetImageSkiaNamed(IDR_ASH_SHELF_BACKGROUND);
    471   if (SHELF_ALIGNMENT_BOTTOM != shelf_->GetAlignment())
    472     shelf_background = gfx::ImageSkiaOperations::CreateRotatedImage(
    473         shelf_background,
    474         shelf_->shelf_layout_manager()->SelectValueForShelfAlignment(
    475             SkBitmapOperations::ROTATION_90_CW,
    476             SkBitmapOperations::ROTATION_90_CW,
    477             SkBitmapOperations::ROTATION_270_CW,
    478             SkBitmapOperations::ROTATION_180_CW));
    479   const gfx::Rect dock_bounds(shelf_->shelf_layout_manager()->dock_bounds());
    480   SkPaint paint;
    481   paint.setAlpha(alpha_);
    482   canvas->DrawImageInt(shelf_background,
    483                        0,
    484                        0,
    485                        shelf_background.width(),
    486                        shelf_background.height(),
    487                        (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() &&
    488                         dock_bounds.x() == 0 && dock_bounds.width() > 0)
    489                            ? dock_bounds.width()
    490                            : 0,
    491                        0,
    492                        SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment()
    493                            ? width() - dock_bounds.width()
    494                            : width(),
    495                        height(),
    496                        false,
    497                        paint);
    498   if (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() &&
    499       dock_bounds.width() > 0) {
    500     // The part of the shelf background that is in the corner below the docked
    501     // windows close to the work area is an arched gradient that blends
    502     // vertically oriented docked background and horizontal shelf.
    503     gfx::ImageSkia shelf_corner = *rb.GetImageSkiaNamed(IDR_ASH_SHELF_CORNER);
    504     if (dock_bounds.x() == 0) {
    505       shelf_corner = gfx::ImageSkiaOperations::CreateRotatedImage(
    506           shelf_corner, SkBitmapOperations::ROTATION_90_CW);
    507     }
    508     canvas->DrawImageInt(
    509         shelf_corner,
    510         0,
    511         0,
    512         shelf_corner.width(),
    513         shelf_corner.height(),
    514         dock_bounds.x() > 0 ? dock_bounds.x() : dock_bounds.width() - height(),
    515         0,
    516         height(),
    517         height(),
    518         false,
    519         paint);
    520     // The part of the shelf background that is just below the docked windows
    521     // is drawn using the last (lowest) 1-pixel tall strip of the image asset.
    522     // This avoids showing the border 3D shadow between the shelf and the dock.
    523     canvas->DrawImageInt(shelf_background,
    524                          0,
    525                          shelf_background.height() - 1,
    526                          shelf_background.width(),
    527                          1,
    528                          dock_bounds.x() > 0 ? dock_bounds.x() + height() : 0,
    529                          0,
    530                          dock_bounds.width() - height(),
    531                          height(),
    532                          false,
    533                          paint);
    534   }
    535   gfx::Rect black_rect =
    536       shelf_->shelf_layout_manager()->SelectValueForShelfAlignment(
    537           gfx::Rect(0, height() - kNumBlackPixels, width(), kNumBlackPixels),
    538           gfx::Rect(0, 0, kNumBlackPixels, height()),
    539           gfx::Rect(width() - kNumBlackPixels, 0, kNumBlackPixels, height()),
    540           gfx::Rect(0, 0, width(), kNumBlackPixels));
    541   canvas->FillRect(black_rect, SK_ColorBLACK);
    542 }
    543 
    544 bool ShelfWidget::DelegateView::CanActivate() const {
    545   // Allow to activate as fallback.
    546   if (shelf_->activating_as_fallback_)
    547     return true;
    548   // Allow to activate from the focus cycler.
    549   if (focus_cycler_ && focus_cycler_->widget_activating() == GetWidget())
    550     return true;
    551   // Disallow activating in other cases, especially when using mouse.
    552   return false;
    553 }
    554 
    555 void ShelfWidget::DelegateView::Layout() {
    556   for(int i = 0; i < child_count(); ++i) {
    557     if (shelf_->shelf_layout_manager()->IsHorizontalAlignment()) {
    558       child_at(i)->SetBounds(child_at(i)->x(), child_at(i)->y(),
    559                              child_at(i)->width(), height());
    560     } else {
    561       child_at(i)->SetBounds(child_at(i)->x(), child_at(i)->y(),
    562                              width(), child_at(i)->height());
    563     }
    564   }
    565 }
    566 
    567 void ShelfWidget::DelegateView::ReorderChildLayers(ui::Layer* parent_layer) {
    568   views::View::ReorderChildLayers(parent_layer);
    569   parent_layer->StackAtBottom(&opaque_background_);
    570   parent_layer->StackAtTop(&opaque_foreground_);
    571 }
    572 
    573 void ShelfWidget::DelegateView::OnBoundsChanged(const gfx::Rect& old_bounds) {
    574   opaque_background_.SetBounds(GetLocalBounds());
    575   opaque_foreground_.SetBounds(GetLocalBounds());
    576   if (dimmer_)
    577     dimmer_->SetBounds(GetBoundsInScreen());
    578 }
    579 
    580 void ShelfWidget::DelegateView::OnWindowBoundsChanged(
    581     aura::Window* window,
    582     const gfx::Rect& old_bounds,
    583     const gfx::Rect& new_bounds) {
    584   // Coming here the shelf got repositioned and since the |dimmer_| is placed
    585   // in screen coordinates and not relative to the parent it needs to be
    586   // repositioned accordingly.
    587   dimmer_->SetBounds(GetBoundsInScreen());
    588 }
    589 
    590 void ShelfWidget::DelegateView::ForceUndimming(bool force) {
    591   if (GetDimmed())
    592     dimmer_view_->ForceUndimming(force);
    593 }
    594 
    595 int ShelfWidget::DelegateView::GetDimmingAlphaForTest() {
    596   if (GetDimmed())
    597     return dimmer_view_->get_dimming_alpha_for_test();
    598   return -1;
    599 }
    600 
    601 gfx::Rect ShelfWidget::DelegateView::GetDimmerBoundsForTest() {
    602   if (GetDimmed())
    603     return dimmer_view_->GetBoundsInScreen();
    604   return gfx::Rect();
    605 }
    606 
    607 void ShelfWidget::DelegateView::UpdateBackground(int alpha) {
    608   alpha_ = alpha;
    609   SchedulePaint();
    610 }
    611 
    612 ShelfWidget::ShelfWidget(aura::Window* shelf_container,
    613                          aura::Window* status_container,
    614                          WorkspaceController* workspace_controller)
    615     : delegate_view_(new DelegateView(this)),
    616       background_animator_(delegate_view_, 0, kShelfBackgroundAlpha),
    617       activating_as_fallback_(false),
    618       window_container_(shelf_container) {
    619   views::Widget::InitParams params(
    620       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    621   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
    622   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    623   params.parent = shelf_container;
    624   params.delegate = delegate_view_;
    625   Init(params);
    626 
    627   // The shelf should not take focus when initially shown.
    628   set_focus_on_creation(false);
    629   SetContentsView(delegate_view_);
    630   delegate_view_->SetParentLayer(GetLayer());
    631 
    632   status_area_widget_ = new StatusAreaWidget(status_container);
    633   status_area_widget_->CreateTrayViews();
    634   if (Shell::GetInstance()->session_state_delegate()->
    635           IsActiveUserSessionStarted()) {
    636     status_area_widget_->Show();
    637   }
    638   Shell::GetInstance()->focus_cycler()->AddWidget(status_area_widget_);
    639 
    640   shelf_layout_manager_ = new ShelfLayoutManager(this);
    641   shelf_layout_manager_->AddObserver(this);
    642   shelf_container->SetLayoutManager(shelf_layout_manager_);
    643   shelf_layout_manager_->set_workspace_controller(workspace_controller);
    644   workspace_controller->SetShelf(shelf_layout_manager_);
    645 
    646   status_container->SetLayoutManager(
    647       new StatusAreaLayoutManager(status_container, this));
    648 
    649   shelf_container->SetEventTargeter(scoped_ptr<ui::EventTargeter>(new
    650       ShelfWindowTargeter(shelf_container, shelf_layout_manager_)));
    651   status_container->SetEventTargeter(scoped_ptr<ui::EventTargeter>(new
    652       ShelfWindowTargeter(status_container, shelf_layout_manager_)));
    653 
    654   views::Widget::AddObserver(this);
    655 }
    656 
    657 ShelfWidget::~ShelfWidget() {
    658   RemoveObserver(this);
    659 }
    660 
    661 void ShelfWidget::SetPaintsBackground(
    662     ShelfBackgroundType background_type,
    663     BackgroundAnimatorChangeType change_type) {
    664   ui::Layer* opaque_background = delegate_view_->opaque_background();
    665   float target_opacity =
    666       (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f;
    667   scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation;
    668   if (change_type != BACKGROUND_CHANGE_IMMEDIATE) {
    669     opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings(
    670         opaque_background->GetAnimator()));
    671     opaque_background_animation->SetTransitionDuration(
    672         base::TimeDelta::FromMilliseconds(kTimeToSwitchBackgroundMs));
    673   }
    674   opaque_background->SetOpacity(target_opacity);
    675 
    676   // TODO(mukai): use ui::Layer on both opaque_background and normal background
    677   // retire background_animator_ at all. It would be simpler.
    678   // See also DockedBackgroundWidget::SetPaintsBackground.
    679   background_animator_.SetPaintsBackground(
    680       background_type != SHELF_BACKGROUND_DEFAULT,
    681       change_type);
    682   delegate_view_->SchedulePaint();
    683 }
    684 
    685 ShelfBackgroundType ShelfWidget::GetBackgroundType() const {
    686   if (delegate_view_->opaque_background()->GetTargetOpacity() == 1.0f)
    687     return SHELF_BACKGROUND_MAXIMIZED;
    688   if (background_animator_.paints_background())
    689     return SHELF_BACKGROUND_OVERLAP;
    690 
    691   return SHELF_BACKGROUND_DEFAULT;
    692 }
    693 
    694 void ShelfWidget::HideShelfBehindBlackBar(bool hide, int animation_time_ms) {
    695   if (IsShelfHiddenBehindBlackBar() == hide)
    696     return;
    697 
    698   ui::Layer* opaque_foreground = delegate_view_->opaque_foreground();
    699   float target_opacity = hide ? 1.0f : 0.0f;
    700   scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_foreground_animation;
    701   opaque_foreground_animation.reset(new ui::ScopedLayerAnimationSettings(
    702       opaque_foreground->GetAnimator()));
    703   opaque_foreground_animation->SetTransitionDuration(
    704       base::TimeDelta::FromMilliseconds(animation_time_ms));
    705   opaque_foreground_animation->SetPreemptionStrategy(
    706       ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
    707 
    708   opaque_foreground->SetOpacity(target_opacity);
    709 }
    710 
    711 bool ShelfWidget::IsShelfHiddenBehindBlackBar() const {
    712   return delegate_view_->opaque_foreground()->GetTargetOpacity() != 0.0f;
    713 }
    714 
    715 // static
    716 bool ShelfWidget::ShelfAlignmentAllowed() {
    717   if (Shell::GetInstance()->system_tray_delegate()->IsUserSupervised())
    718     return false;
    719 
    720   user::LoginStatus login_status =
    721       Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
    722 
    723   switch (login_status) {
    724     case user::LOGGED_IN_USER:
    725     case user::LOGGED_IN_OWNER:
    726       return true;
    727     case user::LOGGED_IN_LOCKED:
    728     case user::LOGGED_IN_PUBLIC:
    729     case user::LOGGED_IN_SUPERVISED:
    730     case user::LOGGED_IN_GUEST:
    731     case user::LOGGED_IN_RETAIL_MODE:
    732     case user::LOGGED_IN_KIOSK_APP:
    733     case user::LOGGED_IN_NONE:
    734       return false;
    735   }
    736 
    737   DCHECK(false);
    738   return false;
    739 }
    740 
    741 ShelfAlignment ShelfWidget::GetAlignment() const {
    742   return shelf_layout_manager_->GetAlignment();
    743 }
    744 
    745 void ShelfWidget::SetAlignment(ShelfAlignment alignment) {
    746   if (shelf_)
    747     shelf_->SetAlignment(alignment);
    748   status_area_widget_->SetShelfAlignment(alignment);
    749   delegate_view_->SchedulePaint();
    750 }
    751 
    752 void ShelfWidget::SetDimsShelf(bool dimming) {
    753   delegate_view_->SetDimmed(dimming);
    754   // Repaint all children, allowing updates to reflect dimmed state eg:
    755   // status area background, app list button and overflow button.
    756   if (shelf_)
    757     shelf_->SchedulePaint();
    758   status_area_widget_->SchedulePaint();
    759 }
    760 
    761 bool ShelfWidget::GetDimsShelf() const {
    762   return delegate_view_->GetDimmed();
    763 }
    764 
    765 void ShelfWidget::CreateShelf() {
    766   if (shelf_)
    767     return;
    768 
    769   Shell* shell = Shell::GetInstance();
    770   // This needs to be called before shelf_model().
    771   ShelfDelegate* shelf_delegate = shell->GetShelfDelegate();
    772   if (!shelf_delegate)
    773     return;  // Not ready to create Shelf.
    774 
    775   shelf_.reset(
    776       new Shelf(shell->shelf_model(), shell->GetShelfDelegate(), this));
    777   SetFocusCycler(shell->focus_cycler());
    778 
    779   // Inform the root window controller.
    780   RootWindowController::ForWindow(window_container_)->OnShelfCreated();
    781 
    782   shelf_->SetVisible(
    783       shell->session_state_delegate()->IsActiveUserSessionStarted());
    784   shelf_layout_manager_->LayoutShelf();
    785   Show();
    786 }
    787 
    788 bool ShelfWidget::IsShelfVisible() const {
    789   return shelf_.get() && shelf_->IsVisible();
    790 }
    791 
    792 void ShelfWidget::SetShelfVisibility(bool visible) {
    793   if (shelf_)
    794     shelf_->SetVisible(visible);
    795 }
    796 
    797 void ShelfWidget::SetFocusCycler(FocusCycler* focus_cycler) {
    798   delegate_view_->set_focus_cycler(focus_cycler);
    799   if (focus_cycler)
    800     focus_cycler->AddWidget(this);
    801 }
    802 
    803 FocusCycler* ShelfWidget::GetFocusCycler() {
    804   return delegate_view_->focus_cycler();
    805 }
    806 
    807 void ShelfWidget::ShutdownStatusAreaWidget() {
    808   if (status_area_widget_)
    809     status_area_widget_->Shutdown();
    810   status_area_widget_ = NULL;
    811 }
    812 
    813 void ShelfWidget::ForceUndimming(bool force) {
    814   delegate_view_->ForceUndimming(force);
    815 }
    816 
    817 void ShelfWidget::OnWidgetActivationChanged(views::Widget* widget,
    818                                             bool active) {
    819   activating_as_fallback_ = false;
    820   if (active)
    821     delegate_view_->SetPaneFocusAndFocusDefault();
    822   else
    823     delegate_view_->GetFocusManager()->ClearFocus();
    824 }
    825 
    826 int ShelfWidget::GetDimmingAlphaForTest() {
    827   if (delegate_view_)
    828     return delegate_view_->GetDimmingAlphaForTest();
    829   return -1;
    830 }
    831 
    832 gfx::Rect ShelfWidget::GetDimmerBoundsForTest() {
    833   if (delegate_view_)
    834     return delegate_view_->GetDimmerBoundsForTest();
    835   return gfx::Rect();
    836 }
    837 
    838 void ShelfWidget::DisableDimmingAnimationsForTest() {
    839   DCHECK(delegate_view_);
    840   return delegate_view_->disable_dimming_animations_for_test();
    841 }
    842 
    843 void ShelfWidget::WillDeleteShelf() {
    844   shelf_layout_manager_->RemoveObserver(this);
    845   shelf_layout_manager_ = NULL;
    846 }
    847 
    848 }  // namespace ash
    849