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