diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/ui/message_center/views | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) | |
download | qtwebengine-chromium-ab0a50979b9eb4dfa3320eff7e187e41efedf7a9.tar.gz |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/ui/message_center/views')
35 files changed, 1369 insertions, 1484 deletions
diff --git a/chromium/ui/message_center/views/bounded_label.cc b/chromium/ui/message_center/views/bounded_label.cc index 2b8423b6865..99c66f80b77 100644 --- a/chromium/ui/message_center/views/bounded_label.cc +++ b/chromium/ui/message_center/views/bounded_label.cc @@ -10,6 +10,7 @@ #include "base/strings/utf_string_conversions.h" #include "ui/gfx/canvas.h" #include "ui/gfx/text_elider.h" +#include "ui/gfx/text_utils.h" #include "ui/views/controls/label.h" namespace { @@ -24,7 +25,7 @@ namespace message_center { // InnerBoundedLabel is a views::Label subclass that does all of the work for // BoundedLabel. It is kept private to prevent outside code from calling a -// number of views::Label methods like setFont() that break BoundedLabel's +// number of views::Label methods like SetFontList() that break BoundedLabel's // caching but can't be overridden. // // TODO(dharcourt): Move the line limiting functionality to views::Label to make @@ -40,7 +41,10 @@ class InnerBoundedLabel : public views::Label { // Pass in a -1 width to use the preferred width, a -1 limit to skip limits. int GetLinesForWidthAndLimit(int width, int limit); gfx::Size GetSizeForWidthAndLines(int width, int lines); - std::vector<string16> GetWrappedText(int width, int lines); + std::vector<base::string16> GetWrappedText(int width, int lines); + + // Overridden from views::Label. + virtual void SetText(const base::string16& text) OVERRIDE; protected: // Overridden from views::Label. @@ -57,7 +61,7 @@ class InnerBoundedLabel : public views::Label { void SetCachedSize(std::pair<int, int> width_and_lines, gfx::Size size); const BoundedLabel* owner_; // Weak reference. - string16 wrapped_text_; + base::string16 wrapped_text_; int wrapped_text_width_; int wrapped_text_lines_; std::map<int, int> lines_cache_; @@ -108,8 +112,8 @@ gfx::Size InnerBoundedLabel::GetSizeForWidthAndLines(int width, int lines) { int text_width = (width < 0) ? std::numeric_limits<int>::max() : std::max(width - insets.width(), 0); int text_height = std::numeric_limits<int>::max(); - std::vector<string16> wrapped = GetWrappedText(text_width, lines); - gfx::Canvas::SizeStringInt(JoinString(wrapped, '\n'), font(), + std::vector<base::string16> wrapped = GetWrappedText(text_width, lines); + gfx::Canvas::SizeStringInt(JoinString(wrapped, '\n'), font_list(), &text_width, &text_height, owner_->GetLineHeight(), GetTextFlags()); @@ -120,28 +124,25 @@ gfx::Size InnerBoundedLabel::GetSizeForWidthAndLines(int width, int lines) { return size; } -std::vector<string16> InnerBoundedLabel::GetWrappedText(int width, int lines) { +std::vector<base::string16> InnerBoundedLabel::GetWrappedText(int width, + int lines) { // Short circuit simple case. if (width == 0 || lines == 0) - return std::vector<string16>(); + return std::vector<base::string16>(); // Restrict line limit to ensure (lines + 1) * line_height <= INT_MAX and // use it to calculate a reasonable text height. int height = std::numeric_limits<int>::max(); if (lines > 0) { - int line_height = std::max(font().GetHeight(), 2); // At least 2 pixels. + int line_height = std::max(font_list().GetHeight(), + 2); // At least 2 pixels. int max_lines = std::numeric_limits<int>::max() / line_height - 1; lines = std::min(lines, max_lines); height = (lines + 1) * line_height; } - // Try to ensure that the width is no smaller than the width of the text's - // characters to avoid the http://crbug.com/237700 infinite loop. - // TODO(dharcourt): Remove when http://crbug.com/237700 is fixed. - width = std::max(width, 2 * font().GetStringWidth(UTF8ToUTF16("W"))); - // Wrap, using INT_MAX for -1 widths that indicate no wrapping. - std::vector<string16> wrapped; + std::vector<base::string16> wrapped; gfx::ElideRectangleText(text(), font_list(), (width < 0) ? std::numeric_limits<int>::max() : width, height, gfx::WRAP_LONG_WORDS, &wrapped); @@ -151,9 +152,10 @@ std::vector<string16> InnerBoundedLabel::GetWrappedText(int width, int lines) { // Add an ellipsis to the last line. If this ellipsis makes the last line // too wide, that line will be further elided by the gfx::ElideText below, // so for example "ABC" could become "ABC..." and then "AB...". - string16 last = wrapped[lines - 1] + UTF8ToUTF16(gfx::kEllipsis); - if (width > 0 && font().GetStringWidth(last) > width) - last = gfx::ElideText(last, font(), width, gfx::ELIDE_AT_END); + base::string16 last = + wrapped[lines - 1] + base::UTF8ToUTF16(gfx::kEllipsis); + if (width > 0 && gfx::GetStringWidth(last, font_list()) > width) + last = gfx::ElideText(last, font_list(), width, gfx::ELIDE_TAIL); wrapped.resize(lines - 1); wrapped.push_back(last); } @@ -184,6 +186,11 @@ void InnerBoundedLabel::OnPaint(gfx::Canvas* canvas) { } } +void InnerBoundedLabel::SetText(const base::string16& text) { + views::Label::SetText(text); + ClearCaches(); +} + int InnerBoundedLabel::GetTextFlags() { int flags = gfx::Canvas::MULTI_LINE | gfx::Canvas::CHARACTER_BREAK; @@ -191,8 +198,11 @@ int InnerBoundedLabel::GetTextFlags() { if (SkColorGetA(background_color()) != 0xFF) flags |= gfx::Canvas::NO_SUBPIXEL_RENDERING; - if (directionality_mode() == - views::Label::AUTO_DETECT_DIRECTIONALITY) { + if (directionality_mode() == gfx::DIRECTIONALITY_FORCE_LTR) { + flags |= gfx::Canvas::FORCE_LTR_DIRECTIONALITY; + } else if (directionality_mode() == gfx::DIRECTIONALITY_FORCE_RTL) { + flags |= gfx::Canvas::FORCE_RTL_DIRECTIONALITY; + } else if (directionality_mode() == gfx::DIRECTIONALITY_FROM_TEXT) { base::i18n::TextDirection direction = base::i18n::GetFirstStrongCharacterDirection(text()); if (direction == base::i18n::RIGHT_TO_LEFT) @@ -257,14 +267,15 @@ void InnerBoundedLabel::SetCachedSize(std::pair<int, int> width_and_lines, // BoundedLabel /////////////////////////////////////////////////////////// -BoundedLabel::BoundedLabel(const string16& text, const gfx::FontList& font_list) +BoundedLabel::BoundedLabel(const base::string16& text, + const gfx::FontList& font_list) : line_limit_(-1) { label_.reset(new InnerBoundedLabel(*this)); label_->SetFontList(font_list); label_->SetText(text); } -BoundedLabel::BoundedLabel(const string16& text) +BoundedLabel::BoundedLabel(const base::string16& text) : line_limit_(-1) { label_.reset(new InnerBoundedLabel(*this)); label_->SetText(text); @@ -286,6 +297,10 @@ void BoundedLabel::SetLineLimit(int lines) { line_limit_ = std::max(lines, -1); } +void BoundedLabel::SetText(const base::string16& text) { + label_->SetText(text); +} + int BoundedLabel::GetLineHeight() const { return label_->line_height(); } @@ -307,25 +322,25 @@ int BoundedLabel::GetBaseline() const { return label_->GetBaseline(); } -gfx::Size BoundedLabel::GetPreferredSize() { +gfx::Size BoundedLabel::GetPreferredSize() const { return visible() ? label_->GetSizeForWidthAndLines(-1, -1) : gfx::Size(); } -int BoundedLabel::GetHeightForWidth(int width) { +int BoundedLabel::GetHeightForWidth(int width) const { return visible() ? label_->GetSizeForWidthAndLines(width, line_limit_).height() : 0; } -void BoundedLabel::Paint(gfx::Canvas* canvas) { +void BoundedLabel::Paint(gfx::Canvas* canvas, const views::CullSet& cull_set) { if (visible()) - label_->Paint(canvas); + label_->Paint(canvas, cull_set); } -bool BoundedLabel::HitTestRect(const gfx::Rect& rect) const { - return label_->HitTestRect(rect); +bool BoundedLabel::CanProcessEventsWithinSubtree() const { + return label_->CanProcessEventsWithinSubtree(); } -void BoundedLabel::GetAccessibleState(ui::AccessibleViewState* state) { +void BoundedLabel::GetAccessibleState(ui::AXViewState* state) { label_->GetAccessibleState(state); } @@ -338,7 +353,7 @@ void BoundedLabel::OnNativeThemeChanged(const ui::NativeTheme* theme) { label_->SetNativeTheme(theme); } -string16 BoundedLabel::GetWrappedTextForTest(int width, int lines) { +base::string16 BoundedLabel::GetWrappedTextForTest(int width, int lines) { return JoinString(label_->GetWrappedText(width, lines), '\n'); } diff --git a/chromium/ui/message_center/views/bounded_label.h b/chromium/ui/message_center/views/bounded_label.h index d6b2f49a519..9a9e6d4bc4c 100644 --- a/chromium/ui/message_center/views/bounded_label.h +++ b/chromium/ui/message_center/views/bounded_label.h @@ -33,13 +33,14 @@ class BoundedLabelTest; // bounded_label.cc file for details. class MESSAGE_CENTER_EXPORT BoundedLabel : public views::View { public: - BoundedLabel(const string16& text, const gfx::FontList& font_list); - BoundedLabel(const string16& text); + BoundedLabel(const base::string16& text, const gfx::FontList& font_list); + BoundedLabel(const base::string16& text); virtual ~BoundedLabel(); void SetColors(SkColor textColor, SkColor backgroundColor); void SetLineHeight(int height); // Pass in 0 for default height. void SetLineLimit(int lines); // Pass in -1 for no limit. + void SetText(const base::string16& text); // Additionally clears caches. int GetLineHeight() const; int GetLineLimit() const; @@ -48,13 +49,14 @@ class MESSAGE_CENTER_EXPORT BoundedLabel : public views::View { int GetLinesForWidthAndLimit(int width, int limit); gfx::Size GetSizeForWidthAndLines(int width, int lines); - // Overridden from views::View. + // views::View: virtual int GetBaseline() const OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual int GetHeightForWidth(int width) OVERRIDE; - virtual void Paint(gfx::Canvas* canvas) OVERRIDE; - virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual int GetHeightForWidth(int width) const OVERRIDE; + virtual void Paint(gfx::Canvas* canvas, + const views::CullSet& cull_set) OVERRIDE; + virtual bool CanProcessEventsWithinSubtree() const OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; protected: // Overridden from views::View. @@ -64,7 +66,7 @@ class MESSAGE_CENTER_EXPORT BoundedLabel : public views::View { private: friend class test::BoundedLabelTest; - string16 GetWrappedTextForTest(int width, int lines); + base::string16 GetWrappedTextForTest(int width, int lines); scoped_ptr<InnerBoundedLabel> label_; int line_limit_; diff --git a/chromium/ui/message_center/views/bounded_label_unittest.cc b/chromium/ui/message_center/views/bounded_label_unittest.cc index bdfb0aceeb6..90452e2e1e3 100644 --- a/chromium/ui/message_center/views/bounded_label_unittest.cc +++ b/chromium/ui/message_center/views/bounded_label_unittest.cc @@ -23,9 +23,9 @@ namespace test { class BoundedLabelTest : public testing::Test { public: BoundedLabelTest() { - digit_pixels_ = gfx::GetStringWidth(UTF8ToUTF16("0"), font_list_); - space_pixels_ = gfx::GetStringWidth(UTF8ToUTF16(" "), font_list_); - ellipsis_pixels_ = gfx::GetStringWidth(UTF8ToUTF16("\xE2\x80\xA6"), + digit_pixels_ = gfx::GetStringWidth(base::UTF8ToUTF16("0"), font_list_); + space_pixels_ = gfx::GetStringWidth(base::UTF8ToUTF16(" "), font_list_); + ellipsis_pixels_ = gfx::GetStringWidth(base::UTF8ToUTF16("\xE2\x80\xA6"), font_list_); } @@ -35,10 +35,10 @@ class BoundedLabelTest : public testing::Test { // with an ellipses character (UTF8 "\xE2\x80\xA6") and returns a string16 // with the results. This allows test strings to be specified as ASCII const // char* strings, making tests more readable and easier to write. - string16 ToString(const char* string) { - const string16 periods = UTF8ToUTF16("..."); - const string16 ellipses = UTF8ToUTF16("\xE2\x80\xA6"); - string16 result = UTF8ToUTF16(string); + base::string16 ToString(const char* string) { + const base::string16 periods = base::UTF8ToUTF16("..."); + const base::string16 ellipses = base::UTF8ToUTF16("\xE2\x80\xA6"); + base::string16 result = base::UTF8ToUTF16(string); ReplaceSubstringsAfterOffset(&result, 0, periods, ellipses); return result; } @@ -58,7 +58,7 @@ class BoundedLabelTest : public testing::Test { } // Exercise BounderLabel::GetWrappedText() using the fixture's test label. - string16 GetWrappedText(int width) { + base::string16 GetWrappedText(int width) { return label_->GetWrappedTextForTest(width, lines_); } @@ -71,7 +71,7 @@ class BoundedLabelTest : public testing::Test { protected: // Creates a label to test with. Returns this fixture, which can be used to // test the newly created label using the exercise methods above. - BoundedLabelTest& Label(string16 text, int lines) { + BoundedLabelTest& Label(base::string16 text, int lines) { lines_ = lines; label_.reset(new BoundedLabel(text, font_list_)); label_->SetLineLimit(lines_); diff --git a/chromium/ui/message_center/views/constants.h b/chromium/ui/message_center/views/constants.h index d03ec7f0b79..f2995dd45b5 100644 --- a/chromium/ui/message_center/views/constants.h +++ b/chromium/ui/message_center/views/constants.h @@ -31,8 +31,7 @@ const int kButtonTitleTopPadding = 0; // Character limits: Displayed text will be subject to the line limits above, // but we also remove trailing characters from text to reduce processing cost. // Character limit = pixels per line * line limit / min. pixels per character. -const size_t kTitleCharacterLimit = - message_center::kNotificationWidth * message_center::kTitleLineLimit / 4; +const int kMinPixelsPerTitleCharacter = 4; const size_t kMessageCharacterLimit = message_center::kNotificationWidth * message_center::kMessageExpandedLineLimit / 3; diff --git a/chromium/ui/message_center/views/group_view.cc b/chromium/ui/message_center/views/group_view.cc deleted file mode 100644 index 384c2d2e7b4..00000000000 --- a/chromium/ui/message_center/views/group_view.cc +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2013 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/views/group_view.h" - -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "grit/ui_resources.h" -#include "grit/ui_strings.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/base/layout.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/size.h" -#include "ui/gfx/skia_util.h" -#include "ui/gfx/text_elider.h" -#include "ui/message_center/message_center.h" -#include "ui/message_center/message_center_style.h" -#include "ui/message_center/message_center_tray.h" -#include "ui/message_center/message_center_util.h" -#include "ui/message_center/notification.h" -#include "ui/message_center/views/bounded_label.h" -#include "ui/message_center/views/constants.h" -#include "ui/message_center/views/notification_button.h" -#include "ui/message_center/views/proportional_image_view.h" -#include "ui/native_theme/native_theme.h" -#include "ui/views/background.h" -#include "ui/views/border.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/box_layout.h" -#include "ui/views/layout/fill_layout.h" -#include "ui/views/widget/widget.h" - -#if defined(USE_AURA) -#include "ui/base/cursor/cursor.h" -#endif - -namespace { - -// static -views::Border* MakeTextBorder(int padding, int top, int bottom) { - // Split the padding between the top and the bottom, then add the extra space. - return views::Border::CreateEmptyBorder(padding / 2 + top, - message_center::kTextLeftPadding, - (padding + 1) / 2 + bottom, - message_center::kTextRightPadding); -} - -} // namespace - -namespace message_center { - -// GroupView //////////////////////////////////////////////////////////// - -GroupView::GroupView(MessageCenterController* controller, - const NotifierId& notifier_id, - const Notification& last_notification, - const gfx::ImageSkia& group_icon, - int group_size) - : MessageView(this, - last_notification.id(), - notifier_id, - last_notification.display_source()), - controller_(controller), - notifier_id_(notifier_id), - display_source_(last_notification.display_source()), - group_icon_(group_icon), - group_size_(group_size), - last_notification_id_(last_notification.id()), - background_view_(NULL), - top_view_(NULL), - bottom_view_(NULL), - title_view_(NULL), - message_view_(NULL), - context_message_view_(NULL), - icon_view_(NULL) -{ - std::vector<string16> accessible_lines; - // TODO (dimich): move to MessageView - // Create the opaque background that's above the view's shadow. - background_view_ = new views::View(); - background_view_->set_background( - views::Background::CreateSolidBackground( - message_center::kNotificationBackgroundColor)); - - // Create the top_view_, which collects into a vertical box all content - // at the top of the notification (to the right of the icon) except for the - // close button. - top_view_ = new views::View(); - top_view_->SetLayoutManager( - new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); - top_view_->set_border(views::Border::CreateEmptyBorder( - kTextTopPadding - 8, 0, kTextBottomPadding - 5, 0)); - - const gfx::FontList default_label_font_list = views::Label().font_list(); - - // Create the title view if appropriate. - const gfx::FontList& font_list = - default_label_font_list.DeriveFontListWithSizeDelta(2); - int padding = kTitleLineHeight - font_list.GetHeight(); - - title_view_ = new BoundedLabel( - gfx::TruncateString(string16(last_notification.title()), - kTitleCharacterLimit), - font_list); - accessible_lines.push_back(last_notification.title()); - title_view_->SetLineHeight(kTitleLineHeight); - title_view_->SetLineLimit(message_center::kTitleLineLimit); - title_view_->SetColors(message_center::kRegularTextColor, - kRegularTextBackgroundColor); - title_view_->set_border(MakeTextBorder(padding, 3, 0)); - top_view_->AddChildView(title_view_); - - // Create the message view if appropriate. - if (!last_notification.message().empty()) { - int padding = kMessageLineHeight - default_label_font_list.GetHeight(); - message_view_ = new BoundedLabel( - gfx::TruncateString(last_notification.message(), - kMessageCharacterLimit)); - message_view_->SetLineHeight(kMessageLineHeight); - message_view_->SetColors(message_center::kRegularTextColor, - kDimTextBackgroundColor); - message_view_->set_border(MakeTextBorder(padding, 4, 0)); - top_view_->AddChildView(message_view_); - accessible_lines.push_back(last_notification.message()); - } - - // Create the context message view if appropriate. - if (!last_notification.context_message().empty()) { - int padding = kMessageLineHeight - default_label_font_list.GetHeight(); - context_message_view_ = new BoundedLabel(gfx::TruncateString( - last_notification.context_message(), kContextMessageCharacterLimit), - default_label_font_list); - context_message_view_->SetLineLimit( - message_center::kContextMessageLineLimit); - context_message_view_->SetLineHeight(kMessageLineHeight); - context_message_view_->SetColors(message_center::kDimTextColor, - kContextTextBackgroundColor); - context_message_view_->set_border(MakeTextBorder(padding, 4, 0)); - top_view_->AddChildView(context_message_view_); - accessible_lines.push_back(last_notification.context_message()); - } - - // Create the notification icon view. - icon_view_ = - new ProportionalImageView(last_notification.icon().AsImageSkia()); - icon_view_->set_background(views::Background::CreateSolidBackground( - kIconBackgroundColor)); - - // Create the bottom_view_, which collects into a vertical box all content - // below the notification icon except for the expandGroup button. - bottom_view_ = new views::View(); - bottom_view_->SetLayoutManager( - new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); - - // Create "N more.." action button - views::View* separator = new views::ImageView(); - separator->set_border(views::Border::CreateSolidSidedBorder( - 1, 0, 0, 0, kButtonSeparatorColor)); - bottom_view_->AddChildView(separator); - more_button_ = new NotificationButton(this); - string16 button_title = - l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_MORE_FROM, - base::IntToString16(group_size_), - display_source_); - more_button_->SetTitle(button_title); - more_button_->SetIcon(group_icon_); - bottom_view_->AddChildView(more_button_); - - // Put together the different content and control views. Layering those allows - // for proper layout logic and it also allows the close button to - // overlap the content as needed to provide large enough click and touch area. - AddChildView(background_view_); - AddChildView(top_view_); - AddChildView(icon_view_); - AddChildView(bottom_view_); - AddChildView(close_button()); - set_accessible_name(JoinString(accessible_lines, '\n')); -} - -GroupView::~GroupView() { -} - -gfx::Size GroupView::GetPreferredSize() { - int top_width = top_view_->GetPreferredSize().width(); - int bottom_width = bottom_view_->GetPreferredSize().width(); - int preferred_width = std::max(top_width, bottom_width) + GetInsets().width(); - return gfx::Size(preferred_width, GetHeightForWidth(preferred_width)); -} - -int GroupView::GetHeightForWidth(int width) { - int content_width = width - GetInsets().width(); - int top_height = top_view_->GetHeightForWidth(content_width); - int bottom_height = bottom_view_->GetHeightForWidth(content_width); - int content_height = std::max(top_height, kIconSize) + bottom_height; - - // Adjust the height to make sure there is at least 16px of space below the - // icon if there is any space there (<http://crbug.com/232966>). - if (content_height > kIconSize) - content_height = std::max(content_height, - kIconSize + message_center::kIconBottomPadding); - - return content_height + GetInsets().height(); -} - -void GroupView::Layout() { - gfx::Insets insets = GetInsets(); - int content_width = width() - insets.width(); - int content_right = width() - insets.right(); - - // Background. - background_view_->SetBounds(insets.left(), insets.top(), - content_width, height() - insets.height()); - - // Top views. - int top_height = top_view_->GetHeightForWidth(content_width); - top_view_->SetBounds(insets.left(), insets.top(), content_width, top_height); - - // Icon. - icon_view_->SetBounds(insets.left(), insets.top(), kIconSize, kIconSize); - - // Bottom views. - int bottom_y = insets.top() + std::max(top_height, kIconSize); - int bottom_height = bottom_view_->GetHeightForWidth(content_width); - bottom_view_->SetBounds(insets.left(), bottom_y, - content_width, bottom_height); - - // Close button. - gfx::Size close_size(close_button()->GetPreferredSize()); - close_button()->SetBounds(content_right - close_size.width(), insets.top(), - close_size.width(), close_size.height()); -} - -void GroupView::OnFocus() { - MessageView::OnFocus(); - ScrollRectToVisible(GetLocalBounds()); -} - -gfx::NativeCursor GroupView::GetCursor(const ui::MouseEvent& event) { -// If we ever have non-Aura views environment, this will fail compilation. -#if defined(USE_AURA) - return ui::kCursorHand; -#endif -} - -void GroupView::ButtonPressed(views::Button* sender, - const ui::Event& event) { - if (sender == more_button_) { - controller_->ExpandGroup(notifier_id_); - return; - } - // Let the superclass handle anything other than action buttons. - // Warning: This may cause the GroupView itself to be deleted, - // so don't do anything afterwards. - MessageView::ButtonPressed(sender, event); -} - -void GroupView::ClickOnNotification(const std::string& notification_id) { - controller_->GroupBodyClicked(notification_id); -} - -void GroupView::RemoveNotification(const std::string& notification_id, - bool by_user) { - controller_->RemoveGroup(notifier_id_); -} - -void GroupView::DisableNotificationsFromThisSource( - const NotifierId& notifier_id) { - controller_->DisableNotificationsFromThisSource(notifier_id); -} - -void GroupView::ShowNotifierSettingsBubble() { - controller_->ShowNotifierSettingsBubble(); -} - -} // namespace message_center diff --git a/chromium/ui/message_center/views/group_view.h b/chromium/ui/message_center/views/group_view.h deleted file mode 100644 index d2746a2546c..00000000000 --- a/chromium/ui/message_center/views/group_view.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2013 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. - -#ifndef UI_MESSAGE_CENTER_VIEWS_GROUP_VIEW_H_ -#define UI_MESSAGE_CENTER_VIEWS_GROUP_VIEW_H_ - -#include "ui/gfx/image/image_skia.h" -#include "ui/message_center/views/message_center_controller.h" -#include "ui/message_center/views/message_center_view.h" -#include "ui/message_center/views/message_view.h" - -namespace message_center { - -class BoundedLabel; -class MessageCenter; -class NotificationButton; - -// View that displays a placeholder representing several notifications and a -// button to expand it into a set of individual notifications. -class GroupView : public MessageView, public MessageViewController { - public: - // The group view currently shows the latest notificaiton w/o buttons - // and a single button like "N more from XYZ". - // Each GroupView has a notifier_id which it uses to report clicks and other - // user actions to Client. - GroupView(MessageCenterController* controller, - const NotifierId& notifier_id, - const Notification& last_notification, - const gfx::ImageSkia& group_icon, - int group_size); - - virtual ~GroupView(); - - // Overridden from views::View: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual int GetHeightForWidth(int width) OVERRIDE; - virtual void Layout() OVERRIDE; - virtual void OnFocus() OVERRIDE; - virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) OVERRIDE; - - // Overridden from MessageView: - virtual void ButtonPressed(views::Button* sender, - const ui::Event& event) OVERRIDE; - - // Overridden from MessageViewController: - virtual void ClickOnNotification(const std::string& notification_id) OVERRIDE; - virtual void RemoveNotification(const std::string& notification_id, - bool by_user) OVERRIDE; - virtual void DisableNotificationsFromThisSource( - const NotifierId& notifier_id) OVERRIDE; - virtual void ShowNotifierSettingsBubble() OVERRIDE; - - private: - MessageCenterController* controller_; // Weak, controls lifetime of views. - NotifierId notifier_id_; - string16 display_source_; - gfx::ImageSkia group_icon_; - int group_size_; - std::string last_notification_id_; - - // Weak references to GroupView descendants owned by their parents. - views::View* background_view_; - views::View* top_view_; - views::View* bottom_view_; - BoundedLabel* title_view_; - BoundedLabel* message_view_; - BoundedLabel* context_message_view_; - views::View* icon_view_; - NotificationButton* more_button_; - - DISALLOW_COPY_AND_ASSIGN(GroupView); -}; - -} // namespace message_center - -#endif // UI_MESSAGE_CENTER_VIEWS_GROUP_VIEW_H_ diff --git a/chromium/ui/message_center/views/message_bubble_base.cc b/chromium/ui/message_center/views/message_bubble_base.cc index e517c4af24d..c230c2d04ce 100644 --- a/chromium/ui/message_center/views/message_bubble_base.cc +++ b/chromium/ui/message_center/views/message_bubble_base.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "ui/message_center/message_center_style.h" -#include "ui/message_center/message_center_util.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" diff --git a/chromium/ui/message_center/views/message_center_bubble.cc b/chromium/ui/message_center/views/message_center_bubble.cc index 5886531b2db..ed93d7fa0c8 100644 --- a/chromium/ui/message_center/views/message_center_bubble.cc +++ b/chromium/ui/message_center/views/message_center_bubble.cc @@ -4,8 +4,9 @@ #include "ui/message_center/views/message_center_bubble.h" +#include "grit/ui_strings.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/message_center/message_center_style.h" -#include "ui/message_center/message_center_util.h" #include "ui/message_center/views/message_center_view.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/widget/widget.h" @@ -21,7 +22,7 @@ class ContentsView : public views::View { virtual ~ContentsView(); // Overridden from views::View: - virtual int GetHeightForWidth(int width) OVERRIDE; + virtual int GetHeightForWidth(int width) const OVERRIDE; protected: // Overridden from views::View: @@ -42,7 +43,7 @@ ContentsView::ContentsView(MessageCenterBubble* bubble, views::View* contents) ContentsView::~ContentsView() { } -int ContentsView::GetHeightForWidth(int width) { +int ContentsView::GetHeightForWidth(int width) const { DCHECK_EQ(1, child_count()); int contents_width = std::max(width - GetInsets().width(), 0); int contents_height = child_at(0)->GetHeightForWidth(contents_width); @@ -63,7 +64,8 @@ MessageCenterBubble::MessageCenterBubble(MessageCenter* message_center, : MessageBubbleBase(message_center, tray), message_center_view_(NULL), initially_settings_visible_(false), - first_item_has_no_margin_(first_item_has_no_margin) { + first_item_has_no_margin_(first_item_has_no_margin), + title_(l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_FOOTER_TITLE)) { } MessageCenterBubble::~MessageCenterBubble() { @@ -97,8 +99,9 @@ void MessageCenterBubble::InitializeContents( tray(), max_height(), initially_settings_visible_, - false /* MessageCenterBubble should be used only on ChromeOS. - Message center is never shown top down in ChromeOS. */); + false, /* MessageCenterBubble should be used only on ChromeOS. + Message center is never shown top down in ChromeOS. */ + title_); bubble_view()->AddChildView(new ContentsView(this, message_center_view_)); // Resize the content of the bubble view to the given bubble size. This is // necessary in case of the bubble border forcing a bigger size then the diff --git a/chromium/ui/message_center/views/message_center_bubble.h b/chromium/ui/message_center/views/message_center_bubble.h index 521bf4ac8e2..1173a95393b 100644 --- a/chromium/ui/message_center/views/message_center_bubble.h +++ b/chromium/ui/message_center/views/message_center_bubble.h @@ -55,6 +55,8 @@ class MESSAGE_CENTER_EXPORT MessageCenterBubble // the used anchor. bool first_item_has_no_margin_; + base::string16 title_; + DISALLOW_COPY_AND_ASSIGN(MessageCenterBubble); }; diff --git a/chromium/ui/message_center/views/message_center_button_bar.cc b/chromium/ui/message_center/views/message_center_button_bar.cc index 0816ffa29b1..8008115e705 100644 --- a/chromium/ui/message_center/views/message_center_button_bar.cc +++ b/chromium/ui/message_center/views/message_center_button_bar.cc @@ -13,6 +13,7 @@ #include "ui/gfx/text_constants.h" #include "ui/message_center/message_center.h" #include "ui/message_center/message_center_style.h" +#include "ui/message_center/message_center_tray.h" #include "ui/message_center/notifier_settings.h" #include "ui/message_center/views/message_center_view.h" #include "ui/views/controls/button/button.h" @@ -50,7 +51,7 @@ class NotificationCenterButton : public views::ToggleImageButton { protected: // Overridden from views::View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; private: gfx::Size size_; @@ -82,7 +83,7 @@ NotificationCenterButton::NotificationCenterButton( gfx::Insets(1, 2, 2, 2))); } -gfx::Size NotificationCenterButton::GetPreferredSize() { return size_; } +gfx::Size NotificationCenterButton::GetPreferredSize() const { return size_; } // MessageCenterButtonBar ///////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -90,17 +91,20 @@ MessageCenterButtonBar::MessageCenterButtonBar( MessageCenterView* message_center_view, MessageCenter* message_center, NotifierSettingsProvider* notifier_settings_provider, - bool settings_initially_visible) + bool settings_initially_visible, + const base::string16& title) : message_center_view_(message_center_view), message_center_(message_center), +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + close_bubble_button_(NULL), +#endif title_arrow_(NULL), notification_label_(NULL), button_container_(NULL), close_all_button_(NULL), settings_button_(NULL), quiet_mode_button_(NULL) { - if (get_use_acceleration_when_possible()) - SetPaintToLayer(true); + SetPaintToLayer(true); set_background( views::Background::CreateSolidBackground(kMessageCenterBackgroundColor)); @@ -117,11 +121,7 @@ MessageCenterButtonBar::MessageCenterButtonBar( title_arrow_->SetFocusable(false); AddChildView(title_arrow_); - gfx::Font notification_label_font = - ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont); - notification_label_ = new views::Label( - l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_FOOTER_TITLE), - notification_label_font); + notification_label_ = new views::Label(title); notification_label_->SetAutoColorReadabilityEnabled(false); notification_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); notification_label_->SetEnabledColor(kRegularTextColor); @@ -170,6 +170,20 @@ MessageCenterButtonBar::MessageCenterButtonBar( IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL); button_container_->AddChildView(settings_button_); +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + close_bubble_button_ = new views::ImageButton(this); + close_bubble_button_->SetImage( + views::Button::STATE_NORMAL, + resource_bundle.GetImageSkiaNamed(IDR_NOTIFICATION_BUBBLE_CLOSE)); + close_bubble_button_->SetImage( + views::Button::STATE_HOVERED, + resource_bundle.GetImageSkiaNamed(IDR_NOTIFICATION_BUBBLE_CLOSE_HOVER)); + close_bubble_button_->SetImage( + views::Button::STATE_PRESSED, + resource_bundle.GetImageSkiaNamed(IDR_NOTIFICATION_BUBBLE_CLOSE_PRESSED)); + AddChildView(close_bubble_button_); +#endif + SetCloseAllButtonEnabled(!settings_initially_visible); SetBackArrowVisible(settings_initially_visible); ViewVisibilityChanged(); @@ -218,11 +232,24 @@ void MessageCenterButtonBar::ViewVisibilityChanged() { 0, 0); +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + // The close-bubble button. + column->AddColumn(views::GridLayout::LEADING, + views::GridLayout::LEADING, + 0.0f, + views::GridLayout::USE_PREF, + 0, + 0); +#endif + layout->StartRow(0, 0); if (title_arrow_->visible()) layout->AddView(title_arrow_); layout->AddView(notification_label_); layout->AddView(button_container_); +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + layout->AddView(close_bubble_button_); +#endif } MessageCenterButtonBar::~MessageCenterButtonBar() {} @@ -263,6 +290,10 @@ void MessageCenterButtonBar::ButtonPressed(views::Button* sender, else message_center()->EnterQuietModeWithExpire(base::TimeDelta::FromDays(1)); quiet_mode_button_->SetToggled(message_center()->IsQuietMode()); +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + } else if (sender == close_bubble_button_) { + message_center_view()->tray()->HideMessageCenterBubble(); +#endif } else { NOTREACHED(); } diff --git a/chromium/ui/message_center/views/message_center_button_bar.h b/chromium/ui/message_center/views/message_center_button_bar.h index 8190e3e2cc2..bc87dbed44e 100644 --- a/chromium/ui/message_center/views/message_center_button_bar.h +++ b/chromium/ui/message_center/views/message_center_button_bar.h @@ -30,7 +30,8 @@ class MessageCenterButtonBar : public views::View, MessageCenterButtonBar(MessageCenterView* message_center_view, MessageCenter* message_center, NotifierSettingsProvider* notifier_settings_provider, - bool settings_initially_visible); + bool settings_initially_visible, + const base::string16& title); virtual ~MessageCenterButtonBar(); // Enables or disables all of the buttons in the center. This is used to @@ -63,6 +64,15 @@ class MessageCenterButtonBar : public views::View, MessageCenterView* message_center_view_; // Weak reference. MessageCenter* message_center_; // Weak reference. + // |close_bubble_button_| closes the message center bubble. This is required + // for desktop Linux because the status icon doesn't toggle the bubble, and + // close-on-deactivation is off. This is a tentative solution. Once pkotwicz + // Fixes the problem of focus-follow-mouse, close-on-deactivation will be + // back and this field will be removed. See crbug.com/319516. +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + views::ImageButton* close_bubble_button_; +#endif + // Sub-views of the button bar. NotificationCenterButton* title_arrow_; views::Label* notification_label_; diff --git a/chromium/ui/message_center/views/message_center_controller.h b/chromium/ui/message_center/views/message_center_controller.h index c680c0eedb1..df413555b70 100644 --- a/chromium/ui/message_center/views/message_center_controller.h +++ b/chromium/ui/message_center/views/message_center_controller.h @@ -7,6 +7,9 @@ #include <string> +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "ui/base/models/menu_model.h" #include "ui/message_center/notifier_settings.h" namespace message_center { @@ -20,18 +23,14 @@ class MessageCenterController { virtual void ClickOnNotification(const std::string& notification_id) = 0; virtual void RemoveNotification(const std::string& notification_id, bool by_user) = 0; - virtual void DisableNotificationsFromThisSource( - const NotifierId& notifier_id) = 0; - virtual void ShowNotifierSettingsBubble() = 0; + virtual scoped_ptr<ui::MenuModel> CreateMenuModel( + const NotifierId& notifier_id, + const base::string16& display_source) = 0; virtual bool HasClickedListener(const std::string& notification_id) = 0; virtual void ClickOnNotificationButton(const std::string& notification_id, int button_index) = 0; - virtual void ExpandNotification(const std::string& notification_id) = 0; - virtual void GroupBodyClicked(const std::string& last_notification_id) = 0; - virtual void ExpandGroup(const NotifierId& notifier_id) = 0; - virtual void RemoveGroup(const NotifierId& notifier_id) = 0; }; } // namespace message_center -#endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_CONTROLLER_H_
\ No newline at end of file +#endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_CONTROLLER_H_ diff --git a/chromium/ui/message_center/views/message_center_view.cc b/chromium/ui/message_center/views/message_center_view.cc index ebdbf33b2d6..0f003ed050b 100644 --- a/chromium/ui/message_center/views/message_center_view.cc +++ b/chromium/ui/message_center/views/message_center_view.cc @@ -24,10 +24,9 @@ #include "ui/message_center/message_center_style.h" #include "ui/message_center/message_center_tray.h" #include "ui/message_center/message_center_types.h" -#include "ui/message_center/message_center_util.h" -#include "ui/message_center/views/group_view.h" #include "ui/message_center/views/message_center_button_bar.h" #include "ui/message_center/views/message_view.h" +#include "ui/message_center/views/message_view_context_menu_controller.h" #include "ui/message_center/views/notification_view.h" #include "ui/message_center/views/notifier_settings_view.h" #include "ui/views/animation/bounds_animator.h" @@ -51,80 +50,19 @@ const SkColor kNoNotificationsTextColor = SkColorSetRGB(0xb4, 0xb4, 0xb4); const SkColor kTransparentColor = SkColorSetARGB(0, 0, 0, 0); #endif const int kAnimateClearingNextNotificationDelayMS = 40; -const int kMinScrollViewHeight = 100; const int kDefaultAnimationDurationMs = 120; const int kDefaultFrameRateHz = 60; - -const int kMaxNotificationCountFromSingleDisplaySource = 1; - } // namespace -// BoundedScrollView /////////////////////////////////////////////////////////// - -// A custom scroll view whose height has a minimum and maximum value and whose -// scroll bar disappears when not needed. -class BoundedScrollView : public views::ScrollView { - public: - BoundedScrollView(int min_height, int max_height); - - // Overridden from views::View: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual int GetHeightForWidth(int width) OVERRIDE; - virtual void Layout() OVERRIDE; - - private: - int min_height_; - int max_height_; - - DISALLOW_COPY_AND_ASSIGN(BoundedScrollView); -}; - -BoundedScrollView::BoundedScrollView(int min_height, int max_height) - : min_height_(min_height), - max_height_(max_height) { - set_notify_enter_exit_on_child(true); - set_background( - views::Background::CreateSolidBackground(kMessageCenterBackgroundColor)); - SetVerticalScrollBar(new views::OverlayScrollBar(false)); -} - -gfx::Size BoundedScrollView::GetPreferredSize() { - gfx::Size size = contents()->GetPreferredSize(); - size.SetToMax(gfx::Size(size.width(), min_height_)); - size.SetToMin(gfx::Size(size.width(), max_height_)); - gfx::Insets insets = GetInsets(); - size.Enlarge(insets.width(), insets.height()); - return size; -} - -int BoundedScrollView::GetHeightForWidth(int width) { - gfx::Insets insets = GetInsets(); - width = std::max(0, width - insets.width()); - int height = contents()->GetHeightForWidth(width) + insets.height(); - return std::min(std::max(height, min_height_), max_height_); -} - -void BoundedScrollView::Layout() { - int content_width = width(); - int content_height = contents()->GetHeightForWidth(content_width); - if (content_height > height()) { - content_width = std::max(content_width - GetScrollBarWidth(), 0); - content_height = contents()->GetHeightForWidth(content_width); - } - if (contents()->bounds().size() != gfx::Size(content_width, content_height)) - contents()->SetBounds(0, 0, content_width, content_height); - views::ScrollView::Layout(); -} - class NoNotificationMessageView : public views::View { public: NoNotificationMessageView(); virtual ~NoNotificationMessageView(); // Overridden from views::View. - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual int GetHeightForWidth(int width) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual int GetHeightForWidth(int width) const OVERRIDE; virtual void Layout() OVERRIDE; private: @@ -149,11 +87,11 @@ NoNotificationMessageView::NoNotificationMessageView() { NoNotificationMessageView::~NoNotificationMessageView() { } -gfx::Size NoNotificationMessageView::GetPreferredSize() { +gfx::Size NoNotificationMessageView::GetPreferredSize() const { return gfx::Size(kMinScrollViewHeight, label_->GetPreferredSize().width()); } -int NoNotificationMessageView::GetHeightForWidth(int width) { +int NoNotificationMessageView::GetHeightForWidth(int width) const { return kMinScrollViewHeight; } @@ -174,8 +112,8 @@ class MessageListView : public views::View, virtual ~MessageListView(); void AddNotificationAt(MessageView* view, int i); - void RemoveNotificationAt(int i); - void UpdateNotificationAt(MessageView* view, int i); + void RemoveNotification(MessageView* view); + void UpdateNotification(MessageView* view, const Notification& notification); void SetRepositionTarget(const gfx::Rect& target_rect); void ResetRepositionSession(); void ClearAllNotifications(const gfx::Rect& visible_scroll_rect); @@ -183,9 +121,10 @@ class MessageListView : public views::View, protected: // Overridden from views::View. virtual void Layout() OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual int GetHeightForWidth(int width) OVERRIDE; - virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual int GetHeightForWidth(int width) const OVERRIDE; + virtual void PaintChildren(gfx::Canvas* canvas, + const views::CullSet& cull_set) OVERRIDE; virtual void ReorderChildLayers(ui::Layer* parent_layer) OVERRIDE; // Overridden from views::BoundsAnimatorObserver. @@ -194,14 +133,7 @@ class MessageListView : public views::View, virtual void OnBoundsAnimatorDone(views::BoundsAnimator* animator) OVERRIDE; private: - // Returns the actual index for child of |index|. - // MessageListView allows to slide down upper notifications, which means - // that the upper ones should come above the lower ones if top_down is not - // enabled. To achieve this, inversed order is adopted. The top most - // notification is the last child, and the bottom most notification is the - // first child. - int GetActualIndex(int index); - bool IsValidChild(views::View* child); + bool IsValidChild(const views::View* child) const; void DoUpdateIfPossible(); // Animates all notifications below target upwards to align with the top of @@ -249,7 +181,7 @@ MessageListView::MessageListView(MessageCenterView* message_center_view, weak_ptr_factory_(this) { views::BoxLayout* layout = new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1); - layout->set_spread_blank_space(true); + layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL); SetLayoutManager(layout); // Set the margin to 0 for the layout. BoxLayout assumes the same margin @@ -259,7 +191,7 @@ MessageListView::MessageListView(MessageCenterView* message_center_view, gfx::Insets shadow_insets = MessageView::GetShadowInsets(); set_background(views::Background::CreateSolidBackground( kMessageCenterBackgroundColor)); - set_border(views::Border::CreateEmptyBorder( + SetBorder(views::Border::CreateEmptyBorder( top_down ? 0 : kMarginBetweenItems - shadow_insets.top(), /* top */ kMarginBetweenItems - shadow_insets.left(), /* left */ top_down ? kMarginBetweenItems - shadow_insets.bottom() : 0, /* bottom */ @@ -290,8 +222,22 @@ void MessageListView::Layout() { } } -void MessageListView::AddNotificationAt(MessageView* view, int i) { - AddChildViewAt(view, GetActualIndex(i)); +void MessageListView::AddNotificationAt(MessageView* view, int index) { + // |index| refers to a position in a subset of valid children. |real_index| + // in a list includes the invalid children, so we compute the real index by + // walking the list until |index| number of valid children are encountered, + // or to the end of the list. + int real_index = 0; + while (real_index < child_count()) { + if (IsValidChild(child_at(real_index))) { + --index; + if (index < 0) + break; + } + ++real_index; + } + + AddChildViewAt(view, real_index); if (GetContentsBounds().IsEmpty()) return; @@ -299,43 +245,41 @@ void MessageListView::AddNotificationAt(MessageView* view, int i) { DoUpdateIfPossible(); } -void MessageListView::RemoveNotificationAt(int i) { - views::View* child = child_at(GetActualIndex(i)); +void MessageListView::RemoveNotification(MessageView* view) { + DCHECK_EQ(view->parent(), this); if (GetContentsBounds().IsEmpty()) { - delete child; + delete view; } else { - if (child->layer()) { - deleting_views_.insert(child); + if (view->layer()) { + deleting_views_.insert(view); } else { if (animator_.get()) - animator_->StopAnimatingView(child); - delete child; + animator_->StopAnimatingView(view); + delete view; } DoUpdateIfPossible(); } } -void MessageListView::UpdateNotificationAt(MessageView* view, int i) { - int actual_index = GetActualIndex(i); - views::View* child = child_at(actual_index); +void MessageListView::UpdateNotification(MessageView* view, + const Notification& notification) { + int index = GetIndexOf(view); + DCHECK_LE(0, index); // GetIndexOf is negative if not a child. + if (animator_.get()) - animator_->StopAnimatingView(child); - gfx::Rect old_bounds = child->bounds(); - if (deleting_views_.find(child) != deleting_views_.end()) - deleting_views_.erase(child); - if (deleted_when_done_.find(child) != deleted_when_done_.end()) - deleted_when_done_.erase(child); - delete child; - AddChildViewAt(view, actual_index); - view->SetBounds(old_bounds.x(), old_bounds.y(), old_bounds.width(), - view->GetHeightForWidth(old_bounds.width())); + animator_->StopAnimatingView(view); + if (deleting_views_.find(view) != deleting_views_.end()) + deleting_views_.erase(view); + if (deleted_when_done_.find(view) != deleted_when_done_.end()) + deleted_when_done_.erase(view); + view->UpdateWithNotification(notification); DoUpdateIfPossible(); } -gfx::Size MessageListView::GetPreferredSize() { +gfx::Size MessageListView::GetPreferredSize() const { int width = 0; for (int i = 0; i < child_count(); i++) { - views::View* child = child_at(i); + const views::View* child = child_at(i); if (IsValidChild(child)) width = std::max(width, child->GetPreferredSize().width()); } @@ -344,7 +288,7 @@ gfx::Size MessageListView::GetPreferredSize() { GetHeightForWidth(width + GetInsets().width())); } -int MessageListView::GetHeightForWidth(int width) { +int MessageListView::GetHeightForWidth(int width) const { if (fixed_height_ > 0) return fixed_height_; @@ -352,7 +296,7 @@ int MessageListView::GetHeightForWidth(int width) { int height = 0; int padding = 0; for (int i = 0; i < child_count(); ++i) { - views::View* child = child_at(i); + const views::View* child = child_at(i); if (!IsValidChild(child)) continue; height += child->GetHeightForWidth(width) + padding; @@ -362,12 +306,13 @@ int MessageListView::GetHeightForWidth(int width) { return height + GetInsets().height(); } -void MessageListView::PaintChildren(gfx::Canvas* canvas) { +void MessageListView::PaintChildren(gfx::Canvas* canvas, + const views::CullSet& cull_set) { // Paint in the inversed order. Otherwise upper notification may be // hidden by the lower one. for (int i = child_count() - 1; i >= 0; --i) { if (!child_at(i)->layer()) - child_at(i)->Paint(canvas); + child_at(i)->Paint(canvas, cull_set); } } @@ -446,16 +391,12 @@ void MessageListView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) { GetWidget()->SynthesizeMouseMoveEvent(); } -int MessageListView::GetActualIndex(int index) { - for (int i = 0; i < child_count() && i <= index; ++i) - index += IsValidChild(child_at(i)) ? 0 : 1; - return std::min(index, child_count()); -} - -bool MessageListView::IsValidChild(views::View* child) { +bool MessageListView::IsValidChild(const views::View* child) const { return child->visible() && - deleting_views_.find(child) == deleting_views_.end() && - deleted_when_done_.find(child) == deleted_when_done_.end(); + deleting_views_.find(const_cast<views::View*>(child)) == + deleting_views_.end() && + deleted_when_done_.find(const_cast<views::View*>(child)) == + deleted_when_done_.end(); } void MessageListView::DoUpdateIfPossible() { @@ -594,7 +535,8 @@ MessageCenterView::MessageCenterView(MessageCenter* message_center, MessageCenterTray* tray, int max_height, bool initially_settings_visible, - bool top_down) + bool top_down, + const base::string16& title) : message_center_(message_center), tray_(tray), scroller_(NULL), @@ -606,7 +548,8 @@ MessageCenterView::MessageCenterView(MessageCenter* message_center, source_height_(0), target_view_(NULL), target_height_(0), - is_closing_(false) { + is_closing_(false), + context_menu_controller_(new MessageViewContextMenuController(this)) { message_center_->AddObserver(this); set_notify_enter_exit_on_child(true); set_background(views::Background::CreateSolidBackground( @@ -617,18 +560,20 @@ MessageCenterView::MessageCenterView(MessageCenter* message_center, button_bar_ = new MessageCenterButtonBar(this, message_center, notifier_settings_provider, - initially_settings_visible); + initially_settings_visible, + title); const int button_height = button_bar_->GetPreferredSize().height(); - scroller_ = - new BoundedScrollView(kMinScrollViewHeight, max_height - button_height); + scroller_ = new views::ScrollView(); + scroller_->ClipHeightTo(kMinScrollViewHeight, max_height - button_height); + scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false)); + scroller_->set_background( + views::Background::CreateSolidBackground(kMessageCenterBackgroundColor)); - if (get_use_acceleration_when_possible()) { - scroller_->SetPaintToLayer(true); - scroller_->SetFillsBoundsOpaquely(false); - scroller_->layer()->SetMasksToBounds(true); - } + scroller_->SetPaintToLayer(true); + scroller_->SetFillsBoundsOpaquely(false); + scroller_->layer()->SetMasksToBounds(true); empty_list_view_.reset(new NoNotificationMessageView); empty_list_view_->set_owned_by_client(); @@ -670,65 +615,15 @@ void MessageCenterView::SetNotifications( notification_views_.clear(); - // Count how many times each Notifier is encountered. We group Notifications - // by NotifierId. - std::map<NotifierId, int> groups; int index = 0; + for (NotificationList::Notifications::const_iterator iter = + notifications.begin(); iter != notifications.end(); ++iter) { + AddNotificationAt(*(*iter), index++); - if (IsExperimentalNotificationUIEnabled()) { - for (NotificationList::Notifications::const_iterator iter = - notifications.begin(); iter != notifications.end(); ++iter) { - NotifierId group_id = (*iter)->notifier_id(); - std::map<NotifierId, int>::iterator group_iter = groups.find(group_id); - if (group_iter != groups.end()) - group_iter->second++; - else - groups[group_id] = 1; - } - - // TODO(dimich): Find a better group icon. Preferably associated with - // the group (notifier icon?). - gfx::ImageSkia* group_icon = ui::ResourceBundle::GetSharedInstance(). - GetImageSkiaNamed(IDR_FOLDER_CLOSED); - - for (NotificationList::Notifications::const_iterator iter = - notifications.begin(); iter != notifications.end(); ++iter) { - // See if the notification's NotifierId is encountered too many - // times - in this case replace all notifications from this source with - // a synthetic placeholder that says "N more". Mark the NotifierId - // as "seen" by setting count to 0 so the subsequent notificaitons from - // the same source are ignored. - std::map<NotifierId, int>::iterator group_iter = - groups.find((*iter)->notifier_id()); - // We should have collected all groups in the loop above. - DCHECK(group_iter != groups.end()); - - if (group_iter->second > kMaxNotificationCountFromSingleDisplaySource) { - AddGroupPlaceholder(group_iter->first, - *(*iter), - group_icon ? *group_icon : gfx::ImageSkia(), - group_iter->second, - index++); - group_iter->second = 0; // Mark. - } else if (group_iter->second == 0) { // Marked, skip. - continue; - } else { // Ungrouped notifications - AddNotificationAt(*(*iter), index++); - } - - message_center_->DisplayedNotification((*iter)->id()); - if (notification_views_.size() >= kMaxVisibleMessageCenterNotifications) - break; - } - } else { - for (NotificationList::Notifications::const_iterator iter = - notifications.begin(); iter != notifications.end(); ++iter) { - AddNotificationAt(*(*iter), index++); - - message_center_->DisplayedNotification((*iter)->id()); - if (notification_views_.size() >= kMaxVisibleMessageCenterNotifications) - break; - } + message_center_->DisplayedNotification( + (*iter)->id(), message_center::DISPLAY_SOURCE_MESSAGE_CENTER); + if (notification_views_.size() >= kMaxVisibleMessageCenterNotifications) + break; } NotificationsChanged(); @@ -854,10 +749,10 @@ void MessageCenterView::Layout() { if (is_scrollable) { // Draw separator line on the top of the button bar if it is on the bottom // or draw it at the bottom if the bar is on the top. - button_bar_->set_border(views::Border::CreateSolidSidedBorder( + button_bar_->SetBorder(views::Border::CreateSolidSidedBorder( top_down_ ? 0 : 1, 0, top_down_ ? 1 : 0, 0, kFooterDelimiterColor)); } else { - button_bar_->set_border(views::Border::CreateEmptyBorder( + button_bar_->SetBorder(views::Border::CreateEmptyBorder( top_down_ ? 0 : 1, 0, top_down_ ? 1 : 0, 0)); } button_bar_->SchedulePaint(); @@ -870,7 +765,7 @@ void MessageCenterView::Layout() { GetWidget()->GetRootView()->SchedulePaint(); } -gfx::Size MessageCenterView::GetPreferredSize() { +gfx::Size MessageCenterView::GetPreferredSize() const { if (settings_transition_animation_ && settings_transition_animation_->is_animating()) { int content_width = std::max(source_view_->GetPreferredSize().width(), @@ -882,14 +777,14 @@ gfx::Size MessageCenterView::GetPreferredSize() { int width = 0; for (int i = 0; i < child_count(); ++i) { - views::View* child = child_at(0); + const views::View* child = child_at(0); if (child->visible()) width = std::max(width, child->GetPreferredSize().width()); } return gfx::Size(width, GetHeightForWidth(width)); } -int MessageCenterView::GetHeightForWidth(int width) { +int MessageCenterView::GetHeightForWidth(int width) const { if (settings_transition_animation_ && settings_transition_animation_->is_animating()) { int content_height = target_height_; @@ -927,7 +822,6 @@ void MessageCenterView::OnMouseExited(const ui::MouseEvent& event) { NotificationsChanged(); } -// TODO(dimich): update for GROUP_VIEW void MessageCenterView::OnNotificationAdded(const std::string& id) { int index = 0; const NotificationList::Notifications& notifications = @@ -945,7 +839,6 @@ void MessageCenterView::OnNotificationAdded(const std::string& id) { NotificationsChanged(); } -// TODO(dimich): update for GROUP_VIEW void MessageCenterView::OnNotificationRemoved(const std::string& id, bool by_user) { NotificationViewsMap::iterator view_iter = notification_views_.find(id); @@ -953,6 +846,7 @@ void MessageCenterView::OnNotificationRemoved(const std::string& id, return; NotificationView* view = view_iter->second; int index = message_list_view_->GetIndexOf(view); + DCHECK_LE(0, index); if (by_user) { message_list_view_->SetRepositionTarget(view->bounds()); // Moves the keyboard focus to the next notification if the removed @@ -976,38 +870,27 @@ void MessageCenterView::OnNotificationRemoved(const std::string& id, } } } - message_list_view_->RemoveNotificationAt(index); + message_list_view_->RemoveNotification(view); notification_views_.erase(view_iter); NotificationsChanged(); } -// TODO(dimich): update for GROUP_VIEW void MessageCenterView::OnNotificationUpdated(const std::string& id) { NotificationViewsMap::const_iterator view_iter = notification_views_.find(id); if (view_iter == notification_views_.end()) return; NotificationView* view = view_iter->second; - size_t index = message_list_view_->GetIndexOf(view); - DCHECK(index >= 0); // TODO(dimich): add MessageCenter::GetVisibleNotificationById(id) const NotificationList::Notifications& notifications = message_center_->GetVisibleNotifications(); for (NotificationList::Notifications::const_iterator iter = notifications.begin(); iter != notifications.end(); ++iter) { if ((*iter)->id() == id) { - bool expanded = true; - if (IsExperimentalNotificationUIEnabled()) - expanded = (*iter)->is_expanded(); - NotificationView* view = - NotificationView::Create(this, - *(*iter), - expanded, - false); // Not creating a top-level - // notification. - view->set_scroller(scroller_); - message_list_view_->UpdateNotificationAt(view, index); - notification_views_[id] = view; - NotificationsChanged(); + int old_width = view->width(); + int old_height = view->GetHeightForWidth(old_width); + message_list_view_->UpdateNotification(view, **iter); + if (view->GetHeightForWidth(old_width) != old_height) + NotificationsChanged(); break; } } @@ -1023,13 +906,10 @@ void MessageCenterView::RemoveNotification(const std::string& notification_id, message_center_->RemoveNotification(notification_id, by_user); } -void MessageCenterView::DisableNotificationsFromThisSource( - const NotifierId& notifier_id) { - message_center_->DisableNotificationsByNotifier(notifier_id); -} - -void MessageCenterView::ShowNotifierSettingsBubble() { - tray_->ShowNotifierSettingsBubble(); +scoped_ptr<ui::MenuModel> MessageCenterView::CreateMenuModel( + const NotifierId& notifier_id, + const base::string16& display_source) { + return tray_->CreateNotificationMenuModel(notifier_id, display_source); } bool MessageCenterView::HasClickedListener(const std::string& notification_id) { @@ -1042,41 +922,6 @@ void MessageCenterView::ClickOnNotificationButton( message_center_->ClickOnNotificationButton(notification_id, button_index); } -void MessageCenterView::ExpandNotification(const std::string& notification_id) { - message_center_->ExpandNotification(notification_id); -} - -void MessageCenterView::GroupBodyClicked( - const std::string& last_notification_id) { - message_center_->ClickOnNotification(last_notification_id); -} - -// When clicked on the "N more" button, perform some reasonable action. -// TODO(dimich): find out what the reasonable action could be. -void MessageCenterView::ExpandGroup(const NotifierId& notifier_id) { - NOTIMPLEMENTED(); -} - -// Click on Close button on a GroupView should remove all notifications -// represented by this GroupView. -void MessageCenterView::RemoveGroup(const NotifierId& notifier_id) { - std::vector<std::string> notifications_to_remove; - - // Can not remove notifications while iterating the list. Collect the ids - // and then run separate loop to remove notifications. - const NotificationList::Notifications& notifications = - message_center_->GetVisibleNotifications(); - for (NotificationList::Notifications::const_iterator iter = - notifications.begin(); iter != notifications.end(); ++iter) { - if ((*iter)->notifier_id() == notifier_id) - notifications_to_remove.push_back((*iter)->id()); - } - - for (size_t i = 0; i < notifications_to_remove.size(); ++i) - // "by_user" = true - message_center_->RemoveNotification(notifications_to_remove[i], true); -} - void MessageCenterView::AnimationEnded(const gfx::Animation* animation) { DCHECK_EQ(animation, settings_transition_animation_.get()); @@ -1117,46 +962,18 @@ void MessageCenterView::AnimationCanceled(const gfx::Animation* animation) { AnimationEnded(animation); } - -void MessageCenterView::AddMessageViewAt(MessageView* view, int index) { - view->set_scroller(scroller_); - message_list_view_->AddNotificationAt(view, index); -} - -void MessageCenterView::AddGroupPlaceholder( - const NotifierId& group_id, - const Notification& last_notification, - const gfx::ImageSkia& group_icon, - int group_size, - int index) { - GroupView* view = new GroupView(this, - group_id, - last_notification, - group_icon, - group_size); - group_views_.push_back(view); - AddMessageViewAt(view, index); -} - void MessageCenterView::AddNotificationAt(const Notification& notification, int index) { - // NotificationViews are expanded by default here until - // http://crbug.com/217902 is fixed. TODO(dharcourt): Fix. - bool expanded = true; - if (IsExperimentalNotificationUIEnabled()) - expanded = notification.is_expanded(); NotificationView* view = - NotificationView::Create(this, - notification, - expanded, - false); // Not creating a top-level - // notification. + NotificationView::Create(this, notification, false); // Not top-level. + view->set_context_menu_controller(context_menu_controller_.get()); notification_views_[notification.id()] = view; - AddMessageViewAt(view, index); + view->set_scroller(scroller_); + message_list_view_->AddNotificationAt(view, index); } void MessageCenterView::NotificationsChanged() { - bool no_message_views = notification_views_.empty() && group_views_.empty(); + bool no_message_views = notification_views_.empty(); // When the child view is removed from the hierarchy, its focus is cleared. // In this case we want to save which view has focus so that the user can diff --git a/chromium/ui/message_center/views/message_center_view.h b/chromium/ui/message_center/views/message_center_view.h index b64f770a8e6..94dd139213c 100644 --- a/chromium/ui/message_center/views/message_center_view.h +++ b/chromium/ui/message_center/views/message_center_view.h @@ -5,7 +5,6 @@ #ifndef UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_VIEW_H_ #define UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_VIEW_H_ -#include "ui/views/view.h" #include "ui/gfx/animation/animation_delegate.h" #include "ui/message_center/message_center_export.h" @@ -14,6 +13,7 @@ #include "ui/message_center/views/message_center_controller.h" #include "ui/message_center/views/message_view.h" #include "ui/views/controls/button/button.h" +#include "ui/views/view.h" namespace gfx { class MultiAnimation; @@ -25,7 +25,6 @@ class Button; namespace message_center { -class GroupView; class MessageCenter; class MessageCenterBubble; class NotificationCenterButton; @@ -33,6 +32,7 @@ class MessageCenterButtonBar; class MessageCenterTray; class MessageCenterView; class MessageView; +class MessageViewContextMenuController; class MessageListView; class NotificationView; class NotifierSettingsView; @@ -48,7 +48,8 @@ class MESSAGE_CENTER_EXPORT MessageCenterView : public views::View, MessageCenterTray* tray, int max_height, bool initially_settings_visible, - bool top_down); + bool top_down, + const base::string16& title); virtual ~MessageCenterView(); void SetNotifications(const NotificationList::Notifications& notifications); @@ -61,14 +62,15 @@ class MESSAGE_CENTER_EXPORT MessageCenterView : public views::View, void SetSettingsVisible(bool visible); void OnSettingsChanged(); bool settings_visible() const { return settings_visible_; } + MessageCenterTray* tray() { return tray_; } void SetIsClosing(bool is_closing); protected: // Overridden from views::View: virtual void Layout() OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual int GetHeightForWidth(int width) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual int GetHeightForWidth(int width) const OVERRIDE; virtual bool OnMouseWheel(const ui::MouseWheelEvent& event) OVERRIDE; virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; @@ -82,17 +84,12 @@ class MESSAGE_CENTER_EXPORT MessageCenterView : public views::View, virtual void ClickOnNotification(const std::string& notification_id) OVERRIDE; virtual void RemoveNotification(const std::string& notification_id, bool by_user) OVERRIDE; - virtual void DisableNotificationsFromThisSource( - const NotifierId& notifier_id) OVERRIDE; - virtual void ShowNotifierSettingsBubble() OVERRIDE; + virtual scoped_ptr<ui::MenuModel> CreateMenuModel( + const NotifierId& notifier_id, + const base::string16& display_source) OVERRIDE; virtual bool HasClickedListener(const std::string& notification_id) OVERRIDE; virtual void ClickOnNotificationButton(const std::string& notification_id, int button_index) OVERRIDE; - virtual void ExpandNotification(const std::string& notification_id) OVERRIDE; - virtual void GroupBodyClicked(const std::string& last_notification_id) - OVERRIDE; - virtual void ExpandGroup(const NotifierId& notifier_id) OVERRIDE; - virtual void RemoveGroup(const NotifierId& notifier_id) OVERRIDE; // Overridden from gfx::AnimationDelegate: virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE; @@ -102,12 +99,6 @@ class MESSAGE_CENTER_EXPORT MessageCenterView : public views::View, private: friend class MessageCenterViewTest; - void AddMessageViewAt(MessageView* view, int index); - void AddGroupPlaceholder(const NotifierId& group_id, - const Notification& notification, - const gfx::ImageSkia& group_icon, - int group_size, - int index); void AddNotificationAt(const Notification& notification, int index); void NotificationsChanged(); void SetNotificationViewForTest(MessageView* view); @@ -115,16 +106,11 @@ class MESSAGE_CENTER_EXPORT MessageCenterView : public views::View, MessageCenter* message_center_; // Weak reference. MessageCenterTray* tray_; // Weak reference. - // Map notification_id->NotificationView*. It contains all NotificaitonViews + // Map notification_id->NotificationView*. It contains all NotificationViews // currently displayed in MessageCenter. typedef std::map<std::string, NotificationView*> NotificationViewsMap; NotificationViewsMap notification_views_; // Weak. - // List of all GroupViews. GroupView is responsible for multiple Notifications - // from the same source. - typedef std::list<GroupView*> GroupViews; - GroupViews group_views_; // Weak. - // Child views. views::ScrollView* scroller_; scoped_ptr<MessageListView> message_list_view_; @@ -151,6 +137,8 @@ class MESSAGE_CENTER_EXPORT MessageCenterView : public views::View, // ignored. bool is_closing_; + scoped_ptr<MessageViewContextMenuController> context_menu_controller_; + DISALLOW_COPY_AND_ASSIGN(MessageCenterView); }; diff --git a/chromium/ui/message_center/views/message_center_view_unittest.cc b/chromium/ui/message_center/views/message_center_view_unittest.cc index cd44e2dec66..3335ed8c52b 100644 --- a/chromium/ui/message_center/views/message_center_view_unittest.cc +++ b/chromium/ui/message_center/views/message_center_view_unittest.cc @@ -40,8 +40,8 @@ class MockNotificationView : public NotificationView { Test* test); virtual ~MockNotificationView(); - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual int GetHeightForWidth(int w) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual int GetHeightForWidth(int w) const OVERRIDE; virtual void Layout() OVERRIDE; private: @@ -53,20 +53,20 @@ class MockNotificationView : public NotificationView { MockNotificationView::MockNotificationView(MessageCenterController* controller, const Notification& notification, Test* test) - : NotificationView(controller, notification, true), + : NotificationView(controller, notification), test_(test) { } MockNotificationView::~MockNotificationView() { } -gfx::Size MockNotificationView::GetPreferredSize() { +gfx::Size MockNotificationView::GetPreferredSize() const { test_->RegisterCall(GET_PREFERRED_SIZE); DCHECK(child_count() > 0); return NotificationView::GetPreferredSize(); } -int MockNotificationView::GetHeightForWidth(int width) { +int MockNotificationView::GetHeightForWidth(int width) const { test_->RegisterCall(GET_HEIGHT_FOR_WIDTH); DCHECK(child_count() > 0); return NotificationView::GetHeightForWidth(width); @@ -98,17 +98,12 @@ class MessageCenterViewTest : public testing::Test, virtual void ClickOnNotification(const std::string& notification_id) OVERRIDE; virtual void RemoveNotification(const std::string& notification_id, bool by_user) OVERRIDE; - virtual void DisableNotificationsFromThisSource( - const NotifierId& notifier_id) OVERRIDE; - virtual void ShowNotifierSettingsBubble() OVERRIDE; + virtual scoped_ptr<ui::MenuModel> CreateMenuModel( + const NotifierId& notifier_id, + const base::string16& display_source) OVERRIDE; virtual bool HasClickedListener(const std::string& notification_id) OVERRIDE; virtual void ClickOnNotificationButton(const std::string& notification_id, int button_index) OVERRIDE; - virtual void ExpandNotification(const std::string& notification_id) OVERRIDE; - virtual void GroupBodyClicked(const std::string& last_notification_id) - OVERRIDE; - virtual void ExpandGroup(const NotifierId& notifier_id) OVERRIDE; - virtual void RemoveGroup(const NotifierId& notifier_id) OVERRIDE; // Overridden from MockNotificationView::Test virtual void RegisterCall(CallType type) OVERRIDE; @@ -136,10 +131,10 @@ void MessageCenterViewTest::SetUp() { // Create a dummy notification. Notification notification(NOTIFICATION_TYPE_SIMPLE, std::string("notification id"), - UTF8ToUTF16("title"), - UTF8ToUTF16("message"), + base::UTF8ToUTF16("title"), + base::UTF8ToUTF16("message"), gfx::Image(), - UTF8ToUTF16("display source"), + base::UTF8ToUTF16("display source"), NotifierId(NotifierId::APPLICATION, "extension_id"), message_center::RichNotificationData(), NULL); @@ -149,8 +144,9 @@ void MessageCenterViewTest::SetUp() { notifications.insert(¬ification); // Then create a new MessageCenterView with that single notification. + base::string16 title; message_center_view_.reset(new MessageCenterView( - &message_center_, NULL, 100, false, /*top_down =*/false)); + &message_center_, NULL, 100, false, /*top_down =*/false, title)); message_center_view_->SetNotifications(notifications); // Remove and delete the NotificationView now owned by the MessageCenterView's @@ -191,15 +187,12 @@ void MessageCenterViewTest::RemoveNotification( NOTREACHED(); } -void MessageCenterViewTest::DisableNotificationsFromThisSource( - const NotifierId& notifier_id) { - // For this test, this method should not be invoked. - NOTREACHED(); -} - -void MessageCenterViewTest::ShowNotifierSettingsBubble() { +scoped_ptr<ui::MenuModel> MessageCenterViewTest::CreateMenuModel( + const NotifierId& notifier_id, + const base::string16& display_source) { // For this test, this method should not be invoked. NOTREACHED(); + return scoped_ptr<ui::MenuModel>(); } bool MessageCenterViewTest::HasClickedListener( @@ -214,36 +207,14 @@ void MessageCenterViewTest::ClickOnNotificationButton( NOTREACHED(); } -void MessageCenterViewTest::ExpandNotification( - const std::string& notification_id) { - // For this test, this method should not be invoked. - NOTREACHED(); -} - -void MessageCenterViewTest::GroupBodyClicked( - const std::string& last_notification_id) { - // For this test, this method should not be invoked. - NOTREACHED(); -} - -void MessageCenterViewTest::ExpandGroup(const NotifierId& notifier_id) { - // For this test, this method should not be invoked. - NOTREACHED(); -} - -void MessageCenterViewTest::RemoveGroup(const NotifierId& notifier_id) { - // For this test, this method should not be invoked. - NOTREACHED(); -} - void MessageCenterViewTest::RegisterCall(CallType type) { callCounts_[type] += 1; } void MessageCenterViewTest::LogBounds(int depth, views::View* view) { - string16 inset; + base::string16 inset; for (int i = 0; i < depth; ++i) - inset.append(UTF8ToUTF16(" ")); + inset.append(base::UTF8ToUTF16(" ")); gfx::Rect bounds = view->bounds(); DVLOG(0) << inset << bounds.width() << " x " << bounds.height() << " @ " << bounds.x() << ", " << bounds.y(); diff --git a/chromium/ui/message_center/views/message_popup_collection.cc b/chromium/ui/message_center/views/message_popup_collection.cc index cc8c79b4656..d1116d6f346 100644 --- a/chromium/ui/message_center/views/message_popup_collection.cc +++ b/chromium/ui/message_center/views/message_popup_collection.cc @@ -13,16 +13,16 @@ #include "base/run_loop.h" #include "base/time/time.h" #include "base/timer/timer.h" -#include "ui/base/accessibility/accessibility_types.h" +#include "ui/accessibility/ax_enums.h" #include "ui/gfx/animation/animation_delegate.h" #include "ui/gfx/animation/slide_animation.h" #include "ui/gfx/screen.h" #include "ui/message_center/message_center.h" #include "ui/message_center/message_center_style.h" #include "ui/message_center/message_center_tray.h" -#include "ui/message_center/message_center_util.h" #include "ui/message_center/notification.h" #include "ui/message_center/notification_list.h" +#include "ui/message_center/views/message_view_context_menu_controller.h" #include "ui/message_center/views/notification_view.h" #include "ui/message_center/views/toast_contents_view.h" #include "ui/views/background.h" @@ -64,42 +64,24 @@ MessagePopupCollection::MessagePopupCollection(gfx::NativeView parent, : parent_(parent), message_center_(message_center), tray_(tray), + display_id_(gfx::Display::kInvalidDisplayID), + screen_(NULL), defer_counter_(0), latest_toast_entered_(NULL), user_is_closing_toasts_by_clicking_(false), first_item_has_no_margin_(first_item_has_no_margin), + context_menu_controller_(new MessageViewContextMenuController(this)), weak_factory_(this) { DCHECK(message_center_); defer_timer_.reset(new base::OneShotTimer<MessagePopupCollection>); message_center_->AddObserver(this); - gfx::Screen* screen = NULL; - gfx::Display display; - if (!parent_) { - // On Win+Aura, we don't have a parent since the popups currently show up - // on the Windows desktop, not in the Aura/Ash desktop. This code will - // display the popups on the primary display. - screen = gfx::Screen::GetNativeScreen(); - display = screen->GetPrimaryDisplay(); - } else { - screen = gfx::Screen::GetScreenFor(parent_); - display = screen->GetDisplayNearestWindow(parent_); - } - screen->AddObserver(this); - - display_id_ = display.id(); - work_area_ = display.work_area(); - ComputePopupAlignment(work_area_, display.bounds()); - - // We should not update before work area and popup alignment are computed. - DoUpdateIfPossible(); } MessagePopupCollection::~MessagePopupCollection() { weak_factory_.InvalidateWeakPtrs(); - gfx::Screen* screen = parent_ ? - gfx::Screen::GetScreenFor(parent_) : gfx::Screen::GetNativeScreen(); - screen->RemoveObserver(this); + if (screen_) + screen_->RemoveObserver(this); message_center_->RemoveObserver(this); CloseAllWidgets(); @@ -116,13 +98,10 @@ void MessagePopupCollection::RemoveNotification( message_center_->RemoveNotification(notification_id, by_user); } -void MessagePopupCollection::DisableNotificationsFromThisSource( - const NotifierId& notifier_id) { - message_center_->DisableNotificationsByNotifier(notifier_id); -} - -void MessagePopupCollection::ShowNotifierSettingsBubble() { - tray_->ShowNotifierSettingsBubble(); +scoped_ptr<ui::MenuModel> MessagePopupCollection::CreateMenuModel( + const NotifierId& notifier_id, + const base::string16& display_source) { + return tray_->CreateNotificationMenuModel(notifier_id, display_source); } bool MessagePopupCollection::HasClickedListener( @@ -136,29 +115,6 @@ void MessagePopupCollection::ClickOnNotificationButton( message_center_->ClickOnNotificationButton(notification_id, button_index); } -void MessagePopupCollection::ExpandNotification( - const std::string& notification_id) { - message_center_->ExpandNotification(notification_id); -} - -void MessagePopupCollection::GroupBodyClicked( - const std::string& last_notification_id) { - // No group views in popup collection. - NOTREACHED(); -} - -// When clicked on the "N more" button, perform some reasonable action. -// TODO(dimich): find out what the reasonable action could be. -void MessagePopupCollection::ExpandGroup(const NotifierId& notifier_id) { - // No group views in popup collection. - NOTREACHED(); -} - -void MessagePopupCollection::RemoveGroup(const NotifierId& notifier_id) { - // No group views in popup collection. - NOTREACHED(); -} - void MessagePopupCollection::MarkAllPopupsShown() { std::set<std::string> closed_ids = CloseAllWidgets(); for (std::set<std::string>::iterator iter = closed_ids.begin(); @@ -186,14 +142,11 @@ void MessagePopupCollection::UpdateWidgets() { if (FindToast((*iter)->id())) continue; - bool expanded = true; - if (IsExperimentalNotificationUIEnabled()) - expanded = (*iter)->is_expanded(); NotificationView* view = NotificationView::Create(NULL, *(*iter), - expanded, true); // Create top-level notification. + view->set_context_menu_controller(context_menu_controller_.get()); int view_height = ToastContentsView::GetToastSizeForView(view).height(); int height_available = top_down ? work_area_.bottom() - base : base; @@ -230,10 +183,11 @@ void MessagePopupCollection::UpdateWidgets() { if (views::ViewsDelegate::views_delegate) { views::ViewsDelegate::views_delegate->NotifyAccessibilityEvent( - toast, ui::AccessibilityTypes::EVENT_ALERT); + toast, ui::AX_EVENT_ALERT); } - message_center_->DisplayedNotification((*iter)->id()); + message_center_->DisplayedNotification( + (*iter)->id(), message_center::DISPLAY_SOURCE_POPUP); } } @@ -496,24 +450,22 @@ void MessagePopupCollection::OnNotificationUpdated( for (NotificationList::PopupNotifications::iterator iter = notifications.begin(); iter != notifications.end(); ++iter) { - if ((*iter)->id() != notification_id) - continue; + Notification* notification = *iter; + DCHECK(notification); + ToastContentsView* toast_contents_view = *toast_iter; + DCHECK(toast_contents_view); - bool expanded = true; - if (IsExperimentalNotificationUIEnabled()) - expanded = (*iter)->is_expanded(); + if (notification->id() != notification_id) + continue; const RichNotificationData& optional_fields = - (*iter)->rich_notification_data(); + notification->rich_notification_data(); bool a11y_feedback_for_updates = optional_fields.should_make_spoken_feedback_for_popup_updates; - NotificationView* view = - NotificationView::Create(*toast_iter, - *(*iter), - expanded, - true); // Create top-level notification. - (*toast_iter)->SetContents(view, a11y_feedback_for_updates); + toast_contents_view->UpdateContents(*notification, + a11y_feedback_for_updates); + updated = true; } @@ -556,6 +508,28 @@ void MessagePopupCollection::DecrementDeferCounter() { // deferred tasks are even able to run) // Then, see if there is vacant space for new toasts. void MessagePopupCollection::DoUpdateIfPossible() { + if (!screen_) { + gfx::Display display; + if (!parent_) { + // On Win+Aura, we don't have a parent since the popups currently show up + // on the Windows desktop, not in the Aura/Ash desktop. This code will + // display the popups on the primary display. + screen_ = gfx::Screen::GetNativeScreen(); + display = screen_->GetPrimaryDisplay(); + } else { + screen_ = gfx::Screen::GetScreenFor(parent_); + display = screen_->GetDisplayNearestWindow(parent_); + } + screen_->AddObserver(this); + + display_id_ = display.id(); + // |work_area_| can be set already and it should not be overwritten here. + if (work_area_.IsEmpty()) { + work_area_ = display.work_area(); + ComputePopupAlignment(work_area_, display.bounds()); + } + } + if (defer_counter_ > 0) return; @@ -587,18 +561,24 @@ void MessagePopupCollection::SetDisplayInfo(const gfx::Rect& work_area, RepositionWidgets(); } -void MessagePopupCollection::OnDisplayBoundsChanged( - const gfx::Display& display) { - if (display.id() != display_id_) - return; - - SetDisplayInfo(display.work_area(), display.bounds()); -} - void MessagePopupCollection::OnDisplayAdded(const gfx::Display& new_display) { } void MessagePopupCollection::OnDisplayRemoved(const gfx::Display& old_display) { + if (display_id_ == old_display.id() && !parent_) { + gfx::Display display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); + display_id_ = display.id(); + SetDisplayInfo(display.work_area(), display.bounds()); + } +} + +void MessagePopupCollection::OnDisplayMetricsChanged( + const gfx::Display& display, uint32_t metrics) { + if (display.id() != display_id_) + return; + + if (metrics & DISPLAY_METRIC_BOUNDS || metrics & DISPLAY_METRIC_WORK_AREA) + SetDisplayInfo(display.work_area(), display.bounds()); } views::Widget* MessagePopupCollection::GetWidgetForTest(const std::string& id) diff --git a/chromium/ui/message_center/views/message_popup_collection.h b/chromium/ui/message_center/views/message_popup_collection.h index 48345d80d75..955af32ddb5 100644 --- a/chromium/ui/message_center/views/message_popup_collection.h +++ b/chromium/ui/message_center/views/message_popup_collection.h @@ -35,6 +35,10 @@ class WebNotificationTrayTest; FORWARD_DECLARE_TEST(WebNotificationTrayTest, ManyPopupNotifications); } +namespace gfx { +class Screen; +} + namespace message_center { namespace test { class MessagePopupCollectionTest; @@ -42,6 +46,7 @@ class MessagePopupCollectionTest; class MessageCenter; class MessageCenterTray; +class MessageViewContextMenuController; enum PopupAlignment { POPUP_ALIGNMENT_TOP = 1 << 0, @@ -74,17 +79,12 @@ class MESSAGE_CENTER_EXPORT MessagePopupCollection virtual void ClickOnNotification(const std::string& notification_id) OVERRIDE; virtual void RemoveNotification(const std::string& notification_id, bool by_user) OVERRIDE; - virtual void DisableNotificationsFromThisSource( - const NotifierId& notifier_id) OVERRIDE; - virtual void ShowNotifierSettingsBubble() OVERRIDE; + virtual scoped_ptr<ui::MenuModel> CreateMenuModel( + const NotifierId& notifier_id, + const base::string16& display_source) OVERRIDE; virtual bool HasClickedListener(const std::string& notification_id) OVERRIDE; virtual void ClickOnNotificationButton(const std::string& notification_id, int button_index) OVERRIDE; - virtual void ExpandNotification(const std::string& notification_id) OVERRIDE; - virtual void GroupBodyClicked(const std::string& last_notification_id) - OVERRIDE; - virtual void ExpandGroup(const NotifierId& notifier_id) OVERRIDE; - virtual void RemoveGroup(const NotifierId& notifier_id) OVERRIDE; void MarkAllPopupsShown(); @@ -111,7 +111,7 @@ class MESSAGE_CENTER_EXPORT MessagePopupCollection // Updates |work_area_| and re-calculates the alignment of notification toasts // rearranging them if necessary. - // This is separated from methods from OnDisplayBoundsChanged(), since + // This is separated from methods from OnDisplayMetricsChanged(), since // sometimes the display info has to be specified directly. One example is // shelf's auto-hide change. When the shelf in ChromeOS is temporarily shown // from auto hide status, it doesn't change the display's work area but the @@ -120,9 +120,10 @@ class MESSAGE_CENTER_EXPORT MessagePopupCollection const gfx::Rect& screen_bounds); // Overridden from gfx::DislayObserver: - virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE; virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE; virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE; + virtual void OnDisplayMetricsChanged(const gfx::Display& display, + uint32_t metrics) OVERRIDE; // Used by ToastContentsView to locate itself. gfx::NativeView parent() const { return parent_; } @@ -188,6 +189,7 @@ class MESSAGE_CENTER_EXPORT MessagePopupCollection Toasts toasts_; gfx::Rect work_area_; int64 display_id_; + gfx::Screen* screen_; // Specifies which corner of the screen popups should show up. This should // ideally be the same corner the notification area (systray) is at. @@ -216,6 +218,8 @@ class MESSAGE_CENTER_EXPORT MessagePopupCollection // True if the first item should not have spacing against the tray. bool first_item_has_no_margin_; + scoped_ptr<MessageViewContextMenuController> context_menu_controller_; + // Gives out weak pointers to toast contents views which have an unrelated // lifetime. Must remain the last member variable. base::WeakPtrFactory<MessagePopupCollection> weak_factory_; diff --git a/chromium/ui/message_center/views/message_popup_collection_unittest.cc b/chromium/ui/message_center/views/message_popup_collection_unittest.cc index f763b516743..eacc6807726 100644 --- a/chromium/ui/message_center/views/message_popup_collection_unittest.cc +++ b/chromium/ui/message_center/views/message_popup_collection_unittest.cc @@ -87,10 +87,10 @@ class MessagePopupCollectionTest : public views::ViewsTestBase { scoped_ptr<Notification> notification( new Notification(NOTIFICATION_TYPE_BASE_FORMAT, id, - UTF8ToUTF16("test title"), - UTF8ToUTF16("test message"), + base::UTF8ToUTF16("test title"), + base::UTF8ToUTF16("test message"), gfx::Image(), - string16() /* display_source */, + base::string16() /* display_source */, NotifierId(), message_center::RichNotificationData(), NULL /* delegate */)); @@ -358,7 +358,7 @@ TEST_F(MessagePopupCollectionTest, DetectMouseHover) { views::WidgetDelegateView* toast1 = GetToast(id1); EXPECT_TRUE(toast1 != NULL); - ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), 0); + ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), 0, 0); // Test that mouse detection logic works in presence of out-of-order events. toast0->OnMouseEntered(event); @@ -393,7 +393,7 @@ TEST_F(MessagePopupCollectionTest, DetectMouseHoverWithUserClose) { views::WidgetDelegateView* toast1 = GetToast(id1); ASSERT_TRUE(toast1 != NULL); - ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), 0); + ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), 0, 0); toast1->OnMouseEntered(event); static_cast<MessageCenterObserver*>(collection())->OnNotificationRemoved( id1, true); diff --git a/chromium/ui/message_center/views/message_view.cc b/chromium/ui/message_center/views/message_view.cc index ccf1074b2e6..11ff5485f86 100644 --- a/chromium/ui/message_center/views/message_view.cc +++ b/chromium/ui/message_center/views/message_view.cc @@ -6,7 +6,7 @@ #include "grit/ui_resources.h" #include "grit/ui_strings.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/simple_menu_model.h" #include "ui/base/resource/resource_bundle.h" @@ -14,15 +14,14 @@ #include "ui/gfx/canvas.h" #include "ui/message_center/message_center.h" #include "ui/message_center/message_center_style.h" -#include "ui/message_center/message_center_util.h" #include "ui/message_center/views/padded_button.h" -#include "ui/views/context_menu_controller.h" +#include "ui/views/background.h" #include "ui/views/controls/button/image_button.h" -#include "ui/views/controls/menu/menu_runner.h" +#include "ui/views/controls/image_view.h" #include "ui/views/controls/scroll_view.h" +#include "ui/views/focus/focus_manager.h" #include "ui/views/painter.h" #include "ui/views/shadow_border.h" -#include "ui/views/widget/widget.h" namespace { @@ -32,161 +31,48 @@ const int kCloseIconRightPadding = 5; const int kShadowOffset = 1; const int kShadowBlur = 4; -// Menu constants -const int kTogglePermissionCommand = 0; -const int kShowSettingsCommand = 1; - -// A dropdown menu for notifications. -class MenuModel : public ui::SimpleMenuModel, - public ui::SimpleMenuModel::Delegate { - public: - MenuModel(message_center::MessageViewController* controller, - message_center::NotifierId notifier_id, - const string16& display_source); - virtual ~MenuModel(); - - // Overridden from ui::SimpleMenuModel::Delegate: - virtual bool IsItemForCommandIdDynamic(int command_id) const OVERRIDE; - virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; - virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; - virtual bool GetAcceleratorForCommandId( - int command_id, - ui::Accelerator* accelerator) OVERRIDE; - virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; - - private: - message_center::MessageViewController* controller_; - message_center::NotifierId notifier_id_; - DISALLOW_COPY_AND_ASSIGN(MenuModel); -}; - -MenuModel::MenuModel(message_center::MessageViewController* controller, - message_center::NotifierId notifier_id, - const string16& display_source) - : ui::SimpleMenuModel(this), - controller_(controller), - notifier_id_(notifier_id) { - // Add 'disable notifications' menu item. - if (!display_source.empty()) { - AddItem(kTogglePermissionCommand, - l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_NOTIFIER_DISABLE, - display_source)); - } - // Add settings menu item. - AddItem(kShowSettingsCommand, - l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS)); -} - -MenuModel::~MenuModel() { -} - -bool MenuModel::IsItemForCommandIdDynamic(int command_id) const { - return false; -} - -bool MenuModel::IsCommandIdChecked(int command_id) const { - return false; -} - -bool MenuModel::IsCommandIdEnabled(int command_id) const { - return true; -} - -bool MenuModel::GetAcceleratorForCommandId(int command_id, - ui::Accelerator* accelerator) { - return false; -} - -void MenuModel::ExecuteCommand(int command_id, int event_flags) { - switch (command_id) { - case kTogglePermissionCommand: - controller_->DisableNotificationsFromThisSource(notifier_id_); - break; - case kShowSettingsCommand: - controller_->ShowNotifierSettingsBubble(); - break; - default: - NOTREACHED(); - } -} - } // namespace namespace message_center { -class MessageViewContextMenuController : public views::ContextMenuController { - public: - MessageViewContextMenuController(MessageViewController* controller, - const NotifierId& notifier_id, - const string16& display_source); - virtual ~MessageViewContextMenuController(); - - protected: - // Overridden from views::ContextMenuController: - virtual void ShowContextMenuForView(views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) OVERRIDE; - - MessageViewController* controller_; // Weak, owns us. - NotifierId notifier_id_; - string16 display_source_; -}; - -MessageViewContextMenuController::MessageViewContextMenuController( - MessageViewController* controller, - const NotifierId& notifier_id, - const string16& display_source) - : controller_(controller), - notifier_id_(notifier_id), - display_source_(display_source) { -} - -MessageViewContextMenuController::~MessageViewContextMenuController() { -} - -void MessageViewContextMenuController::ShowContextMenuForView( - views::View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { - MenuModel menu_model(controller_, notifier_id_, display_source_); - if (menu_model.GetItemCount() == 0) - return; - - views::MenuRunner menu_runner(&menu_model); - - ignore_result(menu_runner.RunMenuAt( - source->GetWidget()->GetTopLevelWidget(), - NULL, - gfx::Rect(point, gfx::Size()), - views::MenuItemView::TOPRIGHT, - source_type, - views::MenuRunner::HAS_MNEMONICS)); -} - MessageView::MessageView(MessageViewController* controller, const std::string& notification_id, const NotifierId& notifier_id, - const string16& display_source) + const gfx::ImageSkia& small_image, + const base::string16& display_source) : controller_(controller), notification_id_(notification_id), notifier_id_(notifier_id), - context_menu_controller_( - new MessageViewContextMenuController(controller, - notifier_id, - display_source)), - scroller_(NULL) { + background_view_(NULL), + scroller_(NULL), + display_source_(display_source) { SetFocusable(true); - set_context_menu_controller(context_menu_controller_.get()); + + // Create the opaque background that's above the view's shadow. + background_view_ = new views::View(); + background_view_->set_background( + views::Background::CreateSolidBackground(kNotificationBackgroundColor)); + AddChildView(background_view_); + + views::ImageView* small_image_view = new views::ImageView(); + small_image_view->SetImage(small_image); + small_image_view->SetImageSize(gfx::Size(kSmallImageSize, kSmallImageSize)); + // The small image view should be added to view hierarchy by the derived + // class. This ensures that it is on top of other views. + small_image_view->set_owned_by_client(); + small_image_view_.reset(small_image_view); PaddedButton *close = new PaddedButton(this); close->SetPadding(-kCloseIconRightPadding, kCloseIconTopPadding); close->SetNormalImage(IDR_NOTIFICATION_CLOSE); close->SetHoveredImage(IDR_NOTIFICATION_CLOSE_HOVER); close->SetPressedImage(IDR_NOTIFICATION_CLOSE_PRESSED); - close->set_owned_by_client(); close->set_animate_on_state_change(false); close->SetAccessibleName(l10n_util::GetStringUTF16( IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME)); + // The close button should be added to view hierarchy by the derived class. + // This ensures that it is on top of other views. + close->set_owned_by_client(); close_button_.reset(close); focus_painter_ = views::Painter::CreateSolidFocusPainter( @@ -197,6 +83,11 @@ MessageView::MessageView(MessageViewController* controller, MessageView::~MessageView() { } +void MessageView::UpdateWithNotification(const Notification& notification) { + small_image_view_->SetImage(notification.small_image().AsImageSkia()); + display_source_ = notification.display_source(); +} + // static gfx::Insets MessageView::GetShadowInsets() { return gfx::Insets(kShadowBlur / 2 - kShadowOffset, @@ -206,10 +97,11 @@ gfx::Insets MessageView::GetShadowInsets() { } void MessageView::CreateShadowBorder() { - set_border(new views::ShadowBorder(kShadowBlur, - message_center::kShadowColor, - kShadowOffset, // Vertical offset. - 0)); // Horizontal offset. + SetBorder(scoped_ptr<views::Border>( + new views::ShadowBorder(kShadowBlur, + message_center::kShadowColor, + kShadowOffset, // Vertical offset. + 0))); // Horizontal offset. } bool MessageView::IsCloseButtonFocused() { @@ -221,8 +113,8 @@ void MessageView::RequestFocusOnCloseButton() { close_button_->RequestFocus(); } -void MessageView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; +void MessageView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_BUTTON; state->name = accessible_name_; } @@ -261,6 +153,8 @@ bool MessageView::OnKeyReleased(const ui::KeyEvent& event) { } void MessageView::OnPaint(gfx::Canvas* canvas) { + DCHECK_EQ(this, close_button_->parent()); + DCHECK_EQ(this, small_image_view_->parent()); SlideOutView::OnPaint(canvas); views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); } @@ -277,6 +171,29 @@ void MessageView::OnBlur() { SchedulePaint(); } +void MessageView::Layout() { + gfx::Rect content_bounds = GetContentsBounds(); + + // Background. + background_view_->SetBoundsRect(content_bounds); + + // Close button. + gfx::Size close_size(close_button_->GetPreferredSize()); + gfx::Rect close_rect(content_bounds.right() - close_size.width(), + content_bounds.y(), + close_size.width(), + close_size.height()); + close_button_->SetBoundsRect(close_rect); + + gfx::Size small_image_size(small_image_view_->GetPreferredSize()); + gfx::Rect small_image_rect(small_image_size); + small_image_rect.set_origin(gfx::Point( + content_bounds.right() - small_image_size.width() - kSmallImagePadding, + content_bounds.bottom() - small_image_size.height() - + kSmallImagePadding)); + small_image_view_->SetBoundsRect(small_image_rect); +} + void MessageView::OnGestureEvent(ui::GestureEvent* event) { if (event->type() == ui::ET_GESTURE_TAP) { controller_->ClickOnNotification(notification_id_); diff --git a/chromium/ui/message_center/views/message_view.h b/chromium/ui/message_center/views/message_view.h index ba42e3fc73c..04f4d97a06b 100644 --- a/chromium/ui/message_center/views/message_view.h +++ b/chromium/ui/message_center/views/message_view.h @@ -13,8 +13,13 @@ #include "ui/views/controls/button/button.h" #include "ui/views/controls/slide_out_view.h" +namespace ui { +class MenuModel; +} + namespace views { class ImageButton; +class ImageView; class Painter; class ScrollView; } @@ -28,29 +33,29 @@ class MessageViewController { virtual void ClickOnNotification(const std::string& notification_id) = 0; virtual void RemoveNotification(const std::string& notification_id, bool by_user) = 0; - virtual void DisableNotificationsFromThisSource( - const NotifierId& notifier_id) = 0; - virtual void ShowNotifierSettingsBubble() = 0; }; -class MessageViewContextMenuController; - // Individual notifications constants. const int kPaddingBetweenItems = 10; const int kPaddingHorizontal = 18; const int kWebNotificationButtonWidth = 32; const int kWebNotificationIconSize = 40; -// An abstract class that forms the basis of a view for a notification entry. +// An base class for a notification entry. Contains background, close button +// and other elements shared by derived notification views. class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView, public views::ButtonListener { public: MessageView(MessageViewController* controller, const std::string& notification_id, const NotifierId& notifier_id, - const string16& display_source); + const gfx::ImageSkia& small_image, + const base::string16& display_source); virtual ~MessageView(); + // Updates this view with the new data contained in the notification. + virtual void UpdateWithNotification(const Notification& notification); + // Returns the insets for the shadow it will have for rich notification. static gfx::Insets GetShadowInsets(); @@ -60,16 +65,19 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView, bool IsCloseButtonFocused(); void RequestFocusOnCloseButton(); - void set_accessible_name(const string16& name) { accessible_name_ = name; } + void set_accessible_name(const base::string16& accessible_name) { + accessible_name_ = accessible_name; + } // Overridden from views::View: - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE; virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; virtual void OnFocus() OVERRIDE; virtual void OnBlur() OVERRIDE; + virtual void Layout() OVERRIDE; // Overridden from ui::EventHandler: virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; @@ -81,11 +89,13 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView, void set_scroller(views::ScrollView* scroller) { scroller_ = scroller; } std::string notification_id() { return notification_id_; } NotifierId notifier_id() { return notifier_id_; } + const base::string16& display_source() const { return display_source_; } protected: // Overridden from views::SlideOutView: virtual void OnSlideOut() OVERRIDE; + views::ImageView* small_image() { return small_image_view_.get(); } views::ImageButton* close_button() { return close_button_.get(); } views::ScrollView* scroller() { return scroller_; } @@ -93,11 +103,14 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView, MessageViewController* controller_; std::string notification_id_; NotifierId notifier_id_; - scoped_ptr<MessageViewContextMenuController> context_menu_controller_; + views::View* background_view_; // Owned by views hierarchy. scoped_ptr<views::ImageButton> close_button_; + scoped_ptr<views::ImageView> small_image_view_; views::ScrollView* scroller_; - string16 accessible_name_; + base::string16 accessible_name_; + + base::string16 display_source_; scoped_ptr<views::Painter> focus_painter_; diff --git a/chromium/ui/message_center/views/message_view_context_menu_controller.cc b/chromium/ui/message_center/views/message_view_context_menu_controller.cc new file mode 100644 index 00000000000..3edaa29faa7 --- /dev/null +++ b/chromium/ui/message_center/views/message_view_context_menu_controller.cc @@ -0,0 +1,45 @@ +// Copyright 2013 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/views/message_view_context_menu_controller.h" + +#include "ui/base/models/menu_model.h" +#include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/message_view.h" +#include "ui/views/controls/menu/menu_runner.h" +#include "ui/views/widget/widget.h" + +namespace message_center { + +MessageViewContextMenuController::MessageViewContextMenuController( + MessageCenterController* controller) + : controller_(controller) { +} + +MessageViewContextMenuController::~MessageViewContextMenuController() { +} + +void MessageViewContextMenuController::ShowContextMenuForView( + views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { + // Assumes that the target view has to be MessageView. + MessageView* message_view = static_cast<MessageView*>(source); + scoped_ptr<ui::MenuModel> menu_model(controller_->CreateMenuModel( + message_view->notifier_id(), message_view->display_source())); + + if (!menu_model || menu_model->GetItemCount() == 0) + return; + + views::MenuRunner menu_runner(menu_model.get()); + + ignore_result(menu_runner.RunMenuAt(source->GetWidget()->GetTopLevelWidget(), + NULL, + gfx::Rect(point, gfx::Size()), + views::MENU_ANCHOR_TOPRIGHT, + source_type, + views::MenuRunner::HAS_MNEMONICS)); +} + +} // namespace message_center diff --git a/chromium/ui/message_center/views/message_view_context_menu_controller.h b/chromium/ui/message_center/views/message_view_context_menu_controller.h new file mode 100644 index 00000000000..5ee39de1a07 --- /dev/null +++ b/chromium/ui/message_center/views/message_view_context_menu_controller.h @@ -0,0 +1,34 @@ +// Copyright 2013 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. + +#ifndef UI_MESSAGE_CENTER_VIEWS_MESSAGE_VIEW_CONTEXT_MENU_CONTROLLER_H_ +#define UI_MESSAGE_CENTER_VIEWS_MESSAGE_VIEW_CONTEXT_MENU_CONTROLLER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ui/views/context_menu_controller.h" + +namespace message_center { +class MessageCenterController; + +class MessageViewContextMenuController : public views::ContextMenuController { + public: + explicit MessageViewContextMenuController( + MessageCenterController* controller); + virtual ~MessageViewContextMenuController(); + + private: + // Overridden from views::ContextMenuController: + virtual void ShowContextMenuForView(views::View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) OVERRIDE; + + MessageCenterController* controller_; + + DISALLOW_COPY_AND_ASSIGN(MessageViewContextMenuController); +}; + +} // namespace message_center + +#endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_VIEW_CONTEXT_MENU_CONTROLLER_H_ diff --git a/chromium/ui/message_center/views/notification_button.cc b/chromium/ui/message_center/views/notification_button.cc index 7ef5295ad22..75219e8d3dc 100644 --- a/chromium/ui/message_center/views/notification_button.cc +++ b/chromium/ui/message_center/views/notification_button.cc @@ -48,13 +48,13 @@ void NotificationButton::SetIcon(const gfx::ImageSkia& image) { icon_->SetImage(image); icon_->SetHorizontalAlignment(views::ImageView::LEADING); icon_->SetVerticalAlignment(views::ImageView::LEADING); - icon_->set_border(views::Border::CreateEmptyBorder( + icon_->SetBorder(views::Border::CreateEmptyBorder( message_center::kButtonIconTopPadding, 0, 0, 0)); AddChildViewAt(icon_, 0); } } -void NotificationButton::SetTitle(const string16& title) { +void NotificationButton::SetTitle(const base::string16& title) { if (title_ != NULL) delete title_; // This removes the title from this view's children. if (title.empty()) { @@ -64,19 +64,19 @@ void NotificationButton::SetTitle(const string16& title) { title_->SetHorizontalAlignment(gfx::ALIGN_LEFT); title_->SetEnabledColor(message_center::kRegularTextColor); title_->SetBackgroundColor(kRegularTextBackgroundColor); - title_->set_border(views::Border::CreateEmptyBorder( - kButtonTitleTopPadding, 0, 0, 0)); + title_->SetBorder( + views::Border::CreateEmptyBorder(kButtonTitleTopPadding, 0, 0, 0)); AddChildView(title_); } SetAccessibleName(title); } -gfx::Size NotificationButton::GetPreferredSize() { +gfx::Size NotificationButton::GetPreferredSize() const { return gfx::Size(message_center::kNotificationWidth, message_center::kButtonHeight); } -int NotificationButton::GetHeightForWidth(int width) { +int NotificationButton::GetHeightForWidth(int width) const { return message_center::kButtonHeight; } @@ -98,6 +98,14 @@ void NotificationButton::OnBlur() { SchedulePaint(); } +void NotificationButton::ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) { + // We disable view hierarchy change detection in the parent + // because it resets the hoverstate, which we do not want + // when we update the view to contain a new label or image. + views::View::ViewHierarchyChanged(details); +} + void NotificationButton::StateChanged() { if (state() == STATE_HOVERED || state() == STATE_PRESSED) { set_background(views::Background::CreateSolidBackground( diff --git a/chromium/ui/message_center/views/notification_button.h b/chromium/ui/message_center/views/notification_button.h index 7d3c52ce1ea..b5ac0f12981 100644 --- a/chromium/ui/message_center/views/notification_button.h +++ b/chromium/ui/message_center/views/notification_button.h @@ -24,14 +24,16 @@ class NotificationButton : public views::CustomButton { virtual ~NotificationButton(); void SetIcon(const gfx::ImageSkia& icon); - void SetTitle(const string16& title); + void SetTitle(const base::string16& title); // Overridden from views::View: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual int GetHeightForWidth(int width) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual int GetHeightForWidth(int width) const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; virtual void OnFocus() OVERRIDE; virtual void OnBlur() OVERRIDE; + virtual void ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) OVERRIDE; // Overridden from views::CustomButton: virtual void StateChanged() OVERRIDE; diff --git a/chromium/ui/message_center/views/notification_view.cc b/chromium/ui/message_center/views/notification_view.cc index 4a5d67326be..be77657f6e0 100644 --- a/chromium/ui/message_center/views/notification_view.cc +++ b/chromium/ui/message_center/views/notification_view.cc @@ -5,10 +5,12 @@ #include "ui/message_center/views/notification_view.h" #include "base/command_line.h" +#include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "grit/ui_resources.h" #include "grit/ui_strings.h" +#include "ui/base/cursor/cursor.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/layout.h" #include "ui/base/resource/resource_bundle.h" @@ -18,8 +20,6 @@ #include "ui/gfx/text_elider.h" #include "ui/message_center/message_center.h" #include "ui/message_center/message_center_style.h" -#include "ui/message_center/message_center_switches.h" -#include "ui/message_center/message_center_util.h" #include "ui/message_center/notification.h" #include "ui/message_center/notification_types.h" #include "ui/message_center/views/bounded_label.h" @@ -37,35 +37,27 @@ #include "ui/views/controls/progress_bar.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/fill_layout.h" +#include "ui/views/native_cursor.h" #include "ui/views/painter.h" #include "ui/views/widget/widget.h" -#if defined(USE_AURA) -#include "ui/base/cursor/cursor.h" -#endif - namespace { // Dimensions. const int kProgressBarWidth = message_center::kNotificationWidth - message_center::kTextLeftPadding - message_center::kTextRightPadding; const int kProgressBarBottomPadding = 0; -const int kExpandIconBottomPadding = 8; -const int kExpandIconRightPadding = 11; - -// static -views::Background* MakeBackground( - SkColor color = message_center::kNotificationBackgroundColor) { - return views::Background::CreateSolidBackground(color); -} // static -views::Border* MakeEmptyBorder(int top, int left, int bottom, int right) { +scoped_ptr<views::Border> MakeEmptyBorder(int top, + int left, + int bottom, + int right) { return views::Border::CreateEmptyBorder(top, left, bottom, right); } // static -views::Border* MakeTextBorder(int padding, int top, int bottom) { +scoped_ptr<views::Border> MakeTextBorder(int padding, int top, int bottom) { // Split the padding between the top and the bottom, then add the extra space. return MakeEmptyBorder(padding / 2 + top, message_center::kTextLeftPadding, @@ -74,7 +66,7 @@ views::Border* MakeTextBorder(int padding, int top, int bottom) { } // static -views::Border* MakeProgressBarBorder(int top, int bottom) { +scoped_ptr<views::Border> MakeProgressBarBorder(int top, int bottom) { return MakeEmptyBorder(top, message_center::kTextLeftPadding, bottom, @@ -82,7 +74,9 @@ views::Border* MakeProgressBarBorder(int top, int bottom) { } // static -views::Border* MakeSeparatorBorder(int top, int left, SkColor color) { +scoped_ptr<views::Border> MakeSeparatorBorder(int top, + int left, + SkColor color) { return views::Border::CreateSolidSidedBorder(top, left, 0, 0, color); } @@ -90,16 +84,12 @@ views::Border* MakeSeparatorBorder(int top, int left, SkColor color) { // Return true if and only if the image is null or has alpha. bool HasAlpha(gfx::ImageSkia& image, views::Widget* widget) { // Determine which bitmap to use. - ui::ScaleFactor factor = ui::SCALE_FACTOR_100P; - if (widget) { + float factor = 1.0f; + if (widget) factor = ui::GetScaleFactorForNativeView(widget->GetNativeView()); - if (factor == ui::SCALE_FACTOR_NONE) - factor = ui::SCALE_FACTOR_100P; - } // Extract that bitmap's alpha and look for a non-opaque pixel there. - SkBitmap bitmap = - image.GetRepresentation(ui::GetImageScale(factor)).sk_bitmap(); + SkBitmap bitmap = image.GetRepresentation(factor).sk_bitmap(); if (!bitmap.isNull()) { SkBitmap alpha; bitmap.extractAlpha(&alpha); @@ -175,19 +165,20 @@ views::View* MakeNotificationImage(const gfx::Image& image, gfx::Size size) { container->set_background(views::Background::CreateSolidBackground( message_center::kImageBackgroundColor)); - views::View* proportional_image_view = - new message_center::ProportionalImageView(image.AsImageSkia()); - gfx::Size ideal_size( message_center::kNotificationPreferredImageWidth, message_center::kNotificationPreferredImageHeight); - gfx::Size scaled_size = message_center::GetImageSizeForWidth( - message_center::kNotificationPreferredImageWidth, image.Size()); + gfx::Size scaled_size = + message_center::GetImageSizeForContainerSize(ideal_size, image.Size()); + + views::View* proportional_image_view = + new message_center::ProportionalImageView(image.AsImageSkia(), + ideal_size); // This calculation determines that the new image would have the correct // height for width. if (ideal_size != scaled_size) { - proportional_image_view->set_border(views::Border::CreateSolidBorder( + proportional_image_view->SetBorder(views::Border::CreateSolidBorder( message_center::kNotificationImageBorderSize, SK_ColorTRANSPARENT)); } @@ -204,7 +195,7 @@ class NotificationProgressBar : public views::ProgressBar { private: // Overriden from View - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; DISALLOW_COPY_AND_ASSIGN(NotificationProgressBar); @@ -216,7 +207,7 @@ NotificationProgressBar::NotificationProgressBar() { NotificationProgressBar::~NotificationProgressBar() { } -gfx::Size NotificationProgressBar::GetPreferredSize() { +gfx::Size NotificationProgressBar::GetPreferredSize() const { gfx::Size pref_size(kProgressBarWidth, message_center::kProgressBarThickness); gfx::Insets insets = GetInsets(); pref_size.Enlarge(insets.width(), insets.height()); @@ -265,7 +256,6 @@ namespace message_center { // static NotificationView* NotificationView::Create(MessageCenterController* controller, const Notification& notification, - bool expanded, bool top_level) { switch (notification.type()) { case NOTIFICATION_TYPE_BASE_FORMAT: @@ -287,7 +277,7 @@ NotificationView* NotificationView::Create(MessageCenterController* controller, // Currently all roads lead to the generic NotificationView. NotificationView* notification_view = - new NotificationView(controller, notification, expanded); + new NotificationView(controller, notification); #if defined(OS_LINUX) && !defined(OS_CHROMEOS) // Don't create shadows for notification toasts on linux wih aura. @@ -299,192 +289,86 @@ NotificationView* NotificationView::Create(MessageCenterController* controller, return notification_view; } +void NotificationView::CreateOrUpdateViews(const Notification& notification) { + CreateOrUpdateTitleView(notification); + CreateOrUpdateMessageView(notification); + CreateOrUpdateContextMessageView(notification); + CreateOrUpdateProgressBarView(notification); + CreateOrUpdateListItemViews(notification); + CreateOrUpdateIconView(notification); + CreateOrUpdateImageView(notification); + CreateOrUpdateActionButtonViews(notification); +} + +void NotificationView::SetAccessibleName(const Notification& notification) { + std::vector<base::string16> accessible_lines; + accessible_lines.push_back(notification.title()); + accessible_lines.push_back(notification.message()); + accessible_lines.push_back(notification.context_message()); + std::vector<NotificationItem> items = notification.items(); + for (size_t i = 0; i < items.size() && i < kNotificationMaximumItems; ++i) { + accessible_lines.push_back(items[i].title + base::ASCIIToUTF16(" ") + + items[i].message); + } + set_accessible_name(JoinString(accessible_lines, '\n')); +} + NotificationView::NotificationView(MessageCenterController* controller, - const Notification& notification, - bool expanded) + const Notification& notification) : MessageView(this, notification.id(), notification.notifier_id(), + notification.small_image().AsImageSkia(), notification.display_source()), controller_(controller), clickable_(notification.clickable()), - is_expanded_(expanded) { - std::vector<string16> accessible_lines; - - // Create the opaque background that's above the view's shadow. - background_view_ = new views::View(); - background_view_->set_background(MakeBackground()); - + top_view_(NULL), + title_view_(NULL), + message_view_(NULL), + context_message_view_(NULL), + icon_view_(NULL), + bottom_view_(NULL), + image_view_(NULL), + progress_bar_view_(NULL) { // Create the top_view_, which collects into a vertical box all content // at the top of the notification (to the right of the icon) except for the // close button. top_view_ = new views::View(); top_view_->SetLayoutManager( new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); - top_view_->set_border(MakeEmptyBorder( - kTextTopPadding - 8, 0, kTextBottomPadding - 5, 0)); - - const gfx::FontList default_label_font_list = views::Label().font_list(); - - // Create the title view if appropriate. - title_view_ = NULL; - if (!notification.title().empty()) { - const gfx::FontList& font_list = - default_label_font_list.DeriveFontListWithSizeDelta(2); - int padding = kTitleLineHeight - font_list.GetHeight(); - title_view_ = new BoundedLabel( - gfx::TruncateString(notification.title(), kTitleCharacterLimit), - font_list); - title_view_->SetLineHeight(kTitleLineHeight); - title_view_->SetLineLimit(IsExperimentalNotificationUIEnabled() ? - message_center::kExperimentalTitleLineLimit : - message_center::kTitleLineLimit); - title_view_->SetColors(message_center::kRegularTextColor, - kRegularTextBackgroundColor); - title_view_->set_border(MakeTextBorder(padding, 3, 0)); - top_view_->AddChildView(title_view_); - accessible_lines.push_back(notification.title()); - } - - // Create the message view if appropriate. - message_view_ = NULL; - if (!notification.message().empty()) { - int padding = kMessageLineHeight - default_label_font_list.GetHeight(); - message_view_ = new BoundedLabel( - gfx::TruncateString(notification.message(), kMessageCharacterLimit)); - message_view_->SetLineHeight(kMessageLineHeight); - message_view_->SetVisible(!is_expanded_ || !notification.items().size()); - message_view_->SetColors(message_center::kRegularTextColor, - kDimTextBackgroundColor); - message_view_->set_border(MakeTextBorder(padding, 4, 0)); - top_view_->AddChildView(message_view_); - accessible_lines.push_back(notification.message()); - } - - // Create the context message view if appropriate. - context_message_view_ = NULL; - if (!notification.context_message().empty()) { - int padding = kMessageLineHeight - default_label_font_list.GetHeight(); - context_message_view_ = - new BoundedLabel(gfx::TruncateString(notification.context_message(), - kContextMessageCharacterLimit), - default_label_font_list); - context_message_view_->SetLineLimit( - message_center::kContextMessageLineLimit); - context_message_view_->SetLineHeight(kMessageLineHeight); - context_message_view_->SetColors(message_center::kDimTextColor, - kContextTextBackgroundColor); - context_message_view_->set_border(MakeTextBorder(padding, 4, 0)); - top_view_->AddChildView(context_message_view_); - accessible_lines.push_back(notification.context_message()); - } - - // Create the progress bar view. - progress_bar_view_ = NULL; - if (notification.type() == NOTIFICATION_TYPE_PROGRESS) { - progress_bar_view_ = new NotificationProgressBar(); - progress_bar_view_->set_border(MakeProgressBarBorder( - message_center::kProgressBarTopPadding, kProgressBarBottomPadding)); - progress_bar_view_->SetValue(notification.progress() / 100.0); - top_view_->AddChildView(progress_bar_view_); - } - - // Create the list item views (up to a maximum). - int padding = kMessageLineHeight - default_label_font_list.GetHeight(); - std::vector<NotificationItem> items = notification.items(); - for (size_t i = 0; i < items.size() && i < kNotificationMaximumItems; ++i) { - ItemView* item_view = new ItemView(items[i]); - item_view->SetVisible(is_expanded_); - item_view->set_border(MakeTextBorder(padding, i ? 0 : 4, 0)); - item_views_.push_back(item_view); - top_view_->AddChildView(item_view); - accessible_lines.push_back( - items[i].title + base::ASCIIToUTF16(" ") + items[i].message); - } - - // Create the notification icon view. - gfx::ImageSkia icon = notification.icon().AsImageSkia(); - if (notification.type() == NOTIFICATION_TYPE_SIMPLE && - (icon.width() != kIconSize || - icon.height() != kIconSize || - HasAlpha(icon, GetWidget()))) { - views::ImageView* icon_view = new views::ImageView(); - icon_view->SetImage(icon); - icon_view->SetImageSize(gfx::Size(kLegacyIconSize, kLegacyIconSize)); - icon_view->SetHorizontalAlignment(views::ImageView::CENTER); - icon_view->SetVerticalAlignment(views::ImageView::CENTER); - icon_view_ = icon_view; - } else { - icon_view_ = new ProportionalImageView(icon); - } - - icon_view_->set_background(MakeBackground(kIconBackgroundColor)); - + top_view_->SetBorder( + MakeEmptyBorder(kTextTopPadding - 8, 0, kTextBottomPadding - 5, 0)); + AddChildView(top_view_); // Create the bottom_view_, which collects into a vertical box all content - // below the notification icon except for the expand button. + // below the notification icon. bottom_view_ = new views::View(); bottom_view_->SetLayoutManager( new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); + AddChildView(bottom_view_); - // Create the image view if appropriate. - image_view_ = NULL; - if (!notification.image().IsEmpty()) { - gfx::Size image_size( - kNotificationPreferredImageWidth, kNotificationPreferredImageHeight); - image_view_ = MakeNotificationImage(notification.image(), image_size); - image_view_->SetVisible(is_expanded_); - bottom_view_->AddChildView(image_view_); - } - - // Create action buttons if appropriate. - std::vector<ButtonInfo> buttons = notification.buttons(); - for (size_t i = 0; i < buttons.size(); ++i) { - views::View* separator = new views::ImageView(); - separator->set_border(MakeSeparatorBorder(1, 0, kButtonSeparatorColor)); - bottom_view_->AddChildView(separator); - NotificationButton* button = new NotificationButton(this); - ButtonInfo button_info = buttons[i]; - button->SetTitle(button_info.title); - button->SetIcon(button_info.icon.AsImageSkia()); - action_buttons_.push_back(button); - bottom_view_->AddChildView(button); - } - - // Create expand button - expand_button_ = new PaddedButton(this); - expand_button_->SetPadding(-kExpandIconRightPadding, - -kExpandIconBottomPadding); - expand_button_->SetNormalImage(IDR_NOTIFICATION_EXPAND); - expand_button_->SetHoveredImage(IDR_NOTIFICATION_EXPAND_HOVER); - expand_button_->SetPressedImage(IDR_NOTIFICATION_EXPAND_PRESSED); - expand_button_->set_animate_on_state_change(false); - expand_button_->SetAccessibleName(l10n_util::GetStringUTF16( - IDS_MESSAGE_CENTER_EXPAND_NOTIFICATION_BUTTON_ACCESSIBLE_NAME)); + CreateOrUpdateViews(notification); // Put together the different content and control views. Layering those allows - // for proper layout logic and it also allows the close and expand buttons to - // overlap the content as needed to provide large enough click and touch areas - // (<http://crbug.com/168822> and <http://crbug.com/168856>). - AddChildView(background_view_); - AddChildView(top_view_); - AddChildView(icon_view_); - AddChildView(bottom_view_); + // for proper layout logic and it also allows the close button and small + // image to overlap the content as needed to provide large enough click and + // touch areas (<http://crbug.com/168822> and <http://crbug.com/168856>). + AddChildView(small_image()); AddChildView(close_button()); - AddChildView(expand_button_); - set_accessible_name(JoinString(accessible_lines, '\n')); + SetAccessibleName(notification); } NotificationView::~NotificationView() { } -gfx::Size NotificationView::GetPreferredSize() { - int top_width = top_view_->GetPreferredSize().width(); +gfx::Size NotificationView::GetPreferredSize() const { + int top_width = top_view_->GetPreferredSize().width() + + icon_view_->GetPreferredSize().width(); int bottom_width = bottom_view_->GetPreferredSize().width(); int preferred_width = std::max(top_width, bottom_width) + GetInsets().width(); return gfx::Size(preferred_width, GetHeightForWidth(preferred_width)); } -int NotificationView::GetHeightForWidth(int width) { +int NotificationView::GetHeightForWidth(int width) const { // Get the height assuming no line limit changes. int content_width = width - GetInsets().width(); int top_height = top_view_->GetHeightForWidth(content_width); @@ -494,8 +378,13 @@ int NotificationView::GetHeightForWidth(int width) { // line limit would be different for the specified width than it currently is. // TODO(dharcourt): Avoid BoxLayout and directly compute the correct height. if (message_view_) { + int title_lines = 0; + if (title_view_) { + title_lines = title_view_->GetLinesForWidthAndLimit(width, + kMaxTitleLines); + } int used_limit = message_view_->GetLineLimit(); - int correct_limit = GetMessageLineLimit(width); + int correct_limit = GetMessageLineLimit(title_lines, width); if (used_limit != correct_limit) { top_height -= GetMessageHeight(content_width, used_limit); top_height += GetMessageHeight(content_width, correct_limit); @@ -514,17 +403,18 @@ int NotificationView::GetHeightForWidth(int width) { } void NotificationView::Layout() { + MessageView::Layout(); gfx::Insets insets = GetInsets(); int content_width = width() - insets.width(); - int content_right = width() - insets.right(); // Before any resizing, set or adjust the number of message lines. + int title_lines = 0; + if (title_view_) { + title_lines = + title_view_->GetLinesForWidthAndLimit(width(), kMaxTitleLines); + } if (message_view_) - message_view_->SetLineLimit(GetMessageLineLimit(width())); - - // Background. - background_view_->SetBounds(insets.left(), insets.top(), - content_width, height() - insets.height()); + message_view_->SetLineLimit(GetMessageLineLimit(title_lines, width())); // Top views. int top_height = top_view_->GetHeightForWidth(content_width); @@ -538,18 +428,6 @@ void NotificationView::Layout() { int bottom_height = bottom_view_->GetHeightForWidth(content_width); bottom_view_->SetBounds(insets.left(), bottom_y, content_width, bottom_height); - - // Close button. - gfx::Size close_size(close_button()->GetPreferredSize()); - close_button()->SetBounds(content_right - close_size.width(), insets.top(), - close_size.width(), close_size.height()); - - // Expand button. - gfx::Size expand_size(expand_button_->GetPreferredSize()); - int expand_y = bottom_y - expand_size.height(); - expand_button_->SetVisible(IsExpansionNeeded(width())); - expand_button_->SetBounds(content_right - expand_size.width(), expand_y, - expand_size.width(), expand_size.height()); } void NotificationView::OnFocus() { @@ -571,9 +449,9 @@ views::View* NotificationView::GetEventHandlerForRect(const gfx::Rect& rect) { // Want to return this for underlying views, otherwise GetCursor is not // called. But buttons are exceptions, they'll have their own event handlings. - std::vector<views::View*> buttons(action_buttons_); + std::vector<views::View*> buttons(action_buttons_.begin(), + action_buttons_.end()); buttons.push_back(close_button()); - buttons.push_back(expand_button_); for (size_t i = 0; i < buttons.size(); ++i) { gfx::Point point_in_child = point; @@ -589,38 +467,33 @@ gfx::NativeCursor NotificationView::GetCursor(const ui::MouseEvent& event) { if (!clickable_ || !controller_->HasClickedListener(notification_id())) return views::View::GetCursor(event); -#if defined(USE_AURA) - return ui::kCursorHand; -#elif defined(OS_WIN) - static HCURSOR g_hand_cursor = LoadCursor(NULL, IDC_HAND); - return g_hand_cursor; -#endif + return views::GetNativeHandCursor(); +} + +void NotificationView::UpdateWithNotification( + const Notification& notification) { + MessageView::UpdateWithNotification(notification); + + CreateOrUpdateViews(notification); + SetAccessibleName(notification); + Layout(); + SchedulePaint(); } void NotificationView::ButtonPressed(views::Button* sender, const ui::Event& event) { + // Certain operations can cause |this| to be destructed, so copy the members + // we send to other parts of the code. + // TODO(dewittj): Remove this hack. + std::string id(notification_id()); // See if the button pressed was an action button. for (size_t i = 0; i < action_buttons_.size(); ++i) { if (sender == action_buttons_[i]) { - controller_->ClickOnNotificationButton(notification_id(), i); + controller_->ClickOnNotificationButton(id, i); return; } } - // Adjust notification subviews for expansion. - if (sender == expand_button_) { - if (message_view_ && item_views_.size()) - message_view_->SetVisible(false); - for (size_t i = 0; i < item_views_.size(); ++i) - item_views_[i]->SetVisible(true); - if (image_view_) - image_view_->SetVisible(true); - - is_expanded_ = true; - controller_->ExpandNotification(notification_id()); - return; - } - // Let the superclass handled anything other than action buttons. // Warning: This may cause the NotificationView itself to be deleted, // so don't do anything afterwards. @@ -636,34 +509,256 @@ void NotificationView::RemoveNotification(const std::string& notification_id, controller_->RemoveNotification(notification_id, by_user); } -void NotificationView::DisableNotificationsFromThisSource( - const NotifierId& notifier_id) { - controller_->DisableNotificationsFromThisSource(notifier_id); +void NotificationView::CreateOrUpdateTitleView( + const Notification& notification) { + if (notification.title().empty()) { + if (title_view_) { + // Deletion will also remove |title_view_| from its parent. + delete title_view_; + title_view_ = NULL; + } + return; + } + + DCHECK(top_view_ != NULL); + + const gfx::FontList& font_list = + views::Label().font_list().DeriveWithSizeDelta(2); + + int title_character_limit = + kNotificationWidth * kMaxTitleLines / kMinPixelsPerTitleCharacter; + + if (!title_view_) { + int padding = kTitleLineHeight - font_list.GetHeight(); + + title_view_ = new BoundedLabel( + gfx::TruncateString(notification.title(), title_character_limit), + font_list); + title_view_->SetLineHeight(kTitleLineHeight); + title_view_->SetLineLimit(kMaxTitleLines); + title_view_->SetColors(message_center::kRegularTextColor, + kRegularTextBackgroundColor); + title_view_->SetBorder(MakeTextBorder(padding, 3, 0)); + top_view_->AddChildView(title_view_); + } else { + title_view_->SetText( + gfx::TruncateString(notification.title(), title_character_limit)); + } +} + +void NotificationView::CreateOrUpdateMessageView( + const Notification& notification) { + if (notification.message().empty()) { + if (message_view_) { + // Deletion will also remove |message_view_| from its parent. + delete message_view_; + message_view_ = NULL; + } + return; + } + + DCHECK(top_view_ != NULL); + + if (!message_view_) { + int padding = kMessageLineHeight - views::Label().font_list().GetHeight(); + message_view_ = new BoundedLabel( + gfx::TruncateString(notification.message(), kMessageCharacterLimit)); + message_view_->SetLineHeight(kMessageLineHeight); + message_view_->SetColors(message_center::kRegularTextColor, + kDimTextBackgroundColor); + message_view_->SetBorder(MakeTextBorder(padding, 4, 0)); + top_view_->AddChildView(message_view_); + } else { + message_view_->SetText( + gfx::TruncateString(notification.message(), kMessageCharacterLimit)); + } + + message_view_->SetVisible(!notification.items().size()); } -void NotificationView::ShowNotifierSettingsBubble() { - controller_->ShowNotifierSettingsBubble(); +void NotificationView::CreateOrUpdateContextMessageView( + const Notification& notification) { + if (notification.context_message().empty()) { + if (context_message_view_) { + // Deletion will also remove |context_message_view_| from its parent. + delete context_message_view_; + context_message_view_ = NULL; + } + return; + } + + DCHECK(top_view_ != NULL); + + if (!context_message_view_) { + int padding = kMessageLineHeight - views::Label().font_list().GetHeight(); + context_message_view_ = new BoundedLabel(gfx::TruncateString( + notification.context_message(), kContextMessageCharacterLimit)); + context_message_view_->SetLineLimit( + message_center::kContextMessageLineLimit); + context_message_view_->SetLineHeight(kMessageLineHeight); + context_message_view_->SetColors(message_center::kDimTextColor, + kContextTextBackgroundColor); + context_message_view_->SetBorder(MakeTextBorder(padding, 4, 0)); + top_view_->AddChildView(context_message_view_); + } else { + context_message_view_->SetText(gfx::TruncateString( + notification.context_message(), kContextMessageCharacterLimit)); + } } -bool NotificationView::IsExpansionNeeded(int width) { - return (!is_expanded_ && - (image_view_ || - item_views_.size() || - IsMessageExpansionNeeded(width))); +void NotificationView::CreateOrUpdateProgressBarView( + const Notification& notification) { + if (notification.type() != NOTIFICATION_TYPE_PROGRESS) { + if (progress_bar_view_) { + // Deletion will also remove |progress_bar_view_| from its parent. + delete progress_bar_view_; + progress_bar_view_ = NULL; + } + return; + } + + DCHECK(top_view_ != NULL); + + if (!progress_bar_view_) { + progress_bar_view_ = new NotificationProgressBar(); + progress_bar_view_->SetBorder(MakeProgressBarBorder( + message_center::kProgressBarTopPadding, kProgressBarBottomPadding)); + top_view_->AddChildView(progress_bar_view_); + } + + progress_bar_view_->SetValue(notification.progress() / 100.0); + progress_bar_view_->SetVisible(!notification.items().size()); } -bool NotificationView::IsMessageExpansionNeeded(int width) { - int current = GetMessageLines(width, GetMessageLineLimit(width)); - int expanded = GetMessageLines(width, - message_center::kMessageExpandedLineLimit); - return current < expanded; +void NotificationView::CreateOrUpdateListItemViews( + const Notification& notification) { + for (size_t i = 0; i < item_views_.size(); ++i) + delete item_views_[i]; + item_views_.clear(); + + int padding = kMessageLineHeight - views::Label().font_list().GetHeight(); + std::vector<NotificationItem> items = notification.items(); + + if (items.size() == 0) + return; + + DCHECK(top_view_); + for (size_t i = 0; i < items.size() && i < kNotificationMaximumItems; ++i) { + ItemView* item_view = new ItemView(items[i]); + item_view->SetBorder(MakeTextBorder(padding, i ? 0 : 4, 0)); + item_views_.push_back(item_view); + top_view_->AddChildView(item_view); + } } -int NotificationView::GetMessageLineLimit(int width) { - // Expanded notifications get a larger limit, except for image notifications, - // whose images must be kept flush against their icons. - if (is_expanded_ && !image_view_) - return message_center::kMessageExpandedLineLimit; +void NotificationView::CreateOrUpdateIconView( + const Notification& notification) { + if (icon_view_) { + delete icon_view_; + icon_view_ = NULL; + } + + // TODO(dewittj): Detect a compatible update and use the existing icon view. + gfx::ImageSkia icon = notification.icon().AsImageSkia(); + if (notification.type() == NOTIFICATION_TYPE_SIMPLE && + (icon.width() != kIconSize || icon.height() != kIconSize || + HasAlpha(icon, GetWidget()))) { + views::ImageView* icon_view = new views::ImageView(); + icon_view->SetImage(icon); + icon_view->SetImageSize(gfx::Size(kLegacyIconSize, kLegacyIconSize)); + icon_view->SetHorizontalAlignment(views::ImageView::CENTER); + icon_view->SetVerticalAlignment(views::ImageView::CENTER); + icon_view_ = icon_view; + } else { + icon_view_ = + new ProportionalImageView(icon, gfx::Size(kIconSize, kIconSize)); + } + + icon_view_->set_background( + views::Background::CreateSolidBackground(kIconBackgroundColor)); + + AddChildView(icon_view_); +} + +void NotificationView::CreateOrUpdateImageView( + const Notification& notification) { + if (image_view_) { + delete image_view_; + image_view_ = NULL; + } + + DCHECK(bottom_view_); + DCHECK_EQ(this, bottom_view_->parent()); + + // TODO(dewittj): Detect a compatible update and use the existing image view. + if (!notification.image().IsEmpty()) { + gfx::Size image_size(kNotificationPreferredImageWidth, + kNotificationPreferredImageHeight); + image_view_ = MakeNotificationImage(notification.image(), image_size); + bottom_view_->AddChildViewAt(image_view_, 0); + } +} + +void NotificationView::CreateOrUpdateActionButtonViews( + const Notification& notification) { + std::vector<ButtonInfo> buttons = notification.buttons(); + bool new_buttons = action_buttons_.size() != buttons.size(); + + if (new_buttons || buttons.size() == 0) { + // STLDeleteElements also clears the container. + STLDeleteElements(&separators_); + STLDeleteElements(&action_buttons_); + } + + DCHECK(bottom_view_); + DCHECK_EQ(this, bottom_view_->parent()); + + for (size_t i = 0; i < buttons.size(); ++i) { + ButtonInfo button_info = buttons[i]; + if (new_buttons) { + views::View* separator = new views::ImageView(); + separator->SetBorder(MakeSeparatorBorder(1, 0, kButtonSeparatorColor)); + separators_.push_back(separator); + bottom_view_->AddChildView(separator); + NotificationButton* button = new NotificationButton(this); + button->SetTitle(button_info.title); + button->SetIcon(button_info.icon.AsImageSkia()); + action_buttons_.push_back(button); + bottom_view_->AddChildView(button); + } else { + action_buttons_[i]->SetTitle(button_info.title); + action_buttons_[i]->SetIcon(button_info.icon.AsImageSkia()); + action_buttons_[i]->SchedulePaint(); + action_buttons_[i]->Layout(); + } + } + + if (new_buttons) { + Layout(); + views::Widget* widget = GetWidget(); + if (widget != NULL) { + widget->SetSize(widget->GetContentsView()->GetPreferredSize()); + GetWidget()->SynthesizeMouseMoveEvent(); + } + } +} + +int NotificationView::GetMessageLineLimit(int title_lines, int width) const { + // Image notifications require that the image must be kept flush against + // their icons, but we can allow more text if no image. + int effective_title_lines = std::max(0, title_lines - 1); + int line_reduction_from_title = (image_view_ ? 1 : 2) * effective_title_lines; + if (!image_view_) { + // Title lines are counted as twice as big as message lines for the purpose + // of this calculation. + // The effect from the title reduction here should be: + // * 0 title lines: 5 max lines message. + // * 1 title line: 5 max lines message. + // * 2 title lines: 3 max lines message. + return std::max( + 0, + message_center::kMessageExpandedLineLimit - line_reduction_from_title); + } int message_line_limit = message_center::kMessageCollapsedLineLimit; @@ -674,16 +769,17 @@ int NotificationView::GetMessageLineLimit(int width) { message_center::kContextMessageLineLimit); } - DCHECK_GT(message_line_limit, 0); - return message_line_limit; -} + // The effect from the title reduction here should be: + // * 0 title lines: 2 max lines message + context message. + // * 1 title line: 2 max lines message + context message. + // * 2 title lines: 1 max lines message + context message. + message_line_limit = + std::max(0, message_line_limit - line_reduction_from_title); -int NotificationView::GetMessageLines(int width, int limit) { - return message_view_ ? - message_view_->GetLinesForWidthAndLimit(width, limit) : 0; + return message_line_limit; } -int NotificationView::GetMessageHeight(int width, int limit) { +int NotificationView::GetMessageHeight(int width, int limit) const { return message_view_ ? message_view_->GetSizeForWidthAndLines(width, limit).height() : 0; } diff --git a/chromium/ui/message_center/views/notification_view.h b/chromium/ui/message_center/views/notification_view.h index ba47dda9706..2d6913124cc 100644 --- a/chromium/ui/message_center/views/notification_view.h +++ b/chromium/ui/message_center/views/notification_view.h @@ -19,6 +19,7 @@ namespace message_center { class BoundedLabel; class MessageCenter; class MessageCenterController; +class NotificationButton; class NotificationView; class PaddedButton; @@ -38,14 +39,12 @@ class MESSAGE_CENTER_EXPORT NotificationView : public MessageView, // |controller| may be NULL, but has to be set before the view is shown. static NotificationView* Create(MessageCenterController* controller, const Notification& notification, - bool expanded, bool top_level); - - virtual ~NotificationView(); + virtual ~NotificationView(); // Overridden from views::View: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual int GetHeightForWidth(int width) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual int GetHeightForWidth(int width) const OVERRIDE; virtual void Layout() OVERRIDE; virtual void OnFocus() OVERRIDE; virtual void ScrollRectToVisible(const gfx::Rect& rect) OVERRIDE; @@ -53,6 +52,8 @@ class MESSAGE_CENTER_EXPORT NotificationView : public MessageView, virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) OVERRIDE; // Overridden from MessageView: + virtual void UpdateWithNotification( + const Notification& notification) OVERRIDE; virtual void ButtonPressed(views::Button* sender, const ui::Event& event) OVERRIDE; @@ -60,9 +61,6 @@ class MESSAGE_CENTER_EXPORT NotificationView : public MessageView, virtual void ClickOnNotification(const std::string& notification_id) OVERRIDE; virtual void RemoveNotification(const std::string& notification_id, bool by_user) OVERRIDE; - virtual void DisableNotificationsFromThisSource( - const NotifierId& notifier_id) OVERRIDE; - virtual void ShowNotifierSettingsBubble() OVERRIDE; void set_controller(MessageCenterController* controller) { controller_ = controller; @@ -70,24 +68,37 @@ class MESSAGE_CENTER_EXPORT NotificationView : public MessageView, protected: NotificationView(MessageCenterController* controller, - const Notification& notification, - bool expanded); + const Notification& notification); private: - bool IsExpansionNeeded(int width); - bool IsMessageExpansionNeeded(int width); - int GetMessageLineLimit(int width); - int GetMessageLines(int width, int limit); - int GetMessageHeight(int width, int limit); + FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, CreateOrUpdateTest); + FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, TestLineLimits); + FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, UpdateButtonsStateTest); + FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, UpdateButtonCountTest); + + friend class NotificationViewTest; + + void CreateOrUpdateViews(const Notification& notification); + void SetAccessibleName(const Notification& notification); + + void CreateOrUpdateTitleView(const Notification& notification); + void CreateOrUpdateMessageView(const Notification& notification); + void CreateOrUpdateContextMessageView(const Notification& notification); + void CreateOrUpdateProgressBarView(const Notification& notification); + void CreateOrUpdateListItemViews(const Notification& notification); + void CreateOrUpdateIconView(const Notification& notification); + void CreateOrUpdateImageView(const Notification& notification); + void CreateOrUpdateActionButtonViews(const Notification& notification); + + int GetMessageLineLimit(int title_lines, int width) const; + int GetMessageHeight(int width, int limit) const; MessageCenterController* controller_; // Weak, lives longer then views. // Describes whether the view should display a hand pointer or not. bool clickable_; - bool is_expanded_; // Weak references to NotificationView descendants owned by their parents. - views::View* background_view_; views::View* top_view_; BoundedLabel* title_view_; BoundedLabel* message_view_; @@ -97,8 +108,8 @@ class MESSAGE_CENTER_EXPORT NotificationView : public MessageView, views::View* bottom_view_; views::View* image_view_; views::ProgressBar* progress_bar_view_; - std::vector<views::View*> action_buttons_; - PaddedButton* expand_button_; + std::vector<NotificationButton*> action_buttons_; + std::vector<views::View*> separators_; DISALLOW_COPY_AND_ASSIGN(NotificationView); }; diff --git a/chromium/ui/message_center/views/notification_view_unittest.cc b/chromium/ui/message_center/views/notification_view_unittest.cc new file mode 100644 index 00000000000..b6d67526905 --- /dev/null +++ b/chromium/ui/message_center/views/notification_view_unittest.cc @@ -0,0 +1,323 @@ +// Copyright 2014 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/views/notification_view.h" + +#include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/image/image.h" +#include "ui/message_center/notification.h" +#include "ui/message_center/notification_list.h" +#include "ui/message_center/notification_types.h" +#include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/notification_button.h" +#include "ui/views/layout/fill_layout.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/widget_delegate.h" + +namespace message_center { + +/* Test fixture ***************************************************************/ + +class NotificationViewTest : public views::ViewsTestBase, + public MessageCenterController { + public: + NotificationViewTest(); + virtual ~NotificationViewTest(); + + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + views::Widget* widget() { return notification_view_->GetWidget(); } + NotificationView* notification_view() { return notification_view_.get(); } + Notification* notification() { return notification_.get(); } + RichNotificationData* data() { return data_.get(); } + + // Overridden from MessageCenterController: + virtual void ClickOnNotification(const std::string& notification_id) OVERRIDE; + virtual void RemoveNotification(const std::string& notification_id, + bool by_user) OVERRIDE; + virtual scoped_ptr<ui::MenuModel> CreateMenuModel( + const NotifierId& notifier_id, + const base::string16& display_source) OVERRIDE; + virtual bool HasClickedListener(const std::string& notification_id) OVERRIDE; + virtual void ClickOnNotificationButton(const std::string& notification_id, + int button_index) OVERRIDE; + + protected: + const gfx::Image CreateTestImage(int width, int height) { + return gfx::Image::CreateFrom1xBitmap(CreateBitmap(width, height)); + } + + const SkBitmap CreateBitmap(int width, int height) { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + bitmap.allocPixels(); + bitmap.eraseRGB(0, 255, 0); + return bitmap; + } + + std::vector<ButtonInfo> CreateButtons(int number) { + ButtonInfo info(base::ASCIIToUTF16("Test button title.")); + info.icon = CreateTestImage(4, 4); + return std::vector<ButtonInfo>(number, info); + } + + void CheckVerticalOrderInNotification() { + std::vector<views::View*> vertical_order; + vertical_order.push_back(notification_view()->top_view_); + vertical_order.push_back(notification_view()->image_view_); + std::copy(notification_view()->action_buttons_.begin(), + notification_view()->action_buttons_.end(), + std::back_inserter(vertical_order)); + std::vector<views::View*>::iterator current = vertical_order.begin(); + std::vector<views::View*>::iterator last = current++; + while (current != vertical_order.end()) { + gfx::Point last_point = (*last)->bounds().origin(); + views::View::ConvertPointToTarget( + (*last), notification_view(), &last_point); + + gfx::Point current_point = (*current)->bounds().origin(); + views::View::ConvertPointToTarget( + (*current), notification_view(), ¤t_point); + + EXPECT_LT(last_point.y(), current_point.y()); + last = current++; + } + } + + void UpdateNotificationViews() { + notification_view()->CreateOrUpdateViews(*notification()); + notification_view()->Layout(); + } + + private: + scoped_ptr<RichNotificationData> data_; + scoped_ptr<Notification> notification_; + scoped_ptr<NotificationView> notification_view_; + + DISALLOW_COPY_AND_ASSIGN(NotificationViewTest); +}; + +NotificationViewTest::NotificationViewTest() { +} + +NotificationViewTest::~NotificationViewTest() { +} + +void NotificationViewTest::SetUp() { + views::ViewsTestBase::SetUp(); + // Create a dummy notification. + SkBitmap bitmap; + data_.reset(new RichNotificationData()); + notification_.reset( + new Notification(NOTIFICATION_TYPE_BASE_FORMAT, + std::string("notification id"), + base::UTF8ToUTF16("title"), + base::UTF8ToUTF16("message"), + CreateTestImage(80, 80), + base::UTF8ToUTF16("display source"), + NotifierId(NotifierId::APPLICATION, "extension_id"), + *data_, + NULL)); + notification_->set_small_image(CreateTestImage(16, 16)); + notification_->set_image(CreateTestImage(320, 240)); + + // Then create a new NotificationView with that single notification. + notification_view_.reset( + NotificationView::Create(this, *notification_, true)); + notification_view_->set_owned_by_client(); + + views::Widget::InitParams init_params( + CreateParams(views::Widget::InitParams::TYPE_POPUP)); + views::Widget* widget = new views::Widget(); + widget->Init(init_params); + widget->SetContentsView(notification_view_.get()); + widget->SetSize(notification_view_->GetPreferredSize()); +} + +void NotificationViewTest::TearDown() { + widget()->Close(); + notification_view_.reset(); + views::ViewsTestBase::TearDown(); +} + +void NotificationViewTest::ClickOnNotification( + const std::string& notification_id) { + // For this test, this method should not be invoked. + NOTREACHED(); +} + +void NotificationViewTest::RemoveNotification( + const std::string& notification_id, + bool by_user) { + // For this test, this method should not be invoked. + NOTREACHED(); +} + +scoped_ptr<ui::MenuModel> NotificationViewTest::CreateMenuModel( + const NotifierId& notifier_id, + const base::string16& display_source) { + // For this test, this method should not be invoked. + NOTREACHED(); + return scoped_ptr<ui::MenuModel>(); +} + +bool NotificationViewTest::HasClickedListener( + const std::string& notification_id) { + return true; +} + +void NotificationViewTest::ClickOnNotificationButton( + const std::string& notification_id, + int button_index) { + // For this test, this method should not be invoked. + NOTREACHED(); +} + +/* Unit tests *****************************************************************/ + +TEST_F(NotificationViewTest, CreateOrUpdateTest) { + EXPECT_TRUE(NULL != notification_view()->title_view_); + EXPECT_TRUE(NULL != notification_view()->message_view_); + EXPECT_TRUE(NULL != notification_view()->icon_view_); + EXPECT_TRUE(NULL != notification_view()->image_view_); + + notification()->set_image(gfx::Image()); + notification()->set_title(base::ASCIIToUTF16("")); + notification()->set_message(base::ASCIIToUTF16("")); + notification()->set_icon(gfx::Image()); + + notification_view()->CreateOrUpdateViews(*notification()); + EXPECT_TRUE(NULL == notification_view()->title_view_); + EXPECT_TRUE(NULL == notification_view()->message_view_); + EXPECT_TRUE(NULL == notification_view()->image_view_); + // We still expect an icon view for all layouts. + EXPECT_TRUE(NULL != notification_view()->icon_view_); +} + +TEST_F(NotificationViewTest, TestLineLimits) { + notification()->set_image(CreateTestImage(0, 0)); + notification()->set_context_message(base::ASCIIToUTF16("")); + notification_view()->CreateOrUpdateViews(*notification()); + + EXPECT_EQ(5, notification_view()->GetMessageLineLimit(0, 360)); + EXPECT_EQ(5, notification_view()->GetMessageLineLimit(1, 360)); + EXPECT_EQ(3, notification_view()->GetMessageLineLimit(2, 360)); + + notification()->set_image(CreateTestImage(2, 2)); + notification_view()->CreateOrUpdateViews(*notification()); + + EXPECT_EQ(2, notification_view()->GetMessageLineLimit(0, 360)); + EXPECT_EQ(2, notification_view()->GetMessageLineLimit(1, 360)); + EXPECT_EQ(1, notification_view()->GetMessageLineLimit(2, 360)); + + notification()->set_context_message(base::UTF8ToUTF16("foo")); + notification_view()->CreateOrUpdateViews(*notification()); + + EXPECT_TRUE(notification_view()->context_message_view_ != NULL); + + EXPECT_EQ(1, notification_view()->GetMessageLineLimit(0, 360)); + EXPECT_EQ(1, notification_view()->GetMessageLineLimit(1, 360)); + EXPECT_EQ(0, notification_view()->GetMessageLineLimit(2, 360)); +} + +TEST_F(NotificationViewTest, UpdateButtonsStateTest) { + notification()->set_buttons(CreateButtons(2)); + notification_view()->CreateOrUpdateViews(*notification()); + widget()->Show(); + + EXPECT_TRUE(NULL == notification_view()->action_buttons_[0]->background()); + + // Now construct a mouse move event 1 pixel inside the boundary of the action + // button. + gfx::Point cursor_location(1, 1); + views::View::ConvertPointToWidget(notification_view()->action_buttons_[0], + &cursor_location); + ui::MouseEvent move(ui::ET_MOUSE_MOVED, + cursor_location, + cursor_location, + ui::EF_NONE, + ui::EF_NONE); + widget()->OnMouseEvent(&move); + + EXPECT_TRUE(NULL != notification_view()->action_buttons_[0]->background()); + + notification_view()->CreateOrUpdateViews(*notification()); + + EXPECT_TRUE(NULL != notification_view()->action_buttons_[0]->background()); + + // Now construct a mouse move event 1 pixel outside the boundary of the + // widget. + cursor_location = gfx::Point(-1, -1); + move = ui::MouseEvent(ui::ET_MOUSE_MOVED, + cursor_location, + cursor_location, + ui::EF_NONE, + ui::EF_NONE); + widget()->OnMouseEvent(&move); + + EXPECT_TRUE(NULL == notification_view()->action_buttons_[0]->background()); +} + +TEST_F(NotificationViewTest, UpdateButtonCountTest) { + notification()->set_buttons(CreateButtons(2)); + notification_view()->CreateOrUpdateViews(*notification()); + widget()->Show(); + + EXPECT_TRUE(NULL == notification_view()->action_buttons_[0]->background()); + EXPECT_TRUE(NULL == notification_view()->action_buttons_[1]->background()); + + // Now construct a mouse move event 1 pixel inside the boundary of the action + // button. + gfx::Point cursor_location(1, 1); + views::View::ConvertPointToWidget(notification_view()->action_buttons_[0], + &cursor_location); + ui::MouseEvent move(ui::ET_MOUSE_MOVED, + cursor_location, + cursor_location, + ui::EF_NONE, + ui::EF_NONE); + widget()->OnMouseEvent(&move); + + EXPECT_TRUE(NULL != notification_view()->action_buttons_[0]->background()); + EXPECT_TRUE(NULL == notification_view()->action_buttons_[1]->background()); + + notification()->set_buttons(CreateButtons(1)); + notification_view()->CreateOrUpdateViews(*notification()); + + EXPECT_TRUE(NULL != notification_view()->action_buttons_[0]->background()); + EXPECT_EQ(1u, notification_view()->action_buttons_.size()); + + // Now construct a mouse move event 1 pixel outside the boundary of the + // widget. + cursor_location = gfx::Point(-1, -1); + move = ui::MouseEvent(ui::ET_MOUSE_MOVED, + cursor_location, + cursor_location, + ui::EF_NONE, + ui::EF_NONE); + widget()->OnMouseEvent(&move); + + EXPECT_TRUE(NULL == notification_view()->action_buttons_[0]->background()); +} + +TEST_F(NotificationViewTest, ViewOrderingTest) { + // Tests that views are created in the correct vertical order. + notification()->set_buttons(CreateButtons(2)); + + // Layout the initial views. + UpdateNotificationViews(); + + // Double-check that vertical order is correct. + CheckVerticalOrderInNotification(); + + // Tests that views remain in that order even after an update. + UpdateNotificationViews(); + CheckVerticalOrderInNotification(); +} + +} // namespace message_center diff --git a/chromium/ui/message_center/views/notifier_settings_view.cc b/chromium/ui/message_center/views/notifier_settings_view.cc index e345d93e501..a282ccbe04a 100644 --- a/chromium/ui/message_center/views/notifier_settings_view.cc +++ b/chromium/ui/message_center/views/notifier_settings_view.cc @@ -28,6 +28,7 @@ #include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/button/label_button_border.h" #include "ui/views/controls/button/menu_button.h" +#include "ui/views/controls/button/text_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" #include "ui/views/controls/link.h" @@ -41,10 +42,6 @@ #include "ui/views/painter.h" #include "ui/views/widget/widget.h" -#if defined(USE_AURA) -#include "ui/aura/window.h" -#endif - namespace message_center { namespace settings { @@ -141,8 +138,8 @@ class EntryView : public views::View { // views::View: virtual void Layout() OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual void OnFocus() OVERRIDE; virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE; virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE; @@ -171,14 +168,14 @@ void EntryView::Layout() { content->SetBounds(0, y, content_width, content_height); } -gfx::Size EntryView::GetPreferredSize() { +gfx::Size EntryView::GetPreferredSize() const { DCHECK_EQ(1, child_count()); gfx::Size size = child_at(0)->GetPreferredSize(); size.SetToMax(gfx::Size(settings::kWidth, settings::kEntryHeight)); return size; } -void EntryView::GetAccessibleState(ui::AccessibleViewState* state) { +void EntryView::GetAccessibleState(ui::AXViewState* state) { DCHECK_EQ(1, child_count()); child_at(0)->GetAccessibleState(state); } @@ -295,7 +292,7 @@ NotifierSettingsView::NotifierButton::NotifierButton( notifier_(notifier), icon_view_(new views::ImageView()), name_view_(new views::Label(notifier_->name)), - checkbox_(new views::Checkbox(string16())), + checkbox_(new views::Checkbox(base::string16())), learn_more_(NULL) { DCHECK(provider); DCHECK(notifier); @@ -333,7 +330,7 @@ NotifierSettingsView::NotifierButton::NotifierButton( (settings::kLearnMoreTargetHeight - settings::kLearnMoreSize) / 2; // The image itself is quite small, this large invisible border creates a // much bigger click target. - learn_more_->set_border( + learn_more_->SetBorder( views::Border::CreateEmptyBorder(learn_more_border_height, learn_more_border_width, learn_more_border_height, @@ -384,7 +381,8 @@ void NotifierSettingsView::NotifierButton::SendLearnMorePressedForTest() { return; gfx::Point point(110, 120); ui::MouseEvent pressed( - ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON); + ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); ButtonPressed(learn_more_, pressed); } @@ -405,7 +403,7 @@ void NotifierSettingsView::NotifierButton::ButtonPressed( } void NotifierSettingsView::NotifierButton::GetAccessibleState( - ui::AccessibleViewState* state) { + ui::AXViewState* state) { static_cast<views::View*>(checkbox_)->GetAccessibleState(state); } @@ -486,17 +484,15 @@ NotifierSettingsView::NotifierSettingsView(NotifierSettingsProvider* provider) SetFocusable(true); set_background( views::Background::CreateSolidBackground(kMessageCenterBackgroundColor)); - if (get_use_acceleration_when_possible()) - SetPaintToLayer(true); + SetPaintToLayer(true); - gfx::Font title_font = - ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont); title_label_ = new views::Label( l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL), - title_font); + ui::ResourceBundle::GetSharedInstance().GetFontList( + ui::ResourceBundle::MediumFont)); title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); title_label_->SetMultiLine(true); - title_label_->set_border( + title_label_->SetBorder( views::Border::CreateEmptyBorder(kComputedTitleTopMargin, settings::kTitleMargin, kComputedTitleBottomMargin, @@ -545,6 +541,9 @@ void NotifierSettingsView::NotifierGroupChanged() { UpdateContentsView(notifiers); } +void NotifierSettingsView::NotifierEnabledChanged(const NotifierId& notifier_id, + bool enabled) {} + void NotifierSettingsView::UpdateContentsView( const std::vector<Notifier*>& notifiers) { buttons_.clear(); @@ -571,7 +570,7 @@ void NotifierSettingsView::UpdateContentsView( top_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); top_label->SetMultiLine(true); - top_label->set_border(views::Border::CreateEmptyBorder( + top_label->SetBorder(views::Border::CreateEmptyBorder( 0, settings::kTitleMargin + kMenuButtonInnateMargin, 0, @@ -580,7 +579,7 @@ void NotifierSettingsView::UpdateContentsView( if (need_account_switcher) { const NotifierGroup& active_group = provider_->GetActiveNotifierGroup(); - string16 notifier_group_text = active_group.login_info.empty() ? + base::string16 notifier_group_text = active_group.login_info.empty() ? active_group.name : active_group.login_info; notifier_group_selector_ = new views::MenuButton(NULL, notifier_group_text, this, true); @@ -598,7 +597,8 @@ void NotifierSettingsView::UpdateContentsView( selector_border->SetInsets(gfx::Insets( kMenuButtonVerticalPadding, kMenuButtonLeftPadding, kMenuButtonVerticalPadding, kMenuButtonRightPadding)); - notifier_group_selector_->set_border(selector_border.release()); + notifier_group_selector_->SetBorder( + selector_border.PassAs<views::Border>()); notifier_group_selector_->SetFocusPainter(scoped_ptr<views::Painter>()); notifier_group_selector_->set_animate_on_state_change(false); notifier_group_selector_->SetFocusable(true); @@ -616,17 +616,17 @@ void NotifierSettingsView::UpdateContentsView( // border on the last notifier, as the spec leaves a space for it. scoped_ptr<views::Border> entry_border; if (i == notifier_count - 1) { - entry_border.reset(views::Border::CreateEmptyBorder( - 0, 0, settings::kEntrySeparatorHeight, 0)); + entry_border = views::Border::CreateEmptyBorder( + 0, 0, settings::kEntrySeparatorHeight, 0); } else { - entry_border.reset(views::Border::CreateSolidSidedBorder( - 0, - 0, - settings::kEntrySeparatorHeight, - 0, - settings::kEntrySeparatorColor)); + entry_border = + views::Border::CreateSolidSidedBorder(0, + 0, + settings::kEntrySeparatorHeight, + 0, + settings::kEntrySeparatorColor); } - entry->set_border(entry_border.release()); + entry->SetBorder(entry_border.Pass()); entry->SetFocusable(true); contents_view->AddChildView(entry); buttons_.insert(button); @@ -656,7 +656,7 @@ void NotifierSettingsView::Layout() { scroller_->SetBounds(0, title_height, width(), height() - title_height); } -gfx::Size NotifierSettingsView::GetMinimumSize() { +gfx::Size NotifierSettingsView::GetMinimumSize() const { gfx::Size size(settings::kWidth, settings::kMinimumHeight); int total_height = title_label_->GetPreferredSize().height() + scroller_->contents()->GetPreferredSize().height(); @@ -665,7 +665,7 @@ gfx::Size NotifierSettingsView::GetMinimumSize() { return size; } -gfx::Size NotifierSettingsView::GetPreferredSize() { +gfx::Size NotifierSettingsView::GetPreferredSize() const { gfx::Size preferred_size; gfx::Size title_size = title_label_->GetPreferredSize(); gfx::Size content_size = scroller_->contents()->GetPreferredSize(); @@ -717,7 +717,7 @@ void NotifierSettingsView::OnMenuButtonClicked(views::View* source, notifier_group_menu_runner_->RunMenuAt(GetWidget(), notifier_group_selector_, menu_anchor, - views::MenuItemView::BUBBLE_ABOVE, + views::MENU_ANCHOR_BUBBLE_ABOVE, ui::MENU_SOURCE_MOUSE, views::MenuRunner::CONTEXT_MENU)) return; diff --git a/chromium/ui/message_center/views/notifier_settings_view.h b/chromium/ui/message_center/views/notifier_settings_view.h index 665bed0a155..994fa50a6e9 100644 --- a/chromium/ui/message_center/views/notifier_settings_view.h +++ b/chromium/ui/message_center/views/notifier_settings_view.h @@ -43,6 +43,8 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsView virtual void UpdateIconImage(const NotifierId& notifier_id, const gfx::Image& icon) OVERRIDE; virtual void NotifierGroupChanged() OVERRIDE; + virtual void NotifierEnabledChanged(const NotifierId& notifier_id, + bool enabled) OVERRIDE; void set_provider(NotifierSettingsProvider* new_provider) { provider_ = new_provider; @@ -71,7 +73,7 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsView // Overridden from views::ButtonListener: virtual void ButtonPressed(views::Button* button, const ui::Event& event) OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; bool ShouldHaveLearnMoreButton() const; // Helper function to reset the layout when the view has substantially @@ -95,8 +97,8 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsView // Overridden from views::View: virtual void Layout() OVERRIDE; - virtual gfx::Size GetMinimumSize() OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetMinimumSize() const OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE; virtual bool OnMouseWheel(const ui::MouseWheelEvent& event) OVERRIDE; diff --git a/chromium/ui/message_center/views/padded_button.cc b/chromium/ui/message_center/views/padded_button.cc index 182cde698c9..add465b1720 100644 --- a/chromium/ui/message_center/views/padded_button.cc +++ b/chromium/ui/message_center/views/padded_button.cc @@ -50,7 +50,7 @@ void PaddedButton::SetPressedImage(int resource_id) { resource_id)); } -gfx::Size PaddedButton::GetPreferredSize() { +gfx::Size PaddedButton::GetPreferredSize() const { return gfx::Size(message_center::kControlButtonSize, message_center::kControlButtonSize); } @@ -66,8 +66,6 @@ void PaddedButton::OnPaint(gfx::Canvas* canvas) { if (!background_image_.isNull()) canvas->DrawImageInt(background_image_, position.x(), position.y()); canvas->DrawImageInt(image, position.x(), position.y()); - if (!overlay_image_.isNull()) - canvas->DrawImageInt(overlay_image_, position.x(), position.y()); } views::Painter::PaintFocusPainter(this, canvas, focus_painter()); } diff --git a/chromium/ui/message_center/views/padded_button.h b/chromium/ui/message_center/views/padded_button.h index 04ae5c1d36b..c420621e9a1 100644 --- a/chromium/ui/message_center/views/padded_button.h +++ b/chromium/ui/message_center/views/padded_button.h @@ -24,7 +24,7 @@ class PaddedButton : public views::ImageButton { virtual ~PaddedButton(); // Overridden from views::ImageButton: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; virtual void OnFocus() OVERRIDE; @@ -49,4 +49,4 @@ class PaddedButton : public views::ImageButton { } // namespace message_center -#endif // UI_MESSAGE_CENTER_VIEWS_PADDED_BUTTON_H_
\ No newline at end of file +#endif // UI_MESSAGE_CENTER_VIEWS_PADDED_BUTTON_H_ diff --git a/chromium/ui/message_center/views/proportional_image_view.cc b/chromium/ui/message_center/views/proportional_image_view.cc index 1e52fa34cef..8ec9b32e6dc 100644 --- a/chromium/ui/message_center/views/proportional_image_view.cc +++ b/chromium/ui/message_center/views/proportional_image_view.cc @@ -9,55 +9,52 @@ namespace message_center { -ProportionalImageView::ProportionalImageView(const gfx::ImageSkia& image) - : image_(image) { -} +ProportionalImageView::ProportionalImageView(const gfx::ImageSkia& image, + const gfx::Size& max_size) + : image_(image), max_size_(max_size) {} -ProportionalImageView::~ProportionalImageView() { -} +ProportionalImageView::~ProportionalImageView() {} -gfx::Size ProportionalImageView::GetPreferredSize() { - gfx::Insets insets = GetInsets(); - gfx::Rect rect = gfx::Rect(GetImageSizeForWidth(image_.width())); - rect.Inset(-insets); - return rect.size(); -} +gfx::Size ProportionalImageView::GetPreferredSize() const { return max_size_; } -int ProportionalImageView::GetHeightForWidth(int width) { - // The border will count against the width available for the image - // and towards the height taken by the image. - gfx::Insets insets = GetInsets(); - int inset_width = width - insets.width(); - return GetImageSizeForWidth(inset_width).height() + insets.height(); +int ProportionalImageView::GetHeightForWidth(int width) const { + return max_size_.height(); } void ProportionalImageView::OnPaint(gfx::Canvas* canvas) { views::View::OnPaint(canvas); - gfx::Size draw_size(GetImageSizeForWidth(width() - GetInsets().width())); - if (!draw_size.IsEmpty()) { - gfx::Rect draw_bounds = GetContentsBounds(); - draw_bounds.ClampToCenteredSize(draw_size); - - gfx::Size image_size(image_.size()); - if (image_size == draw_size) { - canvas->DrawImageInt(image_, draw_bounds.x(), draw_bounds.y()); - } else { - // Resize case - SkPaint paint; - paint.setFilterBitmap(true); - canvas->DrawImageInt(image_, 0, 0, - image_size.width(), image_size.height(), - draw_bounds.x(), draw_bounds.y(), - draw_size.width(), draw_size.height(), - true, paint); - } + gfx::Size draw_size = GetImageDrawingSize(); + + if (draw_size.IsEmpty()) + return; + + gfx::Rect draw_bounds = GetContentsBounds(); + draw_bounds.ClampToCenteredSize(draw_size); + + gfx::Size image_size(image_.size()); + + if (image_size == draw_size) { + canvas->DrawImageInt(image_, draw_bounds.x(), draw_bounds.y()); + } else { + SkPaint paint; + paint.setFilterLevel(SkPaint::kLow_FilterLevel); + + // This call resizes the image while drawing into the canvas. + canvas->DrawImageInt( + image_, + 0, 0, image_size.width(), image_size.height(), + draw_bounds.x(), draw_bounds.y(), draw_size.width(), draw_size.height(), + true, + paint); } } -gfx::Size ProportionalImageView::GetImageSizeForWidth(int width) { - gfx::Size size = visible() ? image_.size() : gfx::Size(); - return message_center::GetImageSizeForWidth(width, size); +gfx::Size ProportionalImageView::GetImageDrawingSize() { + if (!visible()) + return gfx::Size(); + return message_center::GetImageSizeForContainerSize( + GetContentsBounds().size(), image_.size()); } } // namespace message_center diff --git a/chromium/ui/message_center/views/proportional_image_view.h b/chromium/ui/message_center/views/proportional_image_view.h index 42f6dc2b346..7c1be9243c2 100644 --- a/chromium/ui/message_center/views/proportional_image_view.h +++ b/chromium/ui/message_center/views/proportional_image_view.h @@ -13,18 +13,19 @@ namespace message_center { // ProportionalImageViews center their images to preserve their proportion. class ProportionalImageView : public views::View { public: - ProportionalImageView(const gfx::ImageSkia& image); + ProportionalImageView(const gfx::ImageSkia& image, const gfx::Size& max_size); virtual ~ProportionalImageView(); // Overridden from views::View: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual int GetHeightForWidth(int width) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual int GetHeightForWidth(int width) const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; private: - gfx::Size GetImageSizeForWidth(int width); + gfx::Size GetImageDrawingSize(); gfx::ImageSkia image_; + gfx::Size max_size_; DISALLOW_COPY_AND_ASSIGN(ProportionalImageView); }; diff --git a/chromium/ui/message_center/views/toast_contents_view.cc b/chromium/ui/message_center/views/toast_contents_view.cc index 6c139343ba5..e5de6a25100 100644 --- a/chromium/ui/message_center/views/toast_contents_view.cc +++ b/chromium/ui/message_center/views/toast_contents_view.cc @@ -10,7 +10,7 @@ #include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "base/timer/timer.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/gfx/animation/animation_delegate.h" #include "ui/gfx/animation/slide_animation.h" #include "ui/gfx/display.h" @@ -24,10 +24,12 @@ #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" -#if defined(OS_WIN) && defined(USE_ASH) +#if defined(OS_WIN) #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #endif +using gfx::Screen; + namespace message_center { namespace { @@ -40,7 +42,7 @@ const int kFadeInOutDuration = 200; } // namespace. // static -gfx::Size ToastContentsView::GetToastSizeForView(views::View* view) { +gfx::Size ToastContentsView::GetToastSizeForView(const views::View* view) { int width = kNotificationWidth + view->GetInsets().width(); return gfx::Size(width, view->GetHeightForWidth(width)); } @@ -84,7 +86,16 @@ void ToastContentsView::SetContents(MessageView* view, // The notification type should be ALERT, otherwise the accessibility message // won't be read for this view which returns ROLE_WINDOW. if (already_has_contents && a11y_feedback_for_updates) - NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, false); + NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, false); +} + +void ToastContentsView::UpdateContents(const Notification& notification, + bool a11y_feedback_for_updates) { + DCHECK_GT(child_count(), 0); + MessageView* message_view = static_cast<MessageView*>(child_at(0)); + message_view->UpdateWithNotification(notification); + if (a11y_feedback_for_updates) + NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, false); } void ToastContentsView::RevealWithAnimation(gfx::Point origin) { @@ -152,7 +163,7 @@ void ToastContentsView::StartFadeIn() { fade_animation_->Stop(); GetWidget()->SetOpacity(0); - GetWidget()->Show(); + GetWidget()->ShowInactive(); fade_animation_->Reset(0); fade_animation_->Show(); } @@ -172,16 +183,16 @@ void ToastContentsView::OnBoundsAnimationEndedOrCancelled( const gfx::Animation* animation) { if (is_closing_ && closing_animation_ == animation && GetWidget()) { views::Widget* widget = GetWidget(); -#if defined(USE_AURA) + // TODO(dewittj): This is a workaround to prevent a nasty bug where // closing a transparent widget doesn't actually remove the window, // causing entire areas of the screen to become unresponsive to clicks. // See crbug.com/243469 widget->Hide(); -# if defined(OS_WIN) +#if defined(OS_WIN) widget->SetOpacity(0xFF); -# endif #endif + widget->Close(); } @@ -225,14 +236,6 @@ void ToastContentsView::WindowClosing() { collection_->ForgetToast(this); } -bool ToastContentsView::CanActivate() const { -#if defined(OS_WIN) && defined(USE_AURA) - return true; -#else - return false; -#endif -} - void ToastContentsView::OnDisplayChanged() { views::Widget* widget = GetWidget(); if (!widget) @@ -242,8 +245,10 @@ void ToastContentsView::OnDisplayChanged() { if (!native_view || !collection_.get()) return; - collection_->OnDisplayBoundsChanged(gfx::Screen::GetScreenFor( - native_view)->GetDisplayNearestWindow(native_view)); + collection_->OnDisplayMetricsChanged( + Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view), + gfx::DisplayObserver::DISPLAY_METRIC_BOUNDS | + gfx::DisplayObserver::DISPLAY_METRIC_WORK_AREA); } void ToastContentsView::OnWorkAreaChanged() { @@ -255,8 +260,9 @@ void ToastContentsView::OnWorkAreaChanged() { if (!native_view || !collection_.get()) return; - collection_->OnDisplayBoundsChanged(gfx::Screen::GetScreenFor( - native_view)->GetDisplayNearestWindow(native_view)); + collection_->OnDisplayMetricsChanged( + Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view), + gfx::DisplayObserver::DISPLAY_METRIC_WORK_AREA); } // views::View @@ -277,14 +283,14 @@ void ToastContentsView::Layout() { } } -gfx::Size ToastContentsView::GetPreferredSize() { +gfx::Size ToastContentsView::GetPreferredSize() const { return child_count() ? GetToastSizeForView(child_at(0)) : gfx::Size(); } -void ToastContentsView::GetAccessibleState(ui::AccessibleViewState* state) { +void ToastContentsView::GetAccessibleState(ui::AXViewState* state) { if (child_count() > 0) child_at(0)->GetAccessibleState(state); - state->role = ui::AccessibilityTypes::ROLE_WINDOW; + state->role = ui::AX_ROLE_WINDOW; } void ToastContentsView::ClickOnNotification( @@ -300,15 +306,13 @@ void ToastContentsView::RemoveNotification( collection_->RemoveNotification(notification_id, by_user); } -void ToastContentsView::DisableNotificationsFromThisSource( - const NotifierId& notifier_id) { - if (collection_) - collection_->DisableNotificationsFromThisSource(notifier_id); -} - -void ToastContentsView::ShowNotifierSettingsBubble() { - if (collection_) - collection_->ShowNotifierSettingsBubble(); +scoped_ptr<ui::MenuModel> ToastContentsView::CreateMenuModel( + const NotifierId& notifier_id, + const base::string16& display_source) { + // Should not reach, the context menu should be handled in + // MessagePopupCollection. + NOTREACHED(); + return scoped_ptr<ui::MenuModel>(); } bool ToastContentsView::HasClickedListener( @@ -325,43 +329,17 @@ void ToastContentsView::ClickOnNotificationButton( collection_->ClickOnNotificationButton(notification_id, button_index); } -void ToastContentsView::ExpandNotification( - const std::string& notification_id) { - if (collection_) - collection_->ExpandNotification(notification_id); -} - -void ToastContentsView::GroupBodyClicked( - const std::string& last_notification_id) { - // No group views in popup collection. - NOTREACHED(); -} - -// When clicked on the "N more" button, perform some reasonable action. -// TODO(dimich): find out what the reasonable action could be. -void ToastContentsView::ExpandGroup(const NotifierId& notifier_id) { - // No group views in popup collection. - NOTREACHED(); -} - -void ToastContentsView::RemoveGroup(const NotifierId& notifier_id) { - // No group views in popup collection. - NOTREACHED(); -} - void ToastContentsView::CreateWidget(gfx::NativeView parent) { views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); params.keep_on_top = true; if (parent) params.parent = parent; - else - params.top_level = true; params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; params.delegate = this; views::Widget* widget = new views::Widget(); widget->set_focus_on_creation(false); -#if defined(OS_WIN) && defined(USE_ASH) +#if defined(OS_WIN) // We want to ensure that this toast always goes to the native desktop, // not the Ash desktop (since there is already another toast contents view // there. diff --git a/chromium/ui/message_center/views/toast_contents_view.h b/chromium/ui/message_center/views/toast_contents_view.h index 3a609ca99a6..1b39d81d6f4 100644 --- a/chromium/ui/message_center/views/toast_contents_view.h +++ b/chromium/ui/message_center/views/toast_contents_view.h @@ -39,7 +39,7 @@ class ToastContentsView : public views::WidgetDelegateView, public gfx::AnimationDelegate { public: // Computes the size of a toast assuming it will host the given view. - static gfx::Size GetToastSizeForView(views::View* view); + static gfx::Size GetToastSizeForView(const views::View* view); ToastContentsView(const std::string& notification_id, base::WeakPtr<MessagePopupCollection> collection); @@ -50,6 +50,9 @@ class ToastContentsView : public views::WidgetDelegateView, // accessibility message should be read after this update. void SetContents(MessageView* view, bool a11y_feedback_for_updates); + void UpdateContents(const Notification& notification, + bool a11y_feedback_for_updates); + // Shows the new toast for the first time, animated. // |origin| is the right-bottom corner of the toast. void RevealWithAnimation(gfx::Point origin); @@ -71,25 +74,20 @@ class ToastContentsView : public views::WidgetDelegateView, virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; virtual void Layout() OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; private: // Overridden from MessageCenterController: virtual void ClickOnNotification(const std::string& notification_id) OVERRIDE; virtual void RemoveNotification(const std::string& notification_id, bool by_user) OVERRIDE; - virtual void DisableNotificationsFromThisSource( - const NotifierId& notifier_id) OVERRIDE; - virtual void ShowNotifierSettingsBubble() OVERRIDE; + virtual scoped_ptr<ui::MenuModel> CreateMenuModel( + const NotifierId& notifier_id, + const base::string16& display_source) OVERRIDE; virtual bool HasClickedListener(const std::string& notification_id) OVERRIDE; virtual void ClickOnNotificationButton(const std::string& notification_id, int button_index) OVERRIDE; - virtual void ExpandNotification(const std::string& notification_id) OVERRIDE; - virtual void GroupBodyClicked(const std::string& last_notification_id) - OVERRIDE; - virtual void ExpandGroup(const NotifierId& notifier_id) OVERRIDE; - virtual void RemoveGroup(const NotifierId& notifier_id) OVERRIDE; // Overridden from gfx::AnimationDelegate: virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE; @@ -99,7 +97,6 @@ class ToastContentsView : public views::WidgetDelegateView, // Overridden from views::WidgetDelegate: virtual views::View* GetContentsView() OVERRIDE; virtual void WindowClosing() OVERRIDE; - virtual bool CanActivate() const OVERRIDE; virtual void OnDisplayChanged() OVERRIDE; virtual void OnWorkAreaChanged() OVERRIDE; |