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