// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/message_center/message_view_multiple.h" #include "base/utf_string_conversions.h" #include "grit/ui_resources.h" #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/native_theme/native_theme.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" #include "ui/views/layout/grid_layout.h" namespace { // Notification dimensions. const int kNotificationIconSize = 64; const int kNotificationPadding1Width = 12; const int kNotificationColumn1Width = kNotificationIconSize; const int kNotificationPadding2Width = 12; const int kNotificationPadding3Width = 12; const int kNotificationColumn3Width = 26; const int kNotificationPadding4Width = 10; const int kNotificationColumn4Width = 8; const int kNotificationPadding5Width = 8; const int kNotificationColumn1Top = 12; const int kNotificationColumn2Top = 9; const int kNotificationColumn3Top = 4; const int kNotificationColumn4Top = 8; const int kNotificationPaddingBottom = 19; const int kNotificationItemInternalPadding = 12; // The text background colors below are used only to prevent view::Label from // modifying the text color and will never be used for drawing. See // view::Label's SetEnabledColor() and SetBackgroundColor() for details. const SkColor kNotificationBackgroundColor = SkColorSetRGB(254, 254, 254); const SkColor kNotificationReadBackgroundColor = SkColorSetRGB(250, 250, 250); const SkColor kNotificationTitleColor = SkColorSetRGB(68, 68, 68); const SkColor kNotificationTitleBackgroundColor = SK_ColorWHITE; const SkColor kNotificationMessageColor = SkColorSetRGB(136, 136, 136); const SkColor kNotificationMessageBackgroundColor = SK_ColorBLACK; const SkColor kNotificationTimeColor = SkColorSetRGB(176, 176, 176); const SkColor kNotificationTimeBackgroundColor = SK_ColorBLACK; const SkColor kNotificationItemTitleColor = SkColorSetRGB(68, 68, 68); const SkColor kNotificationItemMessageColor = SkColorSetRGB(136, 136, 136); const SkColor kNotificationSeparatorColor = SkColorSetRGB(226, 226, 226); // ItemViews are responsible for drawing each MessageViewMultiple item's title // and message next to each other within a single column. class ItemView : public views::View { public: ItemView(const message_center::NotificationList::NotificationItem& item) : item_(item), preferred_size_(0, 0) { gfx::Font font = GetDefaultFont(); preferred_size_.Enlarge(font.GetStringWidth(item.title), 0); preferred_size_.Enlarge(kNotificationItemInternalPadding, 0); preferred_size_.Enlarge(font.GetStringWidth(item.message), 0); preferred_size_.set_height(font.GetHeight()); PreferredSizeChanged(); SchedulePaint(); } // Overridden from views::View. virtual gfx::Size GetPreferredSize() OVERRIDE; virtual int GetBaseline() const OVERRIDE; virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE; virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; virtual bool GetTooltipText(const gfx::Point& point, string16* tooltip) const OVERRIDE; protected: // Overridden from views::View. virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; private: static gfx::Font GetDefaultFont(); message_center::NotificationList::NotificationItem item_; gfx::Size preferred_size_; DISALLOW_COPY_AND_ASSIGN(ItemView); }; gfx::Size ItemView::GetPreferredSize() { return preferred_size_; } int ItemView::GetBaseline() const { return GetDefaultFont().GetBaseline(); } bool ItemView::HitTestRect(const gfx::Rect& rect) const { return false; } void ItemView::GetAccessibleState( ui::AccessibleViewState* state) { state->role = ui::AccessibilityTypes::ROLE_STATICTEXT; state->state = ui::AccessibilityTypes::STATE_READONLY; state->name = item_.message; // TODO(dharcourt): Include title localizably. } bool ItemView::GetTooltipText(const gfx::Point& point, string16* tooltip) const { if (preferred_size_.width() > width()) { *tooltip = item_.message; // TODO(dharcourt): Include title localizably. return true; } return false; } void ItemView::OnPaint(gfx::Canvas* canvas) { OnPaintBackground(canvas); OnPaintBorder(canvas); OnPaintFocusBorder(canvas); gfx::Font font = GetDefaultFont(); int y = std::max(0, height() - preferred_size_.height()) / 2; canvas->DrawStringInt(item_.title, font, kNotificationItemTitleColor, 0, y, width(), preferred_size_.height()); int x = font.GetStringWidth(item_.title) + kNotificationItemInternalPadding; if (x < width()) { canvas->DrawStringInt( ui::ElideText(item_.message, font, width() - x, ui::ELIDE_AT_END), font, kNotificationItemMessageColor, x, y, width() - x, preferred_size_.height()); } } // static gfx::Font ItemView::GetDefaultFont() { return ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont); } // BoxView draws a color rectangle or just reserves some space. class BoxView : public views::View { public: BoxView(int width, int height, SkColor color=SkColorSetARGB(0, 0, 0, 0)) : size_(width, height) { if (SkColorGetA(color) > 0) set_background(views::Background::CreateSolidBackground(color)); PreferredSizeChanged(); SchedulePaint(); } // Overridden from View: virtual gfx::Size GetPreferredSize() OVERRIDE; virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE; private: gfx::Size size_; DISALLOW_COPY_AND_ASSIGN(BoxView); }; gfx::Size BoxView::GetPreferredSize() { return size_; } bool BoxView::HitTestRect(const gfx::Rect& rect) const { return false; } } // namespace namespace message_center { MessageViewMultiple::MessageViewMultiple( NotificationList::Delegate* list_delegate, const NotificationList::Notification& notification) : MessageView(list_delegate, notification) {} MessageViewMultiple::MessageViewMultiple() {} MessageViewMultiple::~MessageViewMultiple() {} // TODO(dharcourt): Make this a subclass of BaseFormatView or of a // BaseFormatView superclass and leverage that class' SetUpView(). void MessageViewMultiple::SetUpView() { SkColor background = notification_.is_read ? kNotificationReadBackgroundColor : kNotificationBackgroundColor; set_background(views::Background::CreateSolidBackground(background)); views::GridLayout* layout = new views::GridLayout(this); SetLayoutManager(layout); // Four columns (icon, messages, time, close) surrounded by padding. The icon, // time, and close columns have fixed width and the message column fills up // the remaining space. Inter-column padding is included within columns // whenever horizontal allignment allows for it. views::ColumnSet* columns = layout->AddColumnSet(0); columns->AddPaddingColumn(0, kNotificationPadding1Width); columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING, 0, views::GridLayout::FIXED, kNotificationColumn1Width, kNotificationColumn1Width); columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING, 0, views::GridLayout::FIXED, kNotificationPadding2Width, kNotificationPadding2Width); columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 100, views::GridLayout::USE_PREF, 0, 0); columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::LEADING, 0, views::GridLayout::FIXED, kNotificationPadding3Width + kNotificationColumn3Width, kNotificationPadding3Width + kNotificationColumn3Width); columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::LEADING, 0, views::GridLayout::FIXED, kNotificationPadding4Width + kNotificationColumn4Width, kNotificationPadding4Width + kNotificationColumn4Width); columns->AddPaddingColumn(0, kNotificationPadding5Width); // First row: Icon. layout->StartRow(0, 0); views::ImageView* icon = new views::ImageView; icon->SetImageSize(gfx::Size(kNotificationIconSize, kNotificationIconSize)); icon->SetImage(notification_.image); icon->set_border(CreateTopBorder(kNotificationColumn1Top)); icon->SetVerticalAlignment(views::ImageView::LEADING); layout->AddView(icon, 1, 3 + 2 * notification_.items.size()); // First row: Title. // TODO(dharcourt): Skip the title Label when there's no title text. layout->SkipColumns(1); views::Label* title = new views::Label(notification_.title); title->SetHorizontalAlignment(gfx::ALIGN_LEFT); title->SetFont(title->font().DeriveFont(4)); title->SetEnabledColor(kNotificationTitleColor); title->SetBackgroundColor(kNotificationTitleBackgroundColor); title->set_border(CreateTopBorder(kNotificationColumn2Top)); layout->AddView(title, 1, 2); // First row: Time. // TODO(dharcourt): Timestamp as "1m/5m/1h/5h/1d/5d/..." (ago). views::Label* time = new views::Label(UTF8ToUTF16("")); time->SetEnabledColor(kNotificationTimeColor); time->SetBackgroundColor(kNotificationTimeBackgroundColor); time->set_border(CreateTopBorder(kNotificationColumn3Top)); layout->AddView(time, 1, 2); // First row: Close button padding. layout->AddView(new BoxView(1, kNotificationColumn4Top)); // Second row: Close button, which has to be on a row of its own because it's // an ImageButton and so its padding can't be set using borders. This row is // set to resize vertically to ensure the first row stays at its minimum // height of kNotificationColumn4Top. layout->StartRow(100, 0); layout->SkipColumns(4); DCHECK(close_button_); layout->AddView(close_button_); // Two rows for each notification item, including appropriate padding. layout->AddPaddingRow(0, 3); std::vector::const_iterator i; for (i = notification_.items.begin(); i != notification_.items.end(); ++i) { layout->StartRowWithPadding(0, 0, 0, 3); layout->SkipColumns(2); layout->AddView(new ItemView(*i)); layout->SkipColumns(2); } // Two rows for padding and a horizontal separator line. layout->StartRowWithPadding(0, 0, 0, 6); layout->SkipColumns(1); layout->AddView(new BoxView(1000000, 1, kNotificationSeparatorColor), 4, 1); // Two rows for padding and the summary message and small icon. // TODO(dharcourt): Skip the message Label when there's no message text. // TODO(dharcourt): Add a small icon to the right of the summary message. layout->StartRowWithPadding(0, 0, 0, 6); layout->SkipColumns(2); views::Label* message = new views::Label(notification_.message); message->SetHorizontalAlignment(gfx::ALIGN_LEFT); message->SetMultiLine(true); message->SetEnabledColor(kNotificationMessageColor); message->SetBackgroundColor(kNotificationMessageBackgroundColor); layout->AddView(message); layout->SkipColumns(2); // Final row with the bottom padding. layout->AddPaddingRow(0, kNotificationPaddingBottom); } views::Border* MessageViewMultiple::CreateTopBorder(int height) { return views::Border::CreateEmptyBorder(height, 0, 0, 0); } } // namespace message_center