Chromium Code Reviews| Index: ui/message_center/notification_view.cc |
| diff --git a/ui/message_center/notification_view.cc b/ui/message_center/notification_view.cc |
| index f38713e472f8e7372b0e5b83fa4abc8c79b956c4..018c3c68f68d7d66b7a2a04321d0f566e8165f4f 100644 |
| --- a/ui/message_center/notification_view.cc |
| +++ b/ui/message_center/notification_view.cc |
| @@ -10,6 +10,7 @@ |
| #include "ui/base/accessibility/accessible_view_state.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/base/text/text_elider.h" |
| +#include "ui/gfx/canvas.h" |
| #include "ui/gfx/size.h" |
| #include "ui/message_center/message_center_constants.h" |
| #include "ui/message_center/message_center_switches.h" |
| @@ -57,6 +58,29 @@ views::Background* MakeBackground(SkColor color) { |
| return views::Background::CreateSolidBackground(color); |
| } |
| +// ContentViews just propagate their children's preferred size changes. |
| +class ContentView : public views::View { |
| + public: |
| + ContentView(); |
| + virtual ~ContentView(); |
| + |
| + // Overridden from views::View. |
| + virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE; |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(ContentView); |
| +}; |
| + |
| +ContentView::ContentView() { |
| +} |
| + |
| +ContentView::~ContentView() { |
| +} |
| + |
| +void ContentView::ChildPreferredSizeChanged(views::View* child) { |
| + PreferredSizeChanged(); |
| +} |
| + |
| // ItemViews are responsible for drawing each list notification item's title and |
| // message next to each other within a single column. |
| class ItemView : public views::View { |
| @@ -93,8 +117,10 @@ ItemView::ItemView(const message_center::NotificationItem& item) { |
| ItemView::~ItemView() { |
| } |
| -// ProportionalImageViews match their heights to their widths to preserve the |
| -// proportions of their images. |
| +// ProportionalImageViews center their images to preserve their proportion. |
| +// Note that for this subclass of views::ImageView GetImageBounds() will return |
| +// potentially incorrect values (this can't be fixed because GetImageBounds() |
| +// is not virtual) and horizontal and vertical alignments will be ignored. |
| class ProportionalImageView : public views::ImageView { |
| public: |
| ProportionalImageView(); |
| @@ -102,8 +128,11 @@ class ProportionalImageView : public views::ImageView { |
| // Overridden from views::View. |
| virtual int GetHeightForWidth(int width) OVERRIDE; |
| + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; |
| private: |
| + gfx::Size GetImageSizeForWidth(int width); |
| + |
| DISALLOW_COPY_AND_ASSIGN(ProportionalImageView); |
| }; |
| @@ -114,18 +143,42 @@ ProportionalImageView::~ProportionalImageView() { |
| } |
| int ProportionalImageView::GetHeightForWidth(int width) { |
| - int height = 0; |
| - gfx::ImageSkia image = GetImage(); |
| - if (image.width() > 0 && image.height() > 0) { |
| - double proportion = image.height() / (double) image.width(); |
| - height = 0.5 + width * proportion; |
| - if (height > message_center::kNotificationMaximumImageHeight) { |
| - height = message_center::kNotificationMaximumImageHeight; |
| - width = 0.5 + height / proportion; |
| + return GetImageSizeForWidth(width).height(); |
| +} |
|
dharcourt
2013/02/23 04:32:00
SetImageSize() needed to be removed from GetImageS
|
| + |
| +void ProportionalImageView::OnPaint(gfx::Canvas* canvas) { |
| + View::OnPaint(canvas); |
| + |
| + gfx::Size draw_size(GetImageSizeForWidth(width())); |
| + if (!draw_size.IsEmpty()) { |
| + int x = (width() - draw_size.width()) / 2; |
| + int y = (height() - draw_size.height()) / 2; |
| + |
| + gfx::Size image_size(GetImage().size()); |
| + if (image_size == draw_size) { |
| + canvas->DrawImageInt(GetImage(), x, y); |
| + } else { |
| + // Resize case |
| + SkPaint paint; |
| + paint.setFilterBitmap(true); |
| + canvas->DrawImageInt(GetImage(), 0, 0, |
| + image_size.width(), image_size.height(), x, y, |
| + draw_size.width(), draw_size.height(), true, paint); |
| } |
| - SetImageSize(gfx::Size(width, height)); |
| } |
| - return height; |
| +} |
| + |
| +gfx::Size ProportionalImageView::GetImageSizeForWidth(int width) { |
| + gfx::Size size(GetImage().size()); |
| + if (width > 0 && !size.IsEmpty()) { |
| + double proportion = size.height() / (double) size.width(); |
| + size.SetSize(width, std::max(0.5 + width * proportion, 1.0)); |
| + if (size.height() > message_center::kNotificationMaximumImageHeight) { |
| + int height = message_center::kNotificationMaximumImageHeight; |
| + size.SetSize(std::max(0.5 + height / proportion, 1.0), height); |
| + } |
| + } |
| + return size; |
| } |
| // NotificationsButtons render the action buttons of notifications. |
| @@ -228,53 +281,81 @@ MessageView* NotificationView::ViewForNotification( |
| return new NotificationView(list_delegate, notification); |
| } |
| -NotificationView::NotificationView( |
| - NotificationList::Delegate* list_delegate, |
| - const Notification& notification) |
| - : MessageView(list_delegate, notification) { |
| +NotificationView::NotificationView(NotificationList::Delegate* list_delegate, |
| + const Notification& notification) |
| + : MessageView(list_delegate, notification), |
| + content_view_(NULL), |
| + icon_view_(NULL) { |
| } |
| NotificationView::~NotificationView() { |
| } |
| void NotificationView::Layout() { |
| - if (content_view_) { |
| - gfx::Rect contents_bounds = GetContentsBounds(); |
| - content_view_->SetBoundsRect(contents_bounds); |
| - if (close_button()) { |
| - gfx::Size size(close_button()->GetPreferredSize()); |
| - close_button()->SetBounds(contents_bounds.right() - size.width(), 0, |
| - size.width(), size.height()); |
| + // Create the child views if necessary. Those are in two layers: The first |
| + // layer has the notification content (text, images, action buttons, ...). |
| + // This is overlaid by a second layer that has the notification close and |
| + // expand buttons. This allows the close and expand buttons to overlap the |
| + // content as needed to provide a large enough click area |
| + // (<http://crbug.com/168822> and touch area <http://crbug.com/168856>). |
| + if (!content_view_) { |
| + AddChildView(MakeContentView()); |
| + AddChildView(close_button()); |
| + if (!IsExpanded()) { |
| + AddChildView(expand_button()); |
| } |
| } |
| + |
| + gfx::Rect content_bounds(GetLocalBounds()); |
| + content_bounds.Inset(GetInsets()); |
| + content_view_->SetBoundsRect(content_bounds); |
| + |
| + gfx::Size close_size(close_button()->GetPreferredSize()); |
| + close_button()->SetBounds(content_bounds.right() - close_size.width(), |
| + content_bounds.y(), |
| + close_size.width(), |
| + close_size.height()); |
| + |
| + gfx::Rect icon_bounds(content_bounds.origin(), icon_view_->size()); |
| + gfx::Size expand_size(expand_button()->GetPreferredSize()); |
| + expand_button()->SetBounds(content_bounds.right() - expand_size.width(), |
| + icon_bounds.bottom() - expand_size.height(), |
| + expand_size.width(), |
| + expand_size.height()); |
| } |
| gfx::Size NotificationView::GetPreferredSize() { |
| - if (!content_view_) |
| - return gfx::Size(); |
| - gfx::Size size = content_view_->GetPreferredSize(); |
| - if (border()) { |
| - gfx::Insets border_insets = border()->GetInsets(); |
| - size.Enlarge(border_insets.width(), border_insets.height()); |
| + gfx::Size size; |
| + if (content_view_) { |
| + size = content_view_->GetPreferredSize(); |
| + if (border()) { |
| + gfx::Insets insets = border()->GetInsets(); |
| + size.Enlarge(insets.width(), insets.height()); |
| + } |
| } |
| return size; |
|
dharcourt
2013/02/23 04:32:00
Having a return gfx::Size() in the middle of this
|
| } |
| -void NotificationView::SetUpView() { |
| - // This view is composed of two layers: The first layer has the notification |
| - // content (text, images, action buttons, ...). This is overlaid by a second |
| - // layer that has the notification close button and will later also have the |
| - // expand button. This allows the close and expand buttons to overlap the |
| - // content as needed to provide a large enough click area |
| - // (<http://crbug.com/168822> and touch area <http://crbug.com/168856>). |
| - AddChildView(MakeContentView()); |
| - AddChildView(close_button()); |
| +void NotificationView::ChildPreferredSizeChanged(views::View* child) { |
| + PreferredSizeChanged(); |
| +} |
| + |
| +void NotificationView::SetExpanded(bool expanded) { |
| + if (expanded != IsExpanded()) { |
| + MessageView::SetExpanded(expanded); |
| + content_view_ = NULL; |
| + icon_view_ = NULL; |
| + action_buttons_.clear(); |
| + RemoveAllChildViews(true); |
| + PreferredSizeChanged(); |
| + SchedulePaint(); |
| + } |
| } |
| void NotificationView::ButtonPressed(views::Button* sender, |
| const ui::Event& event) { |
| for (size_t i = 0; i < action_buttons_.size(); ++i) { |
| - if (action_buttons_[i] == sender) { |
| + if (sender == action_buttons_[i]) { |
| list_delegate()->OnButtonClicked(notification().id, i); |
| return; |
| } |
| @@ -283,7 +364,7 @@ void NotificationView::ButtonPressed(views::Button* sender, |
| } |
| views::View* NotificationView::MakeContentView() { |
| - content_view_ = new views::View(); |
| + content_view_ = new ContentView(); |
| content_view_->set_background( |
| views::Background::CreateSolidBackground(kBackgroundColor)); |
| @@ -292,37 +373,44 @@ views::View* NotificationView::MakeContentView() { |
| // items), followed by a padding view. Laying out the icon view will require |
| // information about the text views, so these are created first and collected |
| // in this vector. |
| - std::vector<views::View*> texts; |
| + std::vector<views::View*> text_views; |
| // Title if it exists. |
| if (!notification().title.empty()) { |
| views::Label* title = new views::Label(notification().title); |
| title->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| - title->SetElideBehavior(views::Label::ELIDE_AT_END); |
| + if (IsExpanded()) |
| + title->SetMultiLine(true); |
| + else |
| + title->SetElideBehavior(views::Label::ELIDE_AT_END); |
| title->SetFont(title->font().DeriveFont(4)); |
| title->SetEnabledColor(kTitleColor); |
| title->SetBackgroundColor(kTitleBackgroundColor); |
| title->set_border(MakePadding(kTextTopPadding, 0, 3, kTextRightPadding)); |
| - texts.push_back(title); |
| + text_views.push_back(title); |
| + } |
| + |
| + // List notification items up to a maximum if appropriate. |
| + int items = static_cast<int>(notification().items.size()); |
| + items = std::min(items, IsExpanded() ? kNotificationMaximumItems : 0); |
|
Jun Mukai
2013/02/26 02:23:30
as I wrote in _constants.h, I prefer to keep size_
dharcourt
2013/02/26 03:23:30
Done.
|
| + for (int i = 0; i < items; ++i) { |
| + ItemView* item = new ItemView(notification().items[i]); |
| + item->set_border(MakePadding(0, 0, 4, kTextRightPadding)); |
| + text_views.push_back(item); |
| } |
|
dharcourt
2013/02/23 04:32:00
Item display had to be moved up here so the displa
|
| // Message if appropriate. |
| - if (notification().items.size() == 0 && !notification().message.empty()) { |
| + if (items == 0 && !notification().message.empty()) { |
| views::Label* message = new views::Label(notification().message); |
| message->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| - message->SetMultiLine(true); |
| + if (IsExpanded()) |
| + message->SetMultiLine(true); |
| + else |
| + message->SetElideBehavior(views::Label::ELIDE_AT_END); |
| message->SetEnabledColor(kMessageColor); |
| message->SetBackgroundColor(kMessageBackgroundColor); |
| message->set_border(MakePadding(0, 0, 3, kTextRightPadding)); |
| - texts.push_back(message); |
| - } |
| - |
| - // List notification items up to a maximum. |
| - int items = std::min(notification().items.size(), kNotificationMaximumItems); |
| - for (int i = 0; i < items; ++i) { |
| - ItemView* item = new ItemView(notification().items[i]); |
| - item->set_border(MakePadding(0, 0, 4, kTextRightPadding)); |
| - texts.push_back(item); |
| + text_views.push_back(message); |
| } |
| // Set up the content view with a fixed-width icon column on the left and a |
| @@ -344,28 +432,28 @@ views::View* NotificationView::MakeContentView() { |
| // Create the first row and its icon view, which spans all the text views |
| // to its right as well as the padding view below them. |
| layout->StartRow(0, 0); |
| - views::ImageView* icon = new views::ImageView(); |
| - icon->SetImageSize(gfx::Size(message_center::kNotificationIconSize, |
| - message_center::kNotificationIconSize)); |
| - icon->SetImage(notification().primary_icon); |
| - icon->SetHorizontalAlignment(views::ImageView::LEADING); |
| - icon->SetVerticalAlignment(views::ImageView::LEADING); |
| - icon->set_border(MakePadding(0, 0, 0, kIconToTextPadding)); |
| - layout->AddView(icon, 1, texts.size() + 1); |
| + icon_view_ = new views::ImageView(); |
| + icon_view_->SetImageSize(gfx::Size(message_center::kNotificationIconSize, |
| + message_center::kNotificationIconSize)); |
| + icon_view_->SetImage(notification().primary_icon); |
| + icon_view_->SetHorizontalAlignment(views::ImageView::LEADING); |
| + icon_view_->SetVerticalAlignment(views::ImageView::LEADING); |
| + icon_view_->set_border(MakePadding(0, 0, 0, kIconToTextPadding)); |
| + layout->AddView(icon_view_, 1, text_views.size() + 1); |
| // Add the text views, creating rows for them if necessary. |
| - for (size_t i = 0; i < texts.size(); ++i) { |
| + for (size_t i = 0; i < text_views.size(); ++i) { |
| if (i > 0) { |
| layout->StartRow(0, 0); |
| layout->SkipColumns(1); |
| } |
| - layout->AddView(texts[i]); |
| + layout->AddView(text_views[i]); |
| } |
| // Add a text padding row if necessary. This adds some space between the last |
| // line of text and anything below it but it also ensures views above it are |
| // top-justified by expanding vertically to take up any extra space. |
| - if (texts.size() == 0) { |
| + if (text_views.size() == 0) { |
| layout->SkipColumns(1); |
| } else { |
| layout->StartRow(100, 0); |
| @@ -376,14 +464,11 @@ views::View* NotificationView::MakeContentView() { |
| } |
| // Add an image row if appropriate. |
| - if (!notification().image.isNull()) { |
| + if (IsExpanded() && !notification().image.isNull()) { |
| layout->StartRow(0, 0); |
| - views::ImageView* image = new ProportionalImageView(); |
| - image->SetImageSize(notification().image.size()); |
| - image->SetImage(notification().image); |
| - image->SetHorizontalAlignment(views::ImageView::CENTER); |
| - image->SetVerticalAlignment(views::ImageView::LEADING); |
| - layout->AddView(image, 2, 1); |
| + ProportionalImageView* image_view = new ProportionalImageView(); |
| + image_view->SetImage(notification().image); |
| + layout->AddView(image_view, 2, 1); |
| } |
| // Add action button rows. |