Home | History | Annotate | Download | only in tray
      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/system/tray/tray_background_view.h"
      6 
      7 #include "ash/ash_switches.h"
      8 #include "ash/root_window_controller.h"
      9 #include "ash/screen_util.h"
     10 #include "ash/shelf/shelf_layout_manager.h"
     11 #include "ash/shelf/shelf_widget.h"
     12 #include "ash/shell.h"
     13 #include "ash/shell_window_ids.h"
     14 #include "ash/system/status_area_widget.h"
     15 #include "ash/system/status_area_widget_delegate.h"
     16 #include "ash/system/tray/system_tray.h"
     17 #include "ash/system/tray/tray_constants.h"
     18 #include "ash/system/tray/tray_event_filter.h"
     19 #include "ash/wm/window_animations.h"
     20 #include "base/command_line.h"
     21 #include "grit/ash_resources.h"
     22 #include "ui/accessibility/ax_view_state.h"
     23 #include "ui/aura/window.h"
     24 #include "ui/aura/window_event_dispatcher.h"
     25 #include "ui/base/resource/resource_bundle.h"
     26 #include "ui/compositor/layer.h"
     27 #include "ui/compositor/layer_animation_element.h"
     28 #include "ui/compositor/scoped_layer_animation_settings.h"
     29 #include "ui/events/event_constants.h"
     30 #include "ui/gfx/animation/tween.h"
     31 #include "ui/gfx/canvas.h"
     32 #include "ui/gfx/image/image_skia.h"
     33 #include "ui/gfx/image/image_skia_operations.h"
     34 #include "ui/gfx/rect.h"
     35 #include "ui/gfx/screen.h"
     36 #include "ui/gfx/skia_util.h"
     37 #include "ui/gfx/transform.h"
     38 #include "ui/views/background.h"
     39 #include "ui/views/layout/box_layout.h"
     40 
     41 namespace {
     42 
     43 const int kTrayBackgroundAlpha = 100;
     44 const int kTrayBackgroundHoverAlpha = 150;
     45 const SkColor kTrayBackgroundPressedColor = SkColorSetRGB(66, 129, 244);
     46 
     47 const int kAnimationDurationForPopupMs = 200;
     48 
     49 // Duration of opacity animation for visibility changes.
     50 const int kAnimationDurationForVisibilityMs = 250;
     51 
     52 // When becoming visible delay the animation so that StatusAreaWidgetDelegate
     53 // can animate sibling views out of the position to be occuped by the
     54 // TrayBackgroundView.
     55 const int kShowAnimationDelayMs = 100;
     56 
     57 }  // namespace
     58 
     59 using views::TrayBubbleView;
     60 
     61 namespace ash {
     62 
     63 // static
     64 const char TrayBackgroundView::kViewClassName[] = "tray/TrayBackgroundView";
     65 
     66 // Used to track when the anchor widget changes position on screen so that the
     67 // bubble position can be updated.
     68 class TrayBackgroundView::TrayWidgetObserver : public views::WidgetObserver {
     69  public:
     70   explicit TrayWidgetObserver(TrayBackgroundView* host)
     71       : host_(host) {
     72   }
     73 
     74   virtual void OnWidgetBoundsChanged(views::Widget* widget,
     75                                      const gfx::Rect& new_bounds) OVERRIDE {
     76     host_->AnchorUpdated();
     77   }
     78 
     79   virtual void OnWidgetVisibilityChanged(views::Widget* widget,
     80                                          bool visible) OVERRIDE {
     81     host_->AnchorUpdated();
     82   }
     83 
     84  private:
     85   TrayBackgroundView* host_;
     86 
     87   DISALLOW_COPY_AND_ASSIGN(TrayWidgetObserver);
     88 };
     89 
     90 class TrayBackground : public views::Background {
     91  public:
     92   const static int kImageTypeDefault = 0;
     93   const static int kImageTypeOnBlack = 1;
     94   const static int kImageTypePressed = 2;
     95   const static int kNumStates = 3;
     96 
     97   const static int kImageHorizontal = 0;
     98   const static int kImageVertical = 1;
     99   const static int kNumOrientations = 2;
    100 
    101   explicit TrayBackground(TrayBackgroundView* tray_background_view) :
    102       tray_background_view_(tray_background_view) {
    103     set_alpha(kTrayBackgroundAlpha);
    104     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    105     leading_images_[kImageHorizontal][kImageTypeDefault] =
    106         rb.GetImageNamed(IDR_AURA_TRAY_BG_HORIZ_LEFT).ToImageSkia();
    107     middle_images_[kImageHorizontal][kImageTypeDefault] =
    108         rb.GetImageNamed(IDR_AURA_TRAY_BG_HORIZ_CENTER).ToImageSkia();
    109     trailing_images_[kImageHorizontal][kImageTypeDefault] =
    110         rb.GetImageNamed(IDR_AURA_TRAY_BG_HORIZ_RIGHT).ToImageSkia();
    111 
    112     leading_images_[kImageHorizontal][kImageTypeOnBlack] =
    113         rb.GetImageNamed(IDR_AURA_TRAY_BG_HORIZ_LEFT_ONBLACK).ToImageSkia();
    114     middle_images_[kImageHorizontal][kImageTypeOnBlack] =
    115         rb.GetImageNamed(IDR_AURA_TRAY_BG_HORIZ_CENTER_ONBLACK).ToImageSkia();
    116     trailing_images_[kImageHorizontal][kImageTypeOnBlack] =
    117         rb.GetImageNamed(IDR_AURA_TRAY_BG_HORIZ_RIGHT_ONBLACK).ToImageSkia();
    118 
    119     leading_images_[kImageHorizontal][kImageTypePressed] =
    120         rb.GetImageNamed(IDR_AURA_TRAY_BG_HORIZ_LEFT_PRESSED).ToImageSkia();
    121     middle_images_[kImageHorizontal][kImageTypePressed] =
    122         rb.GetImageNamed(IDR_AURA_TRAY_BG_HORIZ_CENTER_PRESSED).ToImageSkia();
    123     trailing_images_[kImageHorizontal][kImageTypePressed] =
    124         rb.GetImageNamed(IDR_AURA_TRAY_BG_HORIZ_RIGHT_PRESSED).ToImageSkia();
    125 
    126     leading_images_[kImageVertical][kImageTypeDefault] =
    127         rb.GetImageNamed(IDR_AURA_TRAY_BG_VERTICAL_TOP).ToImageSkia();
    128     middle_images_[kImageVertical][kImageTypeDefault] =
    129         rb.GetImageNamed(
    130             IDR_AURA_TRAY_BG_VERTICAL_CENTER).ToImageSkia();
    131     trailing_images_[kImageVertical][kImageTypeDefault] =
    132         rb.GetImageNamed(IDR_AURA_TRAY_BG_VERTICAL_BOTTOM).ToImageSkia();
    133 
    134     leading_images_[kImageVertical][kImageTypeOnBlack] =
    135         rb.GetImageNamed(IDR_AURA_TRAY_BG_VERTICAL_TOP_ONBLACK).ToImageSkia();
    136     middle_images_[kImageVertical][kImageTypeOnBlack] =
    137         rb.GetImageNamed(
    138             IDR_AURA_TRAY_BG_VERTICAL_CENTER_ONBLACK).ToImageSkia();
    139     trailing_images_[kImageVertical][kImageTypeOnBlack] =
    140         rb.GetImageNamed(
    141             IDR_AURA_TRAY_BG_VERTICAL_BOTTOM_ONBLACK).ToImageSkia();
    142 
    143     leading_images_[kImageVertical][kImageTypePressed] =
    144         rb.GetImageNamed(IDR_AURA_TRAY_BG_VERTICAL_TOP_PRESSED).ToImageSkia();
    145     middle_images_[kImageVertical][kImageTypePressed] =
    146         rb.GetImageNamed(
    147             IDR_AURA_TRAY_BG_VERTICAL_CENTER_PRESSED).ToImageSkia();
    148     trailing_images_[kImageVertical][kImageTypePressed] =
    149         rb.GetImageNamed(
    150             IDR_AURA_TRAY_BG_VERTICAL_BOTTOM_PRESSED).ToImageSkia();
    151   }
    152 
    153   virtual ~TrayBackground() {}
    154 
    155   SkColor color() { return color_; }
    156   void set_color(SkColor color) { color_ = color; }
    157   void set_alpha(int alpha) { color_ = SkColorSetARGB(alpha, 0, 0, 0); }
    158 
    159  private:
    160   ShelfWidget* GetShelfWidget() const {
    161     return RootWindowController::ForWindow(tray_background_view_->
    162         status_area_widget()->GetNativeWindow())->shelf();
    163   }
    164 
    165   // Overridden from views::Background.
    166   virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
    167     int orientation = kImageHorizontal;
    168     ShelfWidget* shelf_widget = GetShelfWidget();
    169     if (shelf_widget &&
    170         !shelf_widget->shelf_layout_manager()->IsHorizontalAlignment())
    171       orientation = kImageVertical;
    172 
    173     int state = kImageTypeDefault;
    174     if (tray_background_view_->draw_background_as_active())
    175       state = kImageTypePressed;
    176     else if (shelf_widget && shelf_widget->GetDimsShelf())
    177       state = kImageTypeOnBlack;
    178     else
    179       state = kImageTypeDefault;
    180 
    181     const gfx::ImageSkia* leading = leading_images_[orientation][state];
    182     const gfx::ImageSkia* middle = middle_images_[orientation][state];
    183     const gfx::ImageSkia* trailing = trailing_images_[orientation][state];
    184 
    185     gfx::Rect bounds(view->GetLocalBounds());
    186     gfx::Point leading_location, trailing_location;
    187     gfx::Rect middle_bounds;
    188 
    189     if (orientation == kImageHorizontal) {
    190       leading_location = gfx::Point(0, 0);
    191       trailing_location = gfx::Point(bounds.width() - trailing->width(), 0);
    192       middle_bounds = gfx::Rect(
    193           leading->width(),
    194           0,
    195           bounds.width() - (leading->width() + trailing->width()),
    196           bounds.height());
    197     } else {
    198       leading_location = gfx::Point(0, 0);
    199       trailing_location = gfx::Point(0, bounds.height() - trailing->height());
    200       middle_bounds = gfx::Rect(
    201           0,
    202           leading->height(),
    203           bounds.width(),
    204           bounds.height() - (leading->height() + trailing->height()));
    205     }
    206 
    207     canvas->DrawImageInt(*leading,
    208                          leading_location.x(),
    209                          leading_location.y());
    210 
    211     canvas->DrawImageInt(*trailing,
    212                          trailing_location.x(),
    213                          trailing_location.y());
    214 
    215     canvas->TileImageInt(*middle,
    216                          middle_bounds.x(),
    217                          middle_bounds.y(),
    218                          middle_bounds.width(),
    219                          middle_bounds.height());
    220   }
    221 
    222   SkColor color_;
    223   // Reference to the TrayBackgroundView for which this is a background.
    224   TrayBackgroundView* tray_background_view_;
    225 
    226   // References to the images used as backgrounds, they are owned by the
    227   // resource bundle class.
    228   const gfx::ImageSkia* leading_images_[kNumOrientations][kNumStates];
    229   const gfx::ImageSkia* middle_images_[kNumOrientations][kNumStates];
    230   const gfx::ImageSkia* trailing_images_[kNumOrientations][kNumStates];
    231 
    232   DISALLOW_COPY_AND_ASSIGN(TrayBackground);
    233 };
    234 
    235 TrayBackgroundView::TrayContainer::TrayContainer(ShelfAlignment alignment)
    236     : alignment_(alignment) {
    237   UpdateLayout();
    238 }
    239 
    240 void TrayBackgroundView::TrayContainer::SetAlignment(ShelfAlignment alignment) {
    241   if (alignment_ == alignment)
    242     return;
    243   alignment_ = alignment;
    244   UpdateLayout();
    245 }
    246 
    247 gfx::Size TrayBackgroundView::TrayContainer::GetPreferredSize() const {
    248   if (size_.IsEmpty())
    249     return views::View::GetPreferredSize();
    250   return size_;
    251 }
    252 
    253 void TrayBackgroundView::TrayContainer::ChildPreferredSizeChanged(
    254     views::View* child) {
    255   PreferredSizeChanged();
    256 }
    257 
    258 void TrayBackgroundView::TrayContainer::ChildVisibilityChanged(View* child) {
    259   PreferredSizeChanged();
    260 }
    261 
    262 void TrayBackgroundView::TrayContainer::ViewHierarchyChanged(
    263     const ViewHierarchyChangedDetails& details) {
    264   if (details.parent == this)
    265     PreferredSizeChanged();
    266 }
    267 
    268 void TrayBackgroundView::TrayContainer::UpdateLayout() {
    269   // Adjust the size of status tray dark background by adding additional
    270   // empty border.
    271   if (alignment_ == SHELF_ALIGNMENT_BOTTOM ||
    272       alignment_ == SHELF_ALIGNMENT_TOP) {
    273     SetBorder(views::Border::CreateEmptyBorder(
    274         kPaddingFromEdgeOfShelf,
    275         kPaddingFromEdgeOfShelf,
    276         kPaddingFromEdgeOfShelf,
    277         kPaddingFromEdgeOfShelf));
    278 
    279     views::BoxLayout* layout =
    280         new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0);
    281     layout->SetDefaultFlex(1);
    282     views::View::SetLayoutManager(layout);
    283   } else {
    284     SetBorder(views::Border::CreateEmptyBorder(
    285         kPaddingFromEdgeOfShelf,
    286         kPaddingFromEdgeOfShelf,
    287         kPaddingFromEdgeOfShelf,
    288         kPaddingFromEdgeOfShelf));
    289 
    290     views::BoxLayout* layout =
    291         new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
    292     layout->SetDefaultFlex(1);
    293     views::View::SetLayoutManager(layout);
    294   }
    295   PreferredSizeChanged();
    296 }
    297 
    298 ////////////////////////////////////////////////////////////////////////////////
    299 // TrayBackgroundView
    300 
    301 TrayBackgroundView::TrayBackgroundView(StatusAreaWidget* status_area_widget)
    302     : status_area_widget_(status_area_widget),
    303       tray_container_(NULL),
    304       shelf_alignment_(SHELF_ALIGNMENT_BOTTOM),
    305       background_(NULL),
    306       hide_background_animator_(this, 0, kTrayBackgroundAlpha),
    307       hover_background_animator_(
    308           this,
    309           0,
    310           kTrayBackgroundHoverAlpha - kTrayBackgroundAlpha),
    311       hovered_(false),
    312       draw_background_as_active_(false),
    313       touch_feedback_enabled_(false),
    314       widget_observer_(new TrayWidgetObserver(this)) {
    315   set_notify_enter_exit_on_child(true);
    316 
    317   // Initially we want to paint the background, but without the hover effect.
    318   hide_background_animator_.SetPaintsBackground(
    319       true, BACKGROUND_CHANGE_IMMEDIATE);
    320   hover_background_animator_.SetPaintsBackground(
    321       false, BACKGROUND_CHANGE_IMMEDIATE);
    322 
    323   tray_container_ = new TrayContainer(shelf_alignment_);
    324   SetContents(tray_container_);
    325   tray_event_filter_.reset(new TrayEventFilter);
    326 
    327   SetPaintToLayer(true);
    328   SetFillsBoundsOpaquely(false);
    329   // Start the tray items not visible, because visibility changes are animated.
    330   views::View::SetVisible(false);
    331 
    332   if (CommandLine::ForCurrentProcess()->
    333           HasSwitch(switches::kAshEnableTouchViewTouchFeedback)) {
    334     touch_feedback_enabled_ = true;
    335   }
    336 }
    337 
    338 TrayBackgroundView::~TrayBackgroundView() {
    339   if (GetWidget())
    340     GetWidget()->RemoveObserver(widget_observer_.get());
    341 }
    342 
    343 void TrayBackgroundView::Initialize() {
    344   GetWidget()->AddObserver(widget_observer_.get());
    345   SetTrayBorder();
    346 }
    347 
    348 void TrayBackgroundView::SetVisible(bool visible) {
    349   if (visible == layer()->GetTargetVisibility())
    350     return;
    351 
    352   if (visible) {
    353     // The alignment of the shelf can change while the TrayBackgroundView is
    354     // hidden. Reset the offscreen transform so that the animation to becoming
    355     // visible reflects the current layout.
    356     HideTransformation();
    357     // SetVisible(false) is defered until the animation for hiding is done.
    358     // Otherwise the view is immediately hidden and the animation does not
    359     // render.
    360     views::View::SetVisible(true);
    361     // If SetVisible(true) is called while animating to not visible, then
    362     // views::View::SetVisible(true) is a no-op. When the previous animation
    363     // ends layer->SetVisible(false) is called. To prevent this
    364     // layer->SetVisible(true) immediately interrupts the animation of this
    365     // property, and keeps the layer visible.
    366     layer()->SetVisible(true);
    367   }
    368 
    369   ui::ScopedLayerAnimationSettings animation(layer()->GetAnimator());
    370   animation.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
    371       kAnimationDurationForVisibilityMs));
    372   animation.SetPreemptionStrategy(
    373       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    374 
    375   if (visible) {
    376     animation.SetTweenType(gfx::Tween::EASE_OUT);
    377     // Show is delayed so as to allow time for other children of
    378     // StatusAreaWidget to begin animating to their new positions.
    379     layer()->GetAnimator()->SchedulePauseForProperties(
    380         base::TimeDelta::FromMilliseconds(kShowAnimationDelayMs),
    381         ui::LayerAnimationElement::OPACITY |
    382         ui::LayerAnimationElement::TRANSFORM);
    383     layer()->SetOpacity(1.0f);
    384     gfx::Transform transform;
    385     transform.Translate(0.0f, 0.0f);
    386     layer()->SetTransform(transform);
    387   } else {
    388     // Listen only to the hide animation. As we cannot turn off visibility
    389     // until the animation is over.
    390     animation.AddObserver(this);
    391     animation.SetTweenType(gfx::Tween::EASE_IN);
    392     layer()->SetOpacity(0.0f);
    393     layer()->SetVisible(false);
    394     HideTransformation();
    395   }
    396 }
    397 
    398 const char* TrayBackgroundView::GetClassName() const {
    399   return kViewClassName;
    400 }
    401 
    402 void TrayBackgroundView::OnMouseEntered(const ui::MouseEvent& event) {
    403   hovered_ = true;
    404 }
    405 
    406 void TrayBackgroundView::OnMouseExited(const ui::MouseEvent& event) {
    407   hovered_ = false;
    408 }
    409 
    410 void TrayBackgroundView::ChildPreferredSizeChanged(views::View* child) {
    411   PreferredSizeChanged();
    412 }
    413 
    414 void TrayBackgroundView::GetAccessibleState(ui::AXViewState* state) {
    415   state->role = ui::AX_ROLE_BUTTON;
    416   state->name = GetAccessibleNameForTray();
    417 }
    418 
    419 void TrayBackgroundView::AboutToRequestFocusFromTabTraversal(bool reverse) {
    420   // Return focus to the login view. See crbug.com/120500.
    421   views::View* v = GetNextFocusableView();
    422   if (v)
    423     v->AboutToRequestFocusFromTabTraversal(reverse);
    424 }
    425 
    426 bool TrayBackgroundView::PerformAction(const ui::Event& event) {
    427   return false;
    428 }
    429 
    430 gfx::Rect TrayBackgroundView::GetFocusBounds() {
    431   // The tray itself expands to the right and bottom edge of the screen to make
    432   // sure clicking on the edges brings up the popup. However, the focus border
    433   // should be only around the container.
    434   return GetContentsBounds();
    435 }
    436 
    437 void TrayBackgroundView::OnGestureEvent(ui::GestureEvent* event) {
    438   if (touch_feedback_enabled_) {
    439     if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
    440       SetDrawBackgroundAsActive(true);
    441     } else if (event->type() ==  ui::ET_GESTURE_SCROLL_BEGIN ||
    442                event->type() ==  ui::ET_GESTURE_TAP_CANCEL) {
    443       SetDrawBackgroundAsActive(false);
    444     }
    445   }
    446   ActionableView::OnGestureEvent(event);
    447 }
    448 
    449 void TrayBackgroundView::UpdateBackground(int alpha) {
    450   // The animator should never fire when the alternate shelf layout is used.
    451   if (!background_ || draw_background_as_active_)
    452     return;
    453   background_->set_alpha(hide_background_animator_.alpha() +
    454                          hover_background_animator_.alpha());
    455   SchedulePaint();
    456 }
    457 
    458 void TrayBackgroundView::SetContents(views::View* contents) {
    459   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
    460   AddChildView(contents);
    461 }
    462 
    463 void TrayBackgroundView::SetPaintsBackground(
    464     bool value, BackgroundAnimatorChangeType change_type) {
    465   hide_background_animator_.SetPaintsBackground(value, change_type);
    466 }
    467 
    468 void TrayBackgroundView::SetContentsBackground() {
    469   background_ = new TrayBackground(this);
    470   tray_container_->set_background(background_);
    471 }
    472 
    473 ShelfLayoutManager* TrayBackgroundView::GetShelfLayoutManager() {
    474   return ShelfLayoutManager::ForShelf(GetWidget()->GetNativeView());
    475 }
    476 
    477 void TrayBackgroundView::SetShelfAlignment(ShelfAlignment alignment) {
    478   shelf_alignment_ = alignment;
    479   SetTrayBorder();
    480   tray_container_->SetAlignment(alignment);
    481 }
    482 
    483 void TrayBackgroundView::SetTrayBorder() {
    484   views::View* parent = status_area_widget_->status_area_widget_delegate();
    485   // Tray views are laid out right-to-left or bottom-to-top
    486   bool on_edge = (this == parent->child_at(0));
    487   int left_edge, top_edge, right_edge, bottom_edge;
    488   if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM) {
    489     top_edge = ShelfLayoutManager::kShelfItemInset;
    490     left_edge = 0;
    491     bottom_edge = kShelfSize -
    492         ShelfLayoutManager::kShelfItemInset - kShelfItemHeight;
    493     right_edge = on_edge ? kPaddingFromEdgeOfShelf : 0;
    494   } else if (shelf_alignment() == SHELF_ALIGNMENT_LEFT) {
    495     top_edge = 0;
    496     left_edge = kShelfSize -
    497         ShelfLayoutManager::kShelfItemInset - kShelfItemHeight;
    498     bottom_edge = on_edge ? kPaddingFromEdgeOfShelf : 0;
    499     right_edge = ShelfLayoutManager::kShelfItemInset;
    500   } else { // SHELF_ALIGNMENT_RIGHT
    501     top_edge = 0;
    502     left_edge = ShelfLayoutManager::kShelfItemInset;
    503     bottom_edge = on_edge ? kPaddingFromEdgeOfShelf : 0;
    504     right_edge = kShelfSize -
    505         ShelfLayoutManager::kShelfItemInset - kShelfItemHeight;
    506   }
    507   SetBorder(views::Border::CreateEmptyBorder(
    508       top_edge, left_edge, bottom_edge, right_edge));
    509 }
    510 
    511 void TrayBackgroundView::OnImplicitAnimationsCompleted() {
    512   // If there is another animation in the queue, the reverse animation was
    513   // triggered before the completion of animating to invisible. Do not turn off
    514   // the visibility so that the next animation may render. The value of
    515   // layer()->GetTargetVisibility() can be incorrect if the hide animation was
    516   // aborted to schedule an animation to become visible. As the new animation
    517   // is not yet added to the queue. crbug.com/374236
    518   if(layer()->GetAnimator()->is_animating() ||
    519      layer()->GetTargetVisibility())
    520     return;
    521   views::View::SetVisible(false);
    522 }
    523 
    524 void TrayBackgroundView::HideTransformation() {
    525   gfx::Transform transform;
    526   if (shelf_alignment_ == SHELF_ALIGNMENT_BOTTOM ||
    527       shelf_alignment_ == SHELF_ALIGNMENT_TOP)
    528     transform.Translate(width(), 0.0f);
    529   else
    530     transform.Translate(0.0f, height());
    531   layer()->SetTransform(transform);
    532 }
    533 
    534 void TrayBackgroundView::InitializeBubbleAnimations(
    535     views::Widget* bubble_widget) {
    536   wm::SetWindowVisibilityAnimationType(
    537       bubble_widget->GetNativeWindow(),
    538       wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
    539   wm::SetWindowVisibilityAnimationTransition(
    540       bubble_widget->GetNativeWindow(),
    541       wm::ANIMATE_HIDE);
    542   wm::SetWindowVisibilityAnimationDuration(
    543       bubble_widget->GetNativeWindow(),
    544       base::TimeDelta::FromMilliseconds(kAnimationDurationForPopupMs));
    545 }
    546 
    547 aura::Window* TrayBackgroundView::GetBubbleWindowContainer() const {
    548   return ash::Shell::GetContainer(
    549       tray_container()->GetWidget()->GetNativeWindow()->GetRootWindow(),
    550       ash::kShellWindowId_SettingBubbleContainer);
    551 }
    552 
    553 gfx::Rect TrayBackgroundView::GetBubbleAnchorRect(
    554     views::Widget* anchor_widget,
    555     TrayBubbleView::AnchorType anchor_type,
    556     TrayBubbleView::AnchorAlignment anchor_alignment) const {
    557   gfx::Rect rect;
    558   if (anchor_widget && anchor_widget->IsVisible()) {
    559     rect = anchor_widget->GetWindowBoundsInScreen();
    560     if (anchor_type == TrayBubbleView::ANCHOR_TYPE_TRAY) {
    561       if (anchor_alignment == TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM) {
    562         bool rtl = base::i18n::IsRTL();
    563         rect.Inset(
    564             rtl ? kBubblePaddingHorizontalSide : 0,
    565             kBubblePaddingHorizontalBottom,
    566             rtl ? 0 : kBubblePaddingHorizontalSide,
    567             0);
    568       } else if (anchor_alignment == TrayBubbleView::ANCHOR_ALIGNMENT_LEFT) {
    569         rect.Inset(0, 0, kBubblePaddingVerticalSide + 4,
    570                    kBubblePaddingVerticalBottom);
    571       } else if (anchor_alignment == TrayBubbleView::ANCHOR_ALIGNMENT_RIGHT) {
    572         rect.Inset(kBubblePaddingVerticalSide, 0, 0,
    573                    kBubblePaddingVerticalBottom);
    574       } else {
    575         // TODO(bruthig) May need to handle other ANCHOR_ALIGNMENT_ values.
    576         // ie. ANCHOR_ALIGNMENT_TOP
    577         DCHECK(false) << "Unhandled anchor alignment.";
    578       }
    579     } else if (anchor_type == TrayBubbleView::ANCHOR_TYPE_BUBBLE) {
    580       // Invert the offsets to align with the bubble below.
    581       // Note that with the alternate shelf layout the tips are not shown and
    582       // the offsets for left and right alignment do not need to be applied.
    583       int vertical_alignment = 0;
    584       int horizontal_alignment = kBubblePaddingVerticalBottom;
    585       if (anchor_alignment == TrayBubbleView::ANCHOR_ALIGNMENT_LEFT)
    586         rect.Inset(vertical_alignment, 0, 0, horizontal_alignment);
    587       else if (anchor_alignment == TrayBubbleView::ANCHOR_ALIGNMENT_RIGHT)
    588         rect.Inset(0, 0, vertical_alignment, horizontal_alignment);
    589     } else {
    590       DCHECK(false) << "Unhandled anchor type.";
    591     }
    592   } else {
    593     aura::Window* target_root = anchor_widget ?
    594         anchor_widget->GetNativeView()->GetRootWindow() :
    595         Shell::GetPrimaryRootWindow();
    596     rect = target_root->bounds();
    597     if (anchor_type == TrayBubbleView::ANCHOR_TYPE_TRAY) {
    598       if (anchor_alignment == TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM) {
    599         rect = gfx::Rect(
    600             base::i18n::IsRTL() ?
    601             kPaddingFromRightEdgeOfScreenBottomAlignment :
    602             rect.width() - kPaddingFromRightEdgeOfScreenBottomAlignment,
    603             rect.height() - kPaddingFromBottomOfScreenBottomAlignment,
    604             0, 0);
    605         rect = ScreenUtil::ConvertRectToScreen(target_root, rect);
    606       } else if (anchor_alignment == TrayBubbleView::ANCHOR_ALIGNMENT_LEFT) {
    607         rect = gfx::Rect(
    608             kPaddingFromRightEdgeOfScreenBottomAlignment,
    609             rect.height() - kPaddingFromBottomOfScreenBottomAlignment,
    610             1, 1);
    611         rect = ScreenUtil::ConvertRectToScreen(target_root, rect);
    612       } else if (anchor_alignment == TrayBubbleView::ANCHOR_ALIGNMENT_RIGHT) {
    613         rect = gfx::Rect(
    614             rect.width() - kPaddingFromRightEdgeOfScreenBottomAlignment,
    615             rect.height() - kPaddingFromBottomOfScreenBottomAlignment,
    616             1, 1);
    617         rect = ScreenUtil::ConvertRectToScreen(target_root, rect);
    618       } else {
    619         // TODO(bruthig) May need to handle other ANCHOR_ALIGNMENT_ values.
    620         // ie. ANCHOR_ALIGNMENT_TOP
    621         DCHECK(false) << "Unhandled anchor alignment.";
    622       }
    623     } else {
    624       rect = gfx::Rect(
    625           base::i18n::IsRTL() ?
    626           kPaddingFromRightEdgeOfScreenBottomAlignment :
    627           rect.width() - kPaddingFromRightEdgeOfScreenBottomAlignment,
    628           rect.height() - kPaddingFromBottomOfScreenBottomAlignment,
    629           0, 0);
    630     }
    631   }
    632   return rect;
    633 }
    634 
    635 TrayBubbleView::AnchorAlignment TrayBackgroundView::GetAnchorAlignment() const {
    636   switch (shelf_alignment_) {
    637     case SHELF_ALIGNMENT_BOTTOM:
    638       return TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM;
    639     case SHELF_ALIGNMENT_LEFT:
    640       return TrayBubbleView::ANCHOR_ALIGNMENT_LEFT;
    641     case SHELF_ALIGNMENT_RIGHT:
    642       return TrayBubbleView::ANCHOR_ALIGNMENT_RIGHT;
    643     case SHELF_ALIGNMENT_TOP:
    644       return TrayBubbleView::ANCHOR_ALIGNMENT_TOP;
    645   }
    646   NOTREACHED();
    647   return TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM;
    648 }
    649 
    650 void TrayBackgroundView::SetDrawBackgroundAsActive(bool visible) {
    651   if (draw_background_as_active_ == visible)
    652     return;
    653   draw_background_as_active_ = visible;
    654   if (!background_)
    655     return;
    656 
    657   // Do not change gradually, changing color between grey and blue is weird.
    658   if (draw_background_as_active_)
    659     background_->set_color(kTrayBackgroundPressedColor);
    660   else if (hovered_)
    661     background_->set_alpha(kTrayBackgroundHoverAlpha);
    662   else
    663     background_->set_alpha(kTrayBackgroundAlpha);
    664   SchedulePaint();
    665 }
    666 
    667 void TrayBackgroundView::UpdateBubbleViewArrow(
    668     views::TrayBubbleView* bubble_view) {
    669   // Nothing to do here.
    670 }
    671 
    672 }  // namespace ash
    673