Home | History | Annotate | Download | only in views
      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