Home | History | Annotate | Download | only in ash
      1 // Copyright (c) 2013 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/popup_message.h"
      6 
      7 #include "ash/wm/window_animations.h"
      8 #include "grit/ash_resources.h"
      9 #include "ui/base/resource/resource_bundle.h"
     10 #include "ui/gfx/insets.h"
     11 #include "ui/views/bubble/bubble_delegate.h"
     12 #include "ui/views/bubble/bubble_frame_view.h"
     13 #include "ui/views/controls/image_view.h"
     14 #include "ui/views/controls/label.h"
     15 #include "ui/views/layout/box_layout.h"
     16 #include "ui/views/widget/widget.h"
     17 
     18 namespace ash {
     19 namespace {
     20 const int kMessageTopBottomMargin = 10;
     21 const int kMessageLeftRightMargin = 10;
     22 const int kMessageAppearanceDelayMs = 200;
     23 const int kMessageMinHeight = 29 - 2 * kMessageTopBottomMargin;
     24 const SkColor kMessageTextColor = SkColorSetRGB(0x22, 0x22, 0x22);
     25 
     26 // The maximum width of the Message bubble.  Borrowed the value from
     27 // ash/message/message_controller.cc
     28 const int kMessageMaxWidth = 250;
     29 
     30 // The offset for the Message bubble - making sure that the bubble is flush
     31 // with the shelf. The offset includes the arrow size in pixels as well as
     32 // the activation bar and other spacing elements.
     33 const int kArrowOffsetLeftRight = 11;
     34 const int kArrowOffsetTopBottom = 7;
     35 
     36 // The number of pixels between the icon and the text.
     37 const int kHorizontalPopupPaddingBetweenItems = 10;
     38 
     39 // The number of pixels between the text items.
     40 const int kVerticalPopupPaddingBetweenItems = 10;
     41 }  // namespace
     42 
     43 // The implementation of Message of the launcher.
     44 class PopupMessage::MessageBubble : public views::BubbleDelegateView {
     45  public:
     46   MessageBubble(const base::string16& caption,
     47                 const base::string16& message,
     48                 IconType message_type,
     49                 views::View* anchor,
     50                 views::BubbleBorder::Arrow arrow_orientation,
     51                 const gfx::Size& size_override,
     52                 int arrow_offset);
     53 
     54   void Close();
     55 
     56  private:
     57   // views::View overrides:
     58   virtual gfx::Size GetPreferredSize() OVERRIDE;
     59 
     60   // Each component (width/height) can force a size override for that component
     61   // if not 0.
     62   gfx::Size size_override_;
     63 
     64   DISALLOW_COPY_AND_ASSIGN(MessageBubble);
     65 };
     66 
     67 PopupMessage::MessageBubble::MessageBubble(const base::string16& caption,
     68                                            const base::string16& message,
     69                                            IconType message_type,
     70                                            views::View* anchor,
     71                                            views::BubbleBorder::Arrow arrow,
     72                                            const gfx::Size& size_override,
     73                                            int arrow_offset)
     74     : views::BubbleDelegateView(anchor, arrow),
     75       size_override_(size_override) {
     76   gfx::Insets insets = gfx::Insets(kArrowOffsetTopBottom,
     77                                    kArrowOffsetLeftRight,
     78                                    kArrowOffsetTopBottom,
     79                                    kArrowOffsetLeftRight);
     80   // An anchor can have an asymmetrical border for spacing reasons. Adjust the
     81   // anchor location for this.
     82   if (anchor->border())
     83     insets += anchor->border()->GetInsets();
     84 
     85   set_anchor_view_insets(insets);
     86   set_close_on_esc(false);
     87   set_close_on_deactivate(false);
     88   set_use_focusless(true);
     89   set_accept_events(false);
     90 
     91   set_margins(gfx::Insets(kMessageTopBottomMargin, kMessageLeftRightMargin,
     92                           kMessageTopBottomMargin, kMessageLeftRightMargin));
     93   set_shadow(views::BubbleBorder::SMALL_SHADOW);
     94 
     95   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
     96   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0,
     97                                         kHorizontalPopupPaddingBetweenItems));
     98 
     99   // Here is the layout:
    100   //         arrow_offset (if not 0)
    101   //       |-------------|
    102   //       |             ^
    103   //       +-------------------------------------------------+
    104   //      -|                                                 |-
    105   //  icon |  [!]  Caption in bold which can be multi line   | caption_label
    106   //      -|                                                 |-
    107   //       |       Message text which can be multi line      | message_label
    108   //       |       as well.                                  |
    109   //       |                                                 |-
    110   //       +-------------------------------------------------+
    111   //             |------------details container--------------|
    112   // Note that the icon, caption and massage are optional.
    113 
    114   // Add the icon to the first column (if there is one).
    115   if (message_type != ICON_NONE) {
    116     views::ImageView* icon = new views::ImageView();
    117     icon->SetImage(
    118         bundle.GetImageNamed(IDR_AURA_WARNING_ICON).ToImageSkia());
    119     icon->SetVerticalAlignment(views::ImageView::LEADING);
    120     AddChildView(icon);
    121   }
    122 
    123   // Create a container for the text items and use it as second column.
    124   views::View* details = new views::View();
    125   AddChildView(details);
    126   details->SetLayoutManager(new views::BoxLayout(
    127       views::BoxLayout::kVertical, 0, 0, kVerticalPopupPaddingBetweenItems));
    128 
    129   // The caption label.
    130   if (!caption.empty()) {
    131     views::Label* caption_label = new views::Label(caption);
    132     caption_label->SetMultiLine(true);
    133     caption_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    134     caption_label->SetFont(bundle.GetFont(ui::ResourceBundle::BoldFont));
    135     caption_label->SetEnabledColor(kMessageTextColor);
    136     details->AddChildView(caption_label);
    137   }
    138 
    139   // The message label.
    140   if (!message.empty()) {
    141     views::Label* message_label = new views::Label(message);
    142     message_label->SetMultiLine(true);
    143     message_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    144     message_label->SetEnabledColor(kMessageTextColor);
    145     details->AddChildView(message_label);
    146   }
    147   views::BubbleDelegateView::CreateBubble(this);
    148 
    149   // Change the arrow offset if needed.
    150   if (arrow_offset) {
    151     // With the creation of the bubble, the bubble got already placed (and
    152     // possibly re-oriented to fit on the screen). Since it is not possible to
    153     // set the arrow offset before the creation, we need to set the offset,
    154     // and the orientation variables again and force a re-placement.
    155     GetBubbleFrameView()->bubble_border()->set_arrow_offset(arrow_offset);
    156     GetBubbleFrameView()->bubble_border()->set_arrow(arrow);
    157     SetAlignment(views::BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR);
    158   }
    159 }
    160 
    161 void PopupMessage::MessageBubble::Close() {
    162   if (GetWidget())
    163     GetWidget()->Close();
    164 }
    165 
    166 gfx::Size PopupMessage::MessageBubble::GetPreferredSize() {
    167   gfx::Size pref_size = views::BubbleDelegateView::GetPreferredSize();
    168   // Override the size with either the provided size or adjust it to not
    169   // violate our minimum / maximum sizes.
    170   if (size_override_.height())
    171     pref_size.set_height(size_override_.height());
    172   else if (pref_size.height() < kMessageMinHeight)
    173     pref_size.set_height(kMessageMinHeight);
    174 
    175   if (size_override_.width())
    176     pref_size.set_width(size_override_.width());
    177   else if (pref_size.width() > kMessageMaxWidth)
    178     pref_size.set_width(kMessageMaxWidth);
    179 
    180   return pref_size;
    181 }
    182 
    183 PopupMessage::PopupMessage(const base::string16& caption,
    184                            const base::string16& message,
    185                            IconType message_type,
    186                            views::View* anchor,
    187                            views::BubbleBorder::Arrow arrow,
    188                            const gfx::Size& size_override,
    189                            int arrow_offset)
    190     : view_(NULL) {
    191   view_ = new MessageBubble(
    192       caption, message, message_type, anchor, arrow, size_override,
    193       arrow_offset);
    194   widget_ = view_->GetWidget();
    195 
    196   gfx::NativeView native_view = widget_->GetNativeView();
    197   views::corewm::SetWindowVisibilityAnimationType(
    198       native_view, views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL);
    199   views::corewm::SetWindowVisibilityAnimationTransition(
    200       native_view, views::corewm::ANIMATE_HIDE);
    201   view_->GetWidget()->Show();
    202 }
    203 
    204 PopupMessage::~PopupMessage() {
    205   CancelHidingAnimation();
    206   Close();
    207 }
    208 
    209 void PopupMessage::Close() {
    210   if (view_) {
    211     view_->Close();
    212     view_ = NULL;
    213     widget_ = NULL;
    214   }
    215 }
    216 
    217 void PopupMessage::CancelHidingAnimation() {
    218   if (!widget_ || !widget_->GetNativeView())
    219     return;
    220 
    221   gfx::NativeView native_view = widget_->GetNativeView();
    222   views::corewm::SetWindowVisibilityAnimationTransition(
    223       native_view, views::corewm::ANIMATE_NONE);
    224 }
    225 
    226 }  // namespace ash
    227