1 // Copyright 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 "ui/message_center/views/group_view.h" 6 7 #include "base/strings/string_number_conversions.h" 8 #include "base/strings/string_util.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "grit/ui_resources.h" 11 #include "grit/ui_strings.h" 12 #include "ui/base/l10n/l10n_util.h" 13 #include "ui/base/layout.h" 14 #include "ui/base/resource/resource_bundle.h" 15 #include "ui/gfx/canvas.h" 16 #include "ui/gfx/size.h" 17 #include "ui/gfx/skia_util.h" 18 #include "ui/gfx/text_elider.h" 19 #include "ui/message_center/message_center.h" 20 #include "ui/message_center/message_center_style.h" 21 #include "ui/message_center/message_center_tray.h" 22 #include "ui/message_center/message_center_util.h" 23 #include "ui/message_center/notification.h" 24 #include "ui/message_center/views/bounded_label.h" 25 #include "ui/message_center/views/constants.h" 26 #include "ui/message_center/views/notification_button.h" 27 #include "ui/message_center/views/proportional_image_view.h" 28 #include "ui/native_theme/native_theme.h" 29 #include "ui/views/background.h" 30 #include "ui/views/border.h" 31 #include "ui/views/controls/button/image_button.h" 32 #include "ui/views/controls/image_view.h" 33 #include "ui/views/controls/label.h" 34 #include "ui/views/layout/box_layout.h" 35 #include "ui/views/layout/fill_layout.h" 36 #include "ui/views/widget/widget.h" 37 38 #if defined(USE_AURA) 39 #include "ui/base/cursor/cursor.h" 40 #endif 41 42 namespace { 43 44 // static 45 views::Border* MakeTextBorder(int padding, int top, int bottom) { 46 // Split the padding between the top and the bottom, then add the extra space. 47 return views::Border::CreateEmptyBorder(padding / 2 + top, 48 message_center::kTextLeftPadding, 49 (padding + 1) / 2 + bottom, 50 message_center::kTextRightPadding); 51 } 52 53 } // namespace 54 55 namespace message_center { 56 57 // GroupView //////////////////////////////////////////////////////////// 58 59 GroupView::GroupView(MessageCenterController* controller, 60 const NotifierId& notifier_id, 61 const Notification& last_notification, 62 const gfx::ImageSkia& group_icon, 63 int group_size) 64 : MessageView(this, 65 last_notification.id(), 66 notifier_id, 67 last_notification.display_source()), 68 controller_(controller), 69 notifier_id_(notifier_id), 70 display_source_(last_notification.display_source()), 71 group_icon_(group_icon), 72 group_size_(group_size), 73 last_notification_id_(last_notification.id()), 74 background_view_(NULL), 75 top_view_(NULL), 76 bottom_view_(NULL), 77 title_view_(NULL), 78 message_view_(NULL), 79 context_message_view_(NULL), 80 icon_view_(NULL) 81 { 82 std::vector<string16> accessible_lines; 83 // TODO (dimich): move to MessageView 84 // Create the opaque background that's above the view's shadow. 85 background_view_ = new views::View(); 86 background_view_->set_background( 87 views::Background::CreateSolidBackground( 88 message_center::kNotificationBackgroundColor)); 89 90 // Create the top_view_, which collects into a vertical box all content 91 // at the top of the notification (to the right of the icon) except for the 92 // close button. 93 top_view_ = new views::View(); 94 top_view_->SetLayoutManager( 95 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); 96 top_view_->set_border(views::Border::CreateEmptyBorder( 97 kTextTopPadding - 8, 0, kTextBottomPadding - 5, 0)); 98 99 const gfx::FontList default_label_font_list = views::Label().font_list(); 100 101 // Create the title view if appropriate. 102 const gfx::FontList& font_list = 103 default_label_font_list.DeriveFontListWithSizeDelta(2); 104 int padding = kTitleLineHeight - font_list.GetHeight(); 105 106 title_view_ = new BoundedLabel( 107 gfx::TruncateString(string16(last_notification.title()), 108 kTitleCharacterLimit), 109 font_list); 110 accessible_lines.push_back(last_notification.title()); 111 title_view_->SetLineHeight(kTitleLineHeight); 112 title_view_->SetLineLimit(message_center::kTitleLineLimit); 113 title_view_->SetColors(message_center::kRegularTextColor, 114 kRegularTextBackgroundColor); 115 title_view_->set_border(MakeTextBorder(padding, 3, 0)); 116 top_view_->AddChildView(title_view_); 117 118 // Create the message view if appropriate. 119 if (!last_notification.message().empty()) { 120 int padding = kMessageLineHeight - default_label_font_list.GetHeight(); 121 message_view_ = new BoundedLabel( 122 gfx::TruncateString(last_notification.message(), 123 kMessageCharacterLimit)); 124 message_view_->SetLineHeight(kMessageLineHeight); 125 message_view_->SetColors(message_center::kRegularTextColor, 126 kDimTextBackgroundColor); 127 message_view_->set_border(MakeTextBorder(padding, 4, 0)); 128 top_view_->AddChildView(message_view_); 129 accessible_lines.push_back(last_notification.message()); 130 } 131 132 // Create the context message view if appropriate. 133 if (!last_notification.context_message().empty()) { 134 int padding = kMessageLineHeight - default_label_font_list.GetHeight(); 135 context_message_view_ = new BoundedLabel(gfx::TruncateString( 136 last_notification.context_message(), kContextMessageCharacterLimit), 137 default_label_font_list); 138 context_message_view_->SetLineLimit( 139 message_center::kContextMessageLineLimit); 140 context_message_view_->SetLineHeight(kMessageLineHeight); 141 context_message_view_->SetColors(message_center::kDimTextColor, 142 kContextTextBackgroundColor); 143 context_message_view_->set_border(MakeTextBorder(padding, 4, 0)); 144 top_view_->AddChildView(context_message_view_); 145 accessible_lines.push_back(last_notification.context_message()); 146 } 147 148 // Create the notification icon view. 149 icon_view_ = 150 new ProportionalImageView(last_notification.icon().AsImageSkia()); 151 icon_view_->set_background(views::Background::CreateSolidBackground( 152 kIconBackgroundColor)); 153 154 // Create the bottom_view_, which collects into a vertical box all content 155 // below the notification icon except for the expandGroup button. 156 bottom_view_ = new views::View(); 157 bottom_view_->SetLayoutManager( 158 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); 159 160 // Create "N more.." action button 161 views::View* separator = new views::ImageView(); 162 separator->set_border(views::Border::CreateSolidSidedBorder( 163 1, 0, 0, 0, kButtonSeparatorColor)); 164 bottom_view_->AddChildView(separator); 165 more_button_ = new NotificationButton(this); 166 string16 button_title = 167 l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_MORE_FROM, 168 base::IntToString16(group_size_), 169 display_source_); 170 more_button_->SetTitle(button_title); 171 more_button_->SetIcon(group_icon_); 172 bottom_view_->AddChildView(more_button_); 173 174 // Put together the different content and control views. Layering those allows 175 // for proper layout logic and it also allows the close button to 176 // overlap the content as needed to provide large enough click and touch area. 177 AddChildView(background_view_); 178 AddChildView(top_view_); 179 AddChildView(icon_view_); 180 AddChildView(bottom_view_); 181 AddChildView(close_button()); 182 set_accessible_name(JoinString(accessible_lines, '\n')); 183 } 184 185 GroupView::~GroupView() { 186 } 187 188 gfx::Size GroupView::GetPreferredSize() { 189 int top_width = top_view_->GetPreferredSize().width(); 190 int bottom_width = bottom_view_->GetPreferredSize().width(); 191 int preferred_width = std::max(top_width, bottom_width) + GetInsets().width(); 192 return gfx::Size(preferred_width, GetHeightForWidth(preferred_width)); 193 } 194 195 int GroupView::GetHeightForWidth(int width) { 196 int content_width = width - GetInsets().width(); 197 int top_height = top_view_->GetHeightForWidth(content_width); 198 int bottom_height = bottom_view_->GetHeightForWidth(content_width); 199 int content_height = std::max(top_height, kIconSize) + bottom_height; 200 201 // Adjust the height to make sure there is at least 16px of space below the 202 // icon if there is any space there (<http://crbug.com/232966>). 203 if (content_height > kIconSize) 204 content_height = std::max(content_height, 205 kIconSize + message_center::kIconBottomPadding); 206 207 return content_height + GetInsets().height(); 208 } 209 210 void GroupView::Layout() { 211 gfx::Insets insets = GetInsets(); 212 int content_width = width() - insets.width(); 213 int content_right = width() - insets.right(); 214 215 // Background. 216 background_view_->SetBounds(insets.left(), insets.top(), 217 content_width, height() - insets.height()); 218 219 // Top views. 220 int top_height = top_view_->GetHeightForWidth(content_width); 221 top_view_->SetBounds(insets.left(), insets.top(), content_width, top_height); 222 223 // Icon. 224 icon_view_->SetBounds(insets.left(), insets.top(), kIconSize, kIconSize); 225 226 // Bottom views. 227 int bottom_y = insets.top() + std::max(top_height, kIconSize); 228 int bottom_height = bottom_view_->GetHeightForWidth(content_width); 229 bottom_view_->SetBounds(insets.left(), bottom_y, 230 content_width, bottom_height); 231 232 // Close button. 233 gfx::Size close_size(close_button()->GetPreferredSize()); 234 close_button()->SetBounds(content_right - close_size.width(), insets.top(), 235 close_size.width(), close_size.height()); 236 } 237 238 void GroupView::OnFocus() { 239 MessageView::OnFocus(); 240 ScrollRectToVisible(GetLocalBounds()); 241 } 242 243 gfx::NativeCursor GroupView::GetCursor(const ui::MouseEvent& event) { 244 // If we ever have non-Aura views environment, this will fail compilation. 245 #if defined(USE_AURA) 246 return ui::kCursorHand; 247 #endif 248 } 249 250 void GroupView::ButtonPressed(views::Button* sender, 251 const ui::Event& event) { 252 if (sender == more_button_) { 253 controller_->ExpandGroup(notifier_id_); 254 return; 255 } 256 // Let the superclass handle anything other than action buttons. 257 // Warning: This may cause the GroupView itself to be deleted, 258 // so don't do anything afterwards. 259 MessageView::ButtonPressed(sender, event); 260 } 261 262 void GroupView::ClickOnNotification(const std::string& notification_id) { 263 controller_->GroupBodyClicked(notification_id); 264 } 265 266 void GroupView::RemoveNotification(const std::string& notification_id, 267 bool by_user) { 268 controller_->RemoveGroup(notifier_id_); 269 } 270 271 void GroupView::DisableNotificationsFromThisSource( 272 const NotifierId& notifier_id) { 273 controller_->DisableNotificationsFromThisSource(notifier_id); 274 } 275 276 void GroupView::ShowNotifierSettingsBubble() { 277 controller_->ShowNotifierSettingsBubble(); 278 } 279 280 } // namespace message_center 281