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