summaryrefslogtreecommitdiff
path: root/chromium/ui/message_center/views
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-09-18 14:34:04 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-10-04 11:15:27 +0000
commite6430e577f105ad8813c92e75c54660c4985026e (patch)
tree88115e5d1fb471fea807111924dcccbeadbf9e4f /chromium/ui/message_center/views
parent53d399fe6415a96ea6986ec0d402a9c07da72453 (diff)
downloadqtwebengine-chromium-e6430e577f105ad8813c92e75c54660c4985026e.tar.gz
BASELINE: Update Chromium to 61.0.3163.99
Change-Id: I8452f34574d88ca2b27af9bd56fc9ff3f16b1367 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/ui/message_center/views')
-rw-r--r--chromium/ui/message_center/views/bounded_label.cc15
-rw-r--r--chromium/ui/message_center/views/bounded_label.h2
-rw-r--r--chromium/ui/message_center/views/constants.h2
-rw-r--r--chromium/ui/message_center/views/message_bubble_base.cc13
-rw-r--r--chromium/ui/message_center/views/message_bubble_base.h7
-rw-r--r--chromium/ui/message_center/views/message_center_bubble.cc13
-rw-r--r--chromium/ui/message_center/views/message_center_bubble.h2
-rw-r--r--chromium/ui/message_center/views/message_center_button_bar.cc6
-rw-r--r--chromium/ui/message_center/views/message_center_view.cc27
-rw-r--r--chromium/ui/message_center/views/message_list_view.cc19
-rw-r--r--chromium/ui/message_center/views/message_popup_collection.cc6
-rw-r--r--chromium/ui/message_center/views/message_view.cc15
-rw-r--r--chromium/ui/message_center/views/message_view.h9
-rw-r--r--chromium/ui/message_center/views/message_view_factory.cc20
-rw-r--r--chromium/ui/message_center/views/notification_button.cc41
-rw-r--r--chromium/ui/message_center/views/notification_button.h3
-rw-r--r--chromium/ui/message_center/views/notification_control_buttons_view.cc184
-rw-r--r--chromium/ui/message_center/views/notification_control_buttons_view.h88
-rw-r--r--chromium/ui/message_center/views/notification_header_view.cc360
-rw-r--r--chromium/ui/message_center/views/notification_header_view.h78
-rw-r--r--chromium/ui/message_center/views/notification_view.cc161
-rw-r--r--chromium/ui/message_center/views/notification_view.h12
-rw-r--r--chromium/ui/message_center/views/notification_view_md.cc932
-rw-r--r--chromium/ui/message_center/views/notification_view_md.h129
-rw-r--r--chromium/ui/message_center/views/notification_view_md_unittest.cc535
-rw-r--r--chromium/ui/message_center/views/notification_view_unittest.cc55
-rw-r--r--chromium/ui/message_center/views/notifier_settings_view.cc15
-rw-r--r--chromium/ui/message_center/views/padded_button.cc4
-rw-r--r--chromium/ui/message_center/views/toast_contents_view.cc19
-rw-r--r--chromium/ui/message_center/views/toast_contents_view.h3
30 files changed, 2513 insertions, 262 deletions
diff --git a/chromium/ui/message_center/views/bounded_label.cc b/chromium/ui/message_center/views/bounded_label.cc
index 84ce848eb56..64d88e4bf4f 100644
--- a/chromium/ui/message_center/views/bounded_label.cc
+++ b/chromium/ui/message_center/views/bounded_label.cc
@@ -263,14 +263,14 @@ void InnerBoundedLabel::SetCachedSize(std::pair<int, int> width_and_lines,
BoundedLabel::BoundedLabel(const base::string16& text,
const gfx::FontList& font_list)
- : line_limit_(-1) {
+ : line_limit_(-1), fixed_width_(0) {
label_.reset(new InnerBoundedLabel(*this));
label_->SetFontList(font_list);
label_->SetText(text);
}
BoundedLabel::BoundedLabel(const base::string16& text)
- : line_limit_(-1) {
+ : line_limit_(-1), fixed_width_(0) {
label_.reset(new InnerBoundedLabel(*this));
label_->SetText(text);
}
@@ -312,12 +312,21 @@ gfx::Size BoundedLabel::GetSizeForWidthAndLines(int width, int lines) {
label_->GetSizeForWidthAndLines(width, lines) : gfx::Size();
}
+void BoundedLabel::SizeToFit(int fixed_width) {
+ fixed_width_ = fixed_width;
+ label_->SizeToFit(fixed_width);
+}
+
int BoundedLabel::GetBaseline() const {
return label_->GetBaseline();
}
gfx::Size BoundedLabel::CalculatePreferredSize() const {
- return visible() ? label_->GetSizeForWidthAndLines(-1, -1) : gfx::Size();
+ if (!visible())
+ return gfx::Size();
+ return fixed_width_ != 0
+ ? label_->GetSizeForWidthAndLines(fixed_width_, line_limit_)
+ : label_->GetSizeForWidthAndLines(-1, -1);
}
int BoundedLabel::GetHeightForWidth(int width) const {
diff --git a/chromium/ui/message_center/views/bounded_label.h b/chromium/ui/message_center/views/bounded_label.h
index e241b5aaf5b..8d204cdac19 100644
--- a/chromium/ui/message_center/views/bounded_label.h
+++ b/chromium/ui/message_center/views/bounded_label.h
@@ -42,6 +42,7 @@ class MESSAGE_CENTER_EXPORT BoundedLabel : public views::View {
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.
+ void SizeToFit(int fixed_width); // Same interface as Label::SizeToFit.
int GetLineHeight() const;
int GetLineLimit() const;
@@ -70,6 +71,7 @@ class MESSAGE_CENTER_EXPORT BoundedLabel : public views::View {
std::unique_ptr<InnerBoundedLabel> label_;
int line_limit_;
+ int fixed_width_;
DISALLOW_COPY_AND_ASSIGN(BoundedLabel);
};
diff --git a/chromium/ui/message_center/views/constants.h b/chromium/ui/message_center/views/constants.h
index 9942ed8a1e1..a99a199ccd0 100644
--- a/chromium/ui/message_center/views/constants.h
+++ b/chromium/ui/message_center/views/constants.h
@@ -22,7 +22,7 @@ const SkColor kContextTextBackgroundColor = SK_ColorWHITE;
const int kTextBottomPadding = 12;
const int kItemTitleToMessagePadding = 3;
-const int kButtonVecticalPadding = 0;
+const int kButtonVerticalPadding = 0;
const int kButtonTitleTopPadding = 0;
const int kNotificationSettingsPadding = 5;
diff --git a/chromium/ui/message_center/views/message_bubble_base.cc b/chromium/ui/message_center/views/message_bubble_base.cc
index 4089234047b..25f0120c3b3 100644
--- a/chromium/ui/message_center/views/message_bubble_base.cc
+++ b/chromium/ui/message_center/views/message_bubble_base.cc
@@ -8,7 +8,6 @@
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "ui/message_center/message_center_style.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
@@ -32,7 +31,7 @@ MessageBubbleBase::MessageBubbleBase(MessageCenter* message_center,
MessageBubbleBase::~MessageBubbleBase() {
if (bubble_view_)
- bubble_view_->reset_delegate();
+ bubble_view_->ResetDelegate();
}
void MessageBubbleBase::BubbleViewDestroyed() {
@@ -64,14 +63,4 @@ void MessageBubbleBase::SetMaxHeight(int height) {
bubble_view_->SetMaxHeight(max_height_);
}
-views::TrayBubbleView::InitParams MessageBubbleBase::GetDefaultInitParams(
- views::TrayBubbleView::AnchorAlignment anchor_alignment) {
- views::TrayBubbleView::InitParams init_params(
- anchor_alignment,
- kNotificationWidth,
- kNotificationWidth);
- init_params.bg_color = kBackgroundDarkColor;
- return init_params;
-}
-
} // namespace message_center
diff --git a/chromium/ui/message_center/views/message_bubble_base.h b/chromium/ui/message_center/views/message_bubble_base.h
index 0c9718a85f6..779b9b3fa03 100644
--- a/chromium/ui/message_center/views/message_bubble_base.h
+++ b/chromium/ui/message_center/views/message_bubble_base.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/macros.h"
+#include "ui/gfx/native_widget_types.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_export.h"
#include "ui/views/bubble/tray_bubble_view.h"
@@ -31,10 +32,6 @@ class MESSAGE_CENTER_EXPORT MessageBubbleBase {
void SetMaxHeight(int height);
int max_height() const { return max_height_; }
- // Gets the init params for the implementation.
- virtual views::TrayBubbleView::InitParams GetInitParams(
- views::TrayBubbleView::AnchorAlignment anchor_alignment) = 0;
-
// Called after the bubble view has been constructed. Creates and initializes
// the bubble contents.
virtual void InitializeContents(views::TrayBubbleView* bubble_view) = 0;
@@ -58,8 +55,6 @@ class MESSAGE_CENTER_EXPORT MessageBubbleBase {
views::TrayBubbleView* bubble_view() const { return bubble_view_; }
protected:
- views::TrayBubbleView::InitParams GetDefaultInitParams(
- views::TrayBubbleView::AnchorAlignment anchor_alignment);
MessageCenter* message_center() { return message_center_; }
MessageCenterTray* tray() { return tray_; }
void set_bubble_view(views::TrayBubbleView* bubble_view) {
diff --git a/chromium/ui/message_center/views/message_center_bubble.cc b/chromium/ui/message_center/views/message_center_bubble.cc
index 3045bfd29e4..ad76cef5ace 100644
--- a/chromium/ui/message_center/views/message_center_bubble.cc
+++ b/chromium/ui/message_center/views/message_center_bubble.cc
@@ -5,10 +5,8 @@
#include "ui/message_center/views/message_center_bubble.h"
#include "base/macros.h"
-#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/views/message_center_view.h"
-#include "ui/strings/grit/ui_strings.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/widget/widget.h"
@@ -79,17 +77,6 @@ void MessageCenterBubble::SetSettingsVisible() {
initially_settings_visible_ = true;
}
-views::TrayBubbleView::InitParams MessageCenterBubble::GetInitParams(
- views::TrayBubbleView::AnchorAlignment anchor_alignment) {
- views::TrayBubbleView::InitParams init_params =
- GetDefaultInitParams(anchor_alignment);
- init_params.min_width += kMarginBetweenItems * 2;
- init_params.max_width += kMarginBetweenItems * 2;
- init_params.max_height = max_height();
- init_params.can_activate = true;
- return init_params;
-}
-
void MessageCenterBubble::InitializeContents(
views::TrayBubbleView* new_bubble_view) {
set_bubble_view(new_bubble_view);
diff --git a/chromium/ui/message_center/views/message_center_bubble.h b/chromium/ui/message_center/views/message_center_bubble.h
index 4364a124d76..138c562ea03 100644
--- a/chromium/ui/message_center/views/message_center_bubble.h
+++ b/chromium/ui/message_center/views/message_center_bubble.h
@@ -33,8 +33,6 @@ class MESSAGE_CENTER_EXPORT MessageCenterBubble
void OnWidgetClosing(views::Widget* widget) override;
// Overridden from MessageBubbleBase:
- views::TrayBubbleView::InitParams GetInitParams(
- views::TrayBubbleView::AnchorAlignment anchor_alignment) override;
void InitializeContents(views::TrayBubbleView* bubble_view) override;
void OnBubbleViewDestroyed() override;
void UpdateBubbleView() override;
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 5b8ef31ce98..f08fc1655ca 100644
--- a/chromium/ui/message_center/views/message_center_button_bar.cc
+++ b/chromium/ui/message_center/views/message_center_button_bar.cc
@@ -9,6 +9,7 @@
#include "ui/base/models/simple_menu_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/text_constants.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_style.h"
@@ -83,8 +84,7 @@ MessageCenterButtonBar::MessageCenterButtonBar(
settings_button_(NULL),
quiet_mode_button_(NULL) {
SetPaintToLayer();
- set_background(
- views::Background::CreateSolidBackground(kMessageCenterBackgroundColor));
+ SetBackground(views::CreateSolidBackground(kMessageCenterBackgroundColor));
ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
@@ -104,7 +104,7 @@ MessageCenterButtonBar::MessageCenterButtonBar(
button_container_ = new views::View;
button_container_->SetLayoutManager(
- new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
+ new views::BoxLayout(views::BoxLayout::kHorizontal));
quiet_mode_button_ = CreateNotificationCenterButton(
this, IDR_NOTIFICATION_DO_NOT_DISTURB,
IDR_NOTIFICATION_DO_NOT_DISTURB_HOVER,
diff --git a/chromium/ui/message_center/views/message_center_view.cc b/chromium/ui/message_center/views/message_center_view.cc
index 36ed38c7f28..ae14fb94d9c 100644
--- a/chromium/ui/message_center/views/message_center_view.cc
+++ b/chromium/ui/message_center/views/message_center_view.cc
@@ -27,8 +27,8 @@
#include "ui/message_center/views/message_view.h"
#include "ui/message_center/views/message_view_context_menu_controller.h"
#include "ui/message_center/views/message_view_factory.h"
+#include "ui/message_center/views/notification_control_buttons_view.h"
#include "ui/message_center/views/notifier_settings_view.h"
-#include "ui/resources/grit/ui_resources.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
@@ -81,8 +81,7 @@ MessageCenterView::MessageCenterView(MessageCenter* message_center,
focus_manager_(nullptr) {
message_center_->AddObserver(this);
set_notify_enter_exit_on_child(true);
- set_background(views::Background::CreateSolidBackground(
- kMessageCenterBackgroundColor));
+ SetBackground(views::CreateSolidBackground(kMessageCenterBackgroundColor));
NotifierSettingsProvider* notifier_settings_provider =
message_center_->GetNotifierSettingsProvider();
@@ -212,12 +211,15 @@ void MessageCenterView::SetIsClosing(bool is_closing) {
void MessageCenterView::OnDidChangeFocus(views::View* before,
views::View* now) {
- if (message_list_view_ && (message_list_view_->Contains(before) ||
- message_list_view_->Contains(now))) {
- // Focus state of a children of message view center is changed.
- for (auto pair : notification_views_) {
- if (pair.second->Contains(before) || pair.second->Contains(now))
- pair.second->UpdateControlButtonsVisibility();
+ // Update the button visibility when the focus state is changed.
+ for (auto pair : notification_views_) {
+ // ControlButtonsView is not in the same view hierarchy on ARC++
+ // notifications, so check it separately.
+ if (pair.second->Contains(before) || pair.second->Contains(now) ||
+ (pair.second->GetControlButtonsView() &&
+ (pair.second->GetControlButtonsView()->Contains(before) ||
+ pair.second->GetControlButtonsView()->Contains(now)))) {
+ pair.second->UpdateControlButtonsVisibility();
}
}
}
@@ -513,7 +515,12 @@ void MessageCenterView::AddNotificationAt(const Notification& notification,
int index) {
MessageView* view =
MessageViewFactory::Create(this, notification, false); // Not top-level.
- view->set_context_menu_controller(context_menu_controller_.get());
+
+ // TODO(yoshiki): Temporary disable context menu on custom notifications.
+ // See crbug.com/750307 for detail.
+ if (notification.type() != NOTIFICATION_TYPE_CUSTOM)
+ view->set_context_menu_controller(context_menu_controller_.get());
+
notification_views_[notification.id()] = view;
view->set_scroller(scroller_);
message_list_view_->AddNotificationAt(view, index);
diff --git a/chromium/ui/message_center/views/message_list_view.cc b/chromium/ui/message_center/views/message_list_view.cc
index 127f3fdc8c4..6a1b78426e6 100644
--- a/chromium/ui/message_center/views/message_list_view.cc
+++ b/chromium/ui/message_center/views/message_list_view.cc
@@ -2,18 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <algorithm>
+#include "ui/message_center/views/message_list_view.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/message_loop/message_loop.h"
#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/gfx/animation/slide_animation.h"
+#include "ui/gfx/geometry/insets.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/message_center_switches.h"
#include "ui/message_center/views/message_center_view.h"
-#include "ui/message_center/views/message_list_view.h"
#include "ui/message_center/views/message_view.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
@@ -35,7 +36,7 @@ MessageListView::MessageListView()
quit_message_loop_after_animation_for_test_(false),
weak_ptr_factory_(this) {
views::BoxLayout* layout =
- new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1);
+ new views::BoxLayout(views::BoxLayout::kVertical, gfx::Insets(), 1);
layout->SetDefaultFlex(1);
SetLayoutManager(layout);
@@ -44,8 +45,7 @@ MessageListView::MessageListView()
// because of the shadow of message view. Use an empty border instead
// to provide this margin.
gfx::Insets shadow_insets = MessageView::GetShadowInsets();
- set_background(
- views::Background::CreateSolidBackground(kMessageCenterBackgroundColor));
+ SetBackground(views::CreateSolidBackground(kMessageCenterBackgroundColor));
SetBorder(views::CreateEmptyBorder(
kMarginBetweenItems - shadow_insets.top(), /* top */
kMarginBetweenItems - shadow_insets.left(), /* left */
@@ -105,8 +105,7 @@ void MessageListView::RemoveNotification(MessageView* view) {
// TODO(yhananda): We should consider consolidating clearing_all_views_,
// deleting_views_ and deleted_when_done_.
- if (std::find(clearing_all_views_.begin(), clearing_all_views_.end(), view) !=
- clearing_all_views_.end() ||
+ if (base::ContainsValue(clearing_all_views_, view) ||
deleting_views_.find(view) != deleting_views_.end() ||
deleted_when_done_.find(view) != deleted_when_done_.end()) {
// Let's skip deleting the view if it's already scheduled for deleting.
@@ -138,8 +137,7 @@ void MessageListView::RemoveNotification(MessageView* view) {
void MessageListView::UpdateNotification(MessageView* view,
const Notification& notification) {
// Skip updating the notification being cleared
- if (std::find(clearing_all_views_.begin(), clearing_all_views_.end(), view) !=
- clearing_all_views_.end())
+ if (base::ContainsValue(clearing_all_views_, view))
return;
int index = GetIndexOf(view);
@@ -345,8 +343,7 @@ bool MessageListView::IsValidChild(const views::View* child) const {
deleting_views_.end() &&
deleted_when_done_.find(const_cast<views::View*>(child)) ==
deleted_when_done_.end() &&
- std::find(clearing_all_views_.begin(), clearing_all_views_.end(),
- child) == clearing_all_views_.end();
+ !base::ContainsValue(clearing_all_views_, child);
}
void MessageListView::DoUpdateIfPossible() {
diff --git a/chromium/ui/message_center/views/message_popup_collection.cc b/chromium/ui/message_center/views/message_popup_collection.cc
index 396721d56fd..be17705b3d7 100644
--- a/chromium/ui/message_center/views/message_popup_collection.cc
+++ b/chromium/ui/message_center/views/message_popup_collection.cc
@@ -210,7 +210,11 @@ void MessagePopupCollection::UpdateWidgets() {
view = MessageViewFactory::Create(NULL, *(*iter), true);
}
- view->set_context_menu_controller(context_menu_controller_.get());
+ // TODO(yoshiki): Temporary disable context menu on custom notifications.
+ // See crbug.com/750307 for detail.
+ if ((*iter)->type() != NOTIFICATION_TYPE_CUSTOM)
+ view->set_context_menu_controller(context_menu_controller_.get());
+
int view_height = ToastContentsView::GetToastSizeForView(view).height();
int height_available =
top_down ? alignment_delegate_->GetWorkArea().bottom() - base
diff --git a/chromium/ui/message_center/views/message_view.cc b/chromium/ui/message_center/views/message_view.cc
index f7a5d4bfb2d..340d9bcce8d 100644
--- a/chromium/ui/message_center/views/message_view.cc
+++ b/chromium/ui/message_center/views/message_view.cc
@@ -16,7 +16,6 @@
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/views/message_center_controller.h"
-#include "ui/resources/grit/ui_resources.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
@@ -75,8 +74,8 @@ MessageView::MessageView(MessageCenterController* controller,
// Create the opaque background that's above the view's shadow.
background_view_ = new views::View();
- background_view_->set_background(
- views::Background::CreateSolidBackground(kNotificationBackgroundColor));
+ background_view_->SetBackground(
+ views::CreateSolidBackground(kNotificationBackgroundColor));
AddChildView(background_view_);
focus_painter_ = views::Painter::CreateSolidFocusPainter(
@@ -116,6 +115,10 @@ void MessageView::SetIsNested() {
void MessageView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
node_data->role = ui::AX_ROLE_BUTTON;
+ node_data->AddStringAttribute(
+ ui::AX_ATTR_ROLE_DESCRIPTION,
+ l10n_util::GetStringUTF8(
+ IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
node_data->SetName(accessible_name_);
}
@@ -171,6 +174,8 @@ void MessageView::OnBlur() {
}
void MessageView::Layout() {
+ views::View::Layout();
+
gfx::Rect content_bounds = GetContentsBounds();
// Background.
@@ -230,6 +235,10 @@ void MessageView::OnCloseButtonPressed() {
controller_->RemoveNotification(notification_id_, true); // By user.
}
+void MessageView::OnSettingsButtonPressed() {
+ controller_->ClickOnSettingsButton(notification_id_);
+}
+
void MessageView::SetDrawBackgroundAsActive(bool active) {
background_view_->background()->
SetNativeControlColor(active ? kHoveredButtonBackgroundColor :
diff --git a/chromium/ui/message_center/views/message_view.h b/chromium/ui/message_center/views/message_view.h
index b55a934b464..d07d76a6760 100644
--- a/chromium/ui/message_center/views/message_view.h
+++ b/chromium/ui/message_center/views/message_view.h
@@ -28,14 +28,9 @@ class ScrollView;
namespace message_center {
class Notification;
+class NotificationControlButtonsView;
class MessageCenterController;
-// Individual notifications constants.
-const int kPaddingBetweenItems = 10;
-const int kPaddingHorizontal = 18;
-const int kWebNotificationButtonWidth = 32;
-const int kWebNotificationIconSize = 40;
-
// An base class for a notification entry. Contains background and other
// elements shared by derived notification views.
class MESSAGE_CENTER_EXPORT MessageView
@@ -55,11 +50,13 @@ class MESSAGE_CENTER_EXPORT MessageView
// Creates a shadow around the notification and changes slide-out behavior.
void SetIsNested();
+ virtual NotificationControlButtonsView* GetControlButtonsView() const = 0;
virtual bool IsCloseButtonFocused() const = 0;
virtual void RequestFocusOnCloseButton() = 0;
virtual void UpdateControlButtonsVisibility() = 0;
void OnCloseButtonPressed();
+ void OnSettingsButtonPressed();
// views::View
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
diff --git a/chromium/ui/message_center/views/message_view_factory.cc b/chromium/ui/message_center/views/message_view_factory.cc
index f1c982f4b99..60567af4533 100644
--- a/chromium/ui/message_center/views/message_view_factory.cc
+++ b/chromium/ui/message_center/views/message_view_factory.cc
@@ -4,8 +4,11 @@
#include "ui/message_center/views/message_view_factory.h"
+#include "base/command_line.h"
+#include "ui/message_center/message_center_switches.h"
#include "ui/message_center/notification_types.h"
#include "ui/message_center/views/notification_view.h"
+#include "ui/message_center/views/notification_view_md.h"
#if defined(OS_WIN)
#include "ui/base/win/shell.h"
@@ -23,10 +26,23 @@ MessageView* MessageViewFactory::Create(MessageCenterController* controller,
case NOTIFICATION_TYPE_IMAGE:
case NOTIFICATION_TYPE_MULTIPLE:
case NOTIFICATION_TYPE_SIMPLE:
- case NOTIFICATION_TYPE_PROGRESS:
+ case NOTIFICATION_TYPE_PROGRESS: {
+ bool new_style_notification_enabled = false; // default value
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableMessageCenterNewStyleNotification)) {
+ new_style_notification_enabled = true;
+ } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableMessageCenterNewStyleNotification)) {
+ new_style_notification_enabled = false;
+ }
+
// All above roads lead to the generic NotificationView.
- notification_view = new NotificationView(controller, notification);
+ if (new_style_notification_enabled)
+ notification_view = new NotificationViewMD(controller, notification);
+ else
+ notification_view = new NotificationView(controller, notification);
break;
+ }
#if defined(TOOLKIT_VIEWS) && !defined(OS_MACOSX)
case NOTIFICATION_TYPE_CUSTOM:
notification_view =
diff --git a/chromium/ui/message_center/views/notification_button.cc b/chromium/ui/message_center/views/notification_button.cc
index 72b2be7cdc6..fe0877a8041 100644
--- a/chromium/ui/message_center/views/notification_button.cc
+++ b/chromium/ui/message_center/views/notification_button.cc
@@ -5,6 +5,7 @@
#include "ui/message_center/views/notification_button.h"
#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/insets.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/views/constants.h"
#include "ui/views/background.h"
@@ -17,23 +18,19 @@
namespace message_center {
NotificationButton::NotificationButton(views::ButtonListener* listener)
- : views::CustomButton(listener),
- icon_(NULL),
- title_(NULL),
- focus_painter_(views::Painter::CreateSolidFocusPainter(
- message_center::kFocusBorderColor,
- gfx::Insets(1, 2, 2, 2))) {
+ : views::CustomButton(listener), icon_(NULL), title_(NULL) {
SetFocusForPlatform();
// Create a background so that it does not change when the MessageView
// background changes to show touch feedback
- set_background(views::Background::CreateSolidBackground(
- kNotificationBackgroundColor));
+ SetBackground(views::CreateSolidBackground(kNotificationBackgroundColor));
set_notify_enter_exit_on_child(true);
- SetLayoutManager(
- new views::BoxLayout(views::BoxLayout::kHorizontal,
- message_center::kButtonHorizontalPadding,
- kButtonVecticalPadding,
- message_center::kButtonIconToTitlePadding));
+ SetLayoutManager(new views::BoxLayout(
+ views::BoxLayout::kHorizontal,
+ gfx::Insets(kButtonVerticalPadding,
+ message_center::kButtonHorizontalPadding),
+ message_center::kButtonIconToTitlePadding));
+ SetFocusPainter(views::Painter::CreateSolidFocusPainter(
+ message_center::kFocusBorderColor, gfx::Insets(1, 2, 2, 2)));
}
NotificationButton::~NotificationButton() {
@@ -83,22 +80,9 @@ int NotificationButton::GetHeightForWidth(int width) const {
return message_center::kButtonHeight;
}
-void NotificationButton::OnPaint(gfx::Canvas* canvas) {
- CustomButton::OnPaint(canvas);
- views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
-}
-
void NotificationButton::OnFocus() {
views::CustomButton::OnFocus();
ScrollRectToVisible(GetLocalBounds());
- // We render differently when focused.
- SchedulePaint();
- }
-
-void NotificationButton::OnBlur() {
- views::CustomButton::OnBlur();
- // We render differently when focused.
- SchedulePaint();
}
void NotificationButton::ViewHierarchyChanged(
@@ -111,11 +95,10 @@ void NotificationButton::ViewHierarchyChanged(
void NotificationButton::StateChanged(ButtonState old_state) {
if (state() == STATE_HOVERED || state() == STATE_PRESSED) {
- set_background(views::Background::CreateSolidBackground(
+ SetBackground(views::CreateSolidBackground(
message_center::kHoveredButtonBackgroundColor));
} else {
- set_background(views::Background::CreateSolidBackground(
- kNotificationBackgroundColor));
+ SetBackground(views::CreateSolidBackground(kNotificationBackgroundColor));
}
}
diff --git a/chromium/ui/message_center/views/notification_button.h b/chromium/ui/message_center/views/notification_button.h
index 69f80bae190..3d3d2186b5b 100644
--- a/chromium/ui/message_center/views/notification_button.h
+++ b/chromium/ui/message_center/views/notification_button.h
@@ -30,9 +30,7 @@ class NotificationButton : public views::CustomButton {
// Overridden from views::View:
gfx::Size CalculatePreferredSize() const override;
int GetHeightForWidth(int width) const override;
- void OnPaint(gfx::Canvas* canvas) override;
void OnFocus() override;
- void OnBlur() override;
void ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) override;
@@ -42,7 +40,6 @@ class NotificationButton : public views::CustomButton {
private:
views::ImageView* icon_;
views::Label* title_;
- std::unique_ptr<views::Painter> focus_painter_;
DISALLOW_COPY_AND_ASSIGN(NotificationButton);
};
diff --git a/chromium/ui/message_center/views/notification_control_buttons_view.cc b/chromium/ui/message_center/views/notification_control_buttons_view.cc
new file mode 100644
index 00000000000..bb10972f525
--- /dev/null
+++ b/chromium/ui/message_center/views/notification_control_buttons_view.cc
@@ -0,0 +1,184 @@
+// Copyright 2017 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_control_buttons_view.h"
+
+#include "base/memory/ptr_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/compositor/layer.h"
+#include "ui/events/event.h"
+#include "ui/gfx/animation/linear_animation.h"
+#include "ui/message_center/message_center_style.h"
+#include "ui/message_center/views/message_view.h"
+#include "ui/message_center/views/padded_button.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/views/background.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace {
+
+// This value should be the same as the duration of reveal animation of
+// the settings view of an Android notification.
+constexpr auto kBackgroundColorChangeDuration =
+ base::TimeDelta::FromMilliseconds(360);
+
+// The initial background color of the view.
+constexpr SkColor kInitialBackgroundColor =
+ message_center::kControlButtonBackgroundColor;
+
+} // anonymous namespace
+
+namespace message_center {
+
+const char NotificationControlButtonsView::kViewClassName[] =
+ "NotificationControlButtonsView";
+
+NotificationControlButtonsView::NotificationControlButtonsView(
+ MessageView* message_view)
+ : message_view_(message_view),
+ bgcolor_origin_(kInitialBackgroundColor),
+ bgcolor_target_(kInitialBackgroundColor) {
+ DCHECK(message_view);
+ SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal));
+
+ // Use layer to change the opacity.
+ SetPaintToLayer();
+ layer()->SetFillsBoundsOpaquely(false);
+
+ SetBackground(views::CreateSolidBackground(kInitialBackgroundColor));
+}
+
+NotificationControlButtonsView::~NotificationControlButtonsView() = default;
+
+void NotificationControlButtonsView::ShowCloseButton(bool show) {
+ if (show && !close_button_) {
+ close_button_ = base::MakeUnique<message_center::PaddedButton>(this);
+ close_button_->set_owned_by_client();
+ close_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ message_center::GetCloseIcon());
+ close_button_->SetAccessibleName(l10n_util::GetStringUTF16(
+ IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME));
+ close_button_->SetTooltipText(l10n_util::GetStringUTF16(
+ IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_TOOLTIP));
+ close_button_->SetBackground(
+ views::CreateSolidBackground(SK_ColorTRANSPARENT));
+
+ // Add the button at the last.
+ DCHECK_LE(child_count(), 1);
+ AddChildView(close_button_.get());
+ } else if (!show && close_button_) {
+ DCHECK(Contains(close_button_.get()));
+ close_button_.reset();
+ }
+}
+
+void NotificationControlButtonsView::ShowSettingsButton(bool show) {
+ if (show && !settings_button_) {
+ settings_button_ = base::MakeUnique<message_center::PaddedButton>(this);
+ settings_button_->set_owned_by_client();
+ settings_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ message_center::GetSettingsIcon());
+ settings_button_->SetAccessibleName(l10n_util::GetStringUTF16(
+ IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
+ settings_button_->SetTooltipText(l10n_util::GetStringUTF16(
+ IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
+ settings_button_->SetBackground(
+ views::CreateSolidBackground(SK_ColorTRANSPARENT));
+
+ // Add the button at the first.
+ DCHECK_LE(child_count(), 1);
+ AddChildViewAt(settings_button_.get(), 0);
+ } else if (!show && settings_button_) {
+ DCHECK(Contains(settings_button_.get()));
+ settings_button_.reset();
+ }
+}
+
+void NotificationControlButtonsView::SetBackgroundColor(
+ const SkColor& target_bgcolor) {
+ DCHECK(background());
+ if (background()->get_color() != target_bgcolor) {
+ bgcolor_origin_ = background()->get_color();
+ bgcolor_target_ = target_bgcolor;
+
+ if (bgcolor_animation_)
+ bgcolor_animation_->End();
+ bgcolor_animation_.reset(new gfx::LinearAnimation(this));
+ bgcolor_animation_->SetDuration(kBackgroundColorChangeDuration);
+ bgcolor_animation_->Start();
+ }
+}
+
+void NotificationControlButtonsView::SetVisible(bool visible) {
+ DCHECK(layer());
+ // Manipulate the opacity instead of changing the visibility to keep the tab
+ // order even when the view is invisible.
+ layer()->SetOpacity(visible ? 1. : 0.);
+ set_can_process_events_within_subtree(visible);
+}
+
+void NotificationControlButtonsView::RequestFocusOnCloseButton() {
+ if (close_button_)
+ close_button_->RequestFocus();
+}
+
+bool NotificationControlButtonsView::IsCloseButtonFocused() const {
+ return close_button_ && close_button_->HasFocus();
+}
+
+bool NotificationControlButtonsView::IsSettingsButtonFocused() const {
+ return settings_button_ && settings_button_->HasFocus();
+}
+
+message_center::PaddedButton* NotificationControlButtonsView::close_button()
+ const {
+ return close_button_.get();
+}
+
+message_center::PaddedButton* NotificationControlButtonsView::settings_button()
+ const {
+ return settings_button_.get();
+}
+
+const char* NotificationControlButtonsView::GetClassName() const {
+ return kViewClassName;
+}
+
+void NotificationControlButtonsView::ButtonPressed(views::Button* sender,
+ const ui::Event& event) {
+ if (close_button_ && sender == close_button_.get()) {
+ message_view_->OnCloseButtonPressed();
+ } else if (settings_button_ && sender == settings_button_.get()) {
+ message_view_->OnSettingsButtonPressed();
+ }
+}
+
+void NotificationControlButtonsView::AnimationProgressed(
+ const gfx::Animation* animation) {
+ DCHECK_EQ(animation, bgcolor_animation_.get());
+
+ const SkColor color = gfx::Tween::ColorValueBetween(
+ animation->GetCurrentValue(), bgcolor_origin_, bgcolor_target_);
+ SetBackground(views::CreateSolidBackground(color));
+ SchedulePaint();
+}
+
+void NotificationControlButtonsView::AnimationEnded(
+ const gfx::Animation* animation) {
+ DCHECK_EQ(animation, bgcolor_animation_.get());
+ bgcolor_animation_.reset();
+ bgcolor_origin_ = bgcolor_target_;
+}
+
+void NotificationControlButtonsView::AnimationCanceled(
+ const gfx::Animation* animation) {
+ // The animation is never cancelled explicitly.
+ NOTREACHED();
+
+ bgcolor_origin_ = bgcolor_target_;
+ SetBackground(views::CreateSolidBackground(bgcolor_target_));
+ SchedulePaint();
+}
+
+} // namespace message_center
diff --git a/chromium/ui/message_center/views/notification_control_buttons_view.h b/chromium/ui/message_center/views/notification_control_buttons_view.h
new file mode 100644
index 00000000000..627f0bc9c66
--- /dev/null
+++ b/chromium/ui/message_center/views/notification_control_buttons_view.h
@@ -0,0 +1,88 @@
+// Copyright 2017 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_NOTIFICATION_CONTROL_BUTTONS_VIEW_H_
+#define UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_CONTROL_BUTTONS_VIEW_H_
+
+#include "base/macros.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/animation/animation_delegate.h"
+#include "ui/message_center/message_center_export.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/view.h"
+
+namespace ui {
+class Event;
+}
+
+namespace gfx {
+class LinearAnimation;
+}
+
+namespace message_center {
+
+class MessageView;
+class PaddedButton;
+
+class MESSAGE_CENTER_EXPORT NotificationControlButtonsView
+ : public views::View,
+ public views::ButtonListener,
+ public gfx::AnimationDelegate {
+ public:
+ // String to be returned by GetClassName() method.
+ static const char kViewClassName[];
+
+ explicit NotificationControlButtonsView(MessageView* message_view);
+ ~NotificationControlButtonsView() override;
+
+ // Change the visibility of the close button. True to show, false to hide.
+ void ShowCloseButton(bool show);
+ // Change the visibility of the settings button. True to show, false to hide.
+ void ShowSettingsButton(bool show);
+
+ // Set the background color of the view.
+ void SetBackgroundColor(const SkColor& target_bgcolor);
+
+ // Request the focus on the close button.
+ void RequestFocusOnCloseButton();
+
+ // Return the focus status of the close button. True if the focus is on the
+ // close button, false otherwise.
+ bool IsCloseButtonFocused() const;
+ // Return the focus status of the settings button. True if the focus is on the
+ // close button, false otherwise.
+ bool IsSettingsButtonFocused() const;
+
+ // Methods for retrieving the control buttons directly.
+ message_center::PaddedButton* close_button() const;
+ message_center::PaddedButton* settings_button() const;
+
+ // views::View
+ const char* GetClassName() const override;
+ void SetVisible(bool visible) override;
+
+ // views::ButtonListener
+ void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+ // gfx::AnimationDelegate
+ void AnimationEnded(const gfx::Animation* animation) override;
+ void AnimationProgressed(const gfx::Animation* animation) override;
+ void AnimationCanceled(const gfx::Animation* animation) override;
+
+ private:
+ MessageView* message_view_;
+
+ std::unique_ptr<message_center::PaddedButton> close_button_;
+ std::unique_ptr<message_center::PaddedButton> settings_button_;
+
+ std::unique_ptr<gfx::LinearAnimation> bgcolor_animation_;
+ SkColor bgcolor_origin_;
+ SkColor bgcolor_target_;
+
+ DISALLOW_COPY_AND_ASSIGN(NotificationControlButtonsView);
+};
+
+} // namespace message_center
+
+#endif // UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_CONTROL_BUTTONS_VIEW_H_
diff --git a/chromium/ui/message_center/views/notification_header_view.cc b/chromium/ui/message_center/views/notification_header_view.cc
new file mode 100644
index 00000000000..a43e6eaaf37
--- /dev/null
+++ b/chromium/ui/message_center/views/notification_header_view.cc
@@ -0,0 +1,360 @@
+// Copyright 2017 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_header_view.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/font_list.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/message_center/message_center_style.h"
+#include "ui/message_center/vector_icons.h"
+#include "ui/message_center/views/padded_button.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
+#include "ui/views/animation/ink_drop_highlight.h"
+#include "ui/views/animation/ink_drop_impl.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/painter.h"
+
+namespace message_center {
+
+namespace {
+
+constexpr int kHeaderHeight = 28;
+constexpr int kAppIconSize = 12;
+constexpr int kExpandIconSize = 12;
+constexpr gfx::Insets kHeaderPadding(0, 12, 0, 2);
+constexpr int kHeaderHorizontalSpacing = 2;
+constexpr int kAppInfoConatainerTopPadding = 12;
+// Bullet character. The divider symbol between different parts of the header.
+constexpr wchar_t kNotificationHeaderDivider[] = L" \u2022 ";
+
+// Base ink drop color of action buttons.
+const SkColor kInkDropBaseColor = SkColorSetRGB(0x0, 0x0, 0x0);
+// Ripple ink drop opacity of action buttons.
+constexpr float kInkDropRippleVisibleOpacity = 0.08f;
+// Highlight (hover) ink drop opacity of action buttons.
+constexpr float kInkDropHighlightVisibleOpacity = 0.08f;
+
+// base::TimeBase has similar constants, but some of them are missing.
+constexpr int64_t kMinuteInMillis = 60LL * 1000LL;
+constexpr int64_t kHourInMillis = 60LL * kMinuteInMillis;
+constexpr int64_t kDayInMillis = 24LL * kHourInMillis;
+// In Android, DateUtils.YEAR_IN_MILLIS is 364 days.
+constexpr int64_t kYearInMillis = 364LL * kDayInMillis;
+
+// ExpandButtton forwards all mouse and key events to NotificationHeaderView,
+// but takes tab focus for accessibility purpose.
+class ExpandButton : public views::ImageView {
+ public:
+ ExpandButton();
+ ~ExpandButton() override;
+
+ void OnPaint(gfx::Canvas* canvas) override;
+ void OnFocus() override;
+ void OnBlur() override;
+
+ private:
+ std::unique_ptr<views::Painter> focus_painter_;
+};
+
+ExpandButton::ExpandButton() {
+ SetImage(gfx::CreateVectorIcon(kNotificationExpandMoreIcon, kExpandIconSize,
+ gfx::kChromeIconGrey));
+ focus_painter_ = views::Painter::CreateSolidFocusPainter(
+ kFocusBorderColor, gfx::Insets(1, 2, 2, 2));
+ SetFocusBehavior(FocusBehavior::ALWAYS);
+}
+
+ExpandButton::~ExpandButton() = default;
+
+void ExpandButton::OnPaint(gfx::Canvas* canvas) {
+ views::ImageView::OnPaint(canvas);
+ views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
+}
+
+void ExpandButton::OnFocus() {
+ views::ImageView::OnFocus();
+ SchedulePaint();
+}
+
+void ExpandButton::OnBlur() {
+ views::ImageView::OnBlur();
+ SchedulePaint();
+}
+
+// Do relative time string formatting that is similar to
+// com.java.android.widget.DateTimeView.updateRelativeTime.
+// Chromium has its own base::TimeFormat::Simple(), but none of the formats
+// supported by the function is similar to Android's one.
+base::string16 FormatToRelativeTime(base::Time past) {
+ base::Time now = base::Time::Now();
+ int64_t duration = (now - past).InMilliseconds();
+ if (duration < kMinuteInMillis) {
+ return l10n_util::GetStringUTF16(
+ IDS_MESSAGE_NOTIFICATION_NOW_STRING_SHORTEST);
+ } else if (duration < kHourInMillis) {
+ int count = static_cast<int>(duration / kMinuteInMillis);
+ return l10n_util::GetPluralStringFUTF16(
+ IDS_MESSAGE_NOTIFICATION_DURATION_MINUTES_SHORTEST, count);
+ } else if (duration < kDayInMillis) {
+ int count = static_cast<int>(duration / kHourInMillis);
+ return l10n_util::GetPluralStringFUTF16(
+ IDS_MESSAGE_NOTIFICATION_DURATION_HOURS_SHORTEST, count);
+ } else if (duration < kYearInMillis) {
+ int count = static_cast<int>(duration / kDayInMillis);
+ return l10n_util::GetPluralStringFUTF16(
+ IDS_MESSAGE_NOTIFICATION_DURATION_DAYS_SHORTEST, count);
+ } else {
+ int count = static_cast<int>(duration / kYearInMillis);
+ return l10n_util::GetPluralStringFUTF16(
+ IDS_MESSAGE_NOTIFICATION_DURATION_YEARS_SHORTEST, count);
+ }
+}
+
+} // namespace
+
+NotificationHeaderView::NotificationHeaderView(views::ButtonListener* listener)
+ : views::CustomButton(listener) {
+ SetInkDropMode(InkDropMode::ON);
+ set_has_ink_drop_action_on_click(true);
+ set_animate_on_state_change(true);
+ set_notify_enter_exit_on_child(true);
+ set_ink_drop_base_color(kInkDropBaseColor);
+ set_ink_drop_visible_opacity(kInkDropRippleVisibleOpacity);
+
+ views::BoxLayout* layout = new views::BoxLayout(
+ views::BoxLayout::kHorizontal, kHeaderPadding, kHeaderHorizontalSpacing);
+ layout->set_cross_axis_alignment(
+ views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
+ SetLayoutManager(layout);
+
+ views::View* app_info_container = new views::View();
+ views::BoxLayout* app_info_layout =
+ new views::BoxLayout(views::BoxLayout::kHorizontal,
+ gfx::Insets(kAppInfoConatainerTopPadding, 0, 0, 0),
+ kHeaderHorizontalSpacing);
+ app_info_layout->set_cross_axis_alignment(
+ views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
+ app_info_container->SetLayoutManager(app_info_layout);
+ AddChildView(app_info_container);
+
+ // App icon view
+ app_icon_view_ = new views::ImageView();
+ app_icon_view_->SetImageSize(gfx::Size(kAppIconSize, kAppIconSize));
+ app_info_container->AddChildView(app_icon_view_);
+
+ // App name view
+ const gfx::FontList& font_list = views::Label().font_list().Derive(
+ -2, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL);
+ app_name_view_ = new views::Label(base::string16());
+ app_name_view_->SetFontList(font_list);
+ app_name_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ app_info_container->AddChildView(app_name_view_);
+
+ // Summary text divider
+ summary_text_divider_ =
+ new views::Label(base::WideToUTF16(kNotificationHeaderDivider));
+ summary_text_divider_->SetFontList(font_list);
+ summary_text_divider_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ summary_text_divider_->SetVisible(false);
+ app_info_container->AddChildView(summary_text_divider_);
+
+ // Summary text view
+ summary_text_view_ = new views::Label(base::string16());
+ summary_text_view_->SetFontList(font_list);
+ summary_text_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ summary_text_view_->SetVisible(false);
+ app_info_container->AddChildView(summary_text_view_);
+
+ // Timestamp divider
+ timestamp_divider_ =
+ new views::Label(base::WideToUTF16(kNotificationHeaderDivider));
+ timestamp_divider_->SetFontList(font_list);
+ timestamp_divider_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ timestamp_divider_->SetVisible(false);
+ app_info_container->AddChildView(timestamp_divider_);
+
+ // Timestamp view
+ timestamp_view_ = new views::Label(base::string16());
+ timestamp_view_->SetFontList(font_list);
+ timestamp_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ timestamp_view_->SetVisible(false);
+ app_info_container->AddChildView(timestamp_view_);
+
+ // Expand button view
+ expand_button_ = new ExpandButton();
+ app_info_container->AddChildView(expand_button_);
+
+ // Spacer between left-aligned views and right-aligned views
+ views::View* spacer = new views::View;
+ spacer->SetPreferredSize(gfx::Size(1, kHeaderHeight));
+ AddChildView(spacer);
+ layout->SetFlexForView(spacer, 1);
+
+ // Settings button view
+ settings_button_ = new PaddedButton(listener);
+ settings_button_->SetImage(views::Button::STATE_NORMAL, GetSettingsIcon());
+ settings_button_->SetAccessibleName(l10n_util::GetStringUTF16(
+ IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
+ settings_button_->SetTooltipText(l10n_util::GetStringUTF16(
+ IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
+ AddChildView(settings_button_);
+
+ // Close button view
+ close_button_ = new PaddedButton(listener);
+ close_button_->SetImage(views::Button::STATE_NORMAL, GetCloseIcon());
+ close_button_->SetAccessibleName(l10n_util::GetStringUTF16(
+ IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME));
+ close_button_->SetTooltipText(l10n_util::GetStringUTF16(
+ IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_TOOLTIP));
+ AddChildView(close_button_);
+}
+
+void NotificationHeaderView::SetAppIcon(const gfx::ImageSkia& img) {
+ app_icon_view_->SetImage(img);
+}
+
+void NotificationHeaderView::SetAppName(const base::string16& name) {
+ app_name_view_->SetText(name);
+}
+
+void NotificationHeaderView::SetProgress(int progress) {
+ summary_text_view_->SetText(l10n_util::GetStringFUTF16Int(
+ IDS_MESSAGE_CENTER_NOTIFICATION_PROGRESS_PERCENTAGE, progress));
+ has_progress_ = true;
+ UpdateSummaryTextVisibility();
+}
+
+void NotificationHeaderView::ClearProgress() {
+ has_progress_ = false;
+ UpdateSummaryTextVisibility();
+}
+
+void NotificationHeaderView::SetOverflowIndicator(int count) {
+ if (count > 0) {
+ summary_text_view_->SetText(l10n_util::GetStringFUTF16Int(
+ IDS_MESSAGE_CENTER_LIST_NOTIFICATION_HEADER_OVERFLOW_INDICATOR, count));
+ has_overflow_indicator_ = true;
+ } else {
+ has_overflow_indicator_ = false;
+ }
+ UpdateSummaryTextVisibility();
+}
+
+void NotificationHeaderView::ClearOverflowIndicator() {
+ has_overflow_indicator_ = false;
+ UpdateSummaryTextVisibility();
+}
+
+void NotificationHeaderView::SetTimestamp(base::Time past) {
+ timestamp_view_->SetText(FormatToRelativeTime(past));
+ has_timestamp_ = true;
+ UpdateSummaryTextVisibility();
+}
+
+void NotificationHeaderView::ClearTimestamp() {
+ has_timestamp_ = false;
+ UpdateSummaryTextVisibility();
+}
+
+void NotificationHeaderView::SetExpandButtonEnabled(bool enabled) {
+ // SetInkDropMode iff. the visibility changed.
+ // Otherwise, the ink drop animation cannot finish.
+ if (expand_button_->visible() != enabled)
+ SetInkDropMode(enabled ? InkDropMode::ON : InkDropMode::OFF);
+
+ expand_button_->SetVisible(enabled);
+}
+
+void NotificationHeaderView::SetExpanded(bool expanded) {
+ expand_button_->SetImage(
+ gfx::CreateVectorIcon(
+ expanded ? kNotificationExpandLessIcon : kNotificationExpandMoreIcon,
+ kExpandIconSize, gfx::kChromeIconGrey));
+}
+
+void NotificationHeaderView::SetSettingsButtonEnabled(bool enabled) {
+ if (settings_button_enabled_ != enabled) {
+ settings_button_enabled_ = enabled;
+ UpdateControlButtonsVisibility();
+ }
+}
+
+void NotificationHeaderView::SetCloseButtonEnabled(bool enabled) {
+ if (close_button_enabled_ != enabled) {
+ close_button_enabled_ = enabled;
+ UpdateControlButtonsVisibility();
+ }
+}
+
+void NotificationHeaderView::SetControlButtonsVisible(bool visible) {
+ if (is_control_buttons_visible_ != visible) {
+ is_control_buttons_visible_ = visible;
+ UpdateControlButtonsVisibility();
+ }
+}
+
+bool NotificationHeaderView::IsExpandButtonEnabled() {
+ return expand_button_->visible();
+}
+
+bool NotificationHeaderView::IsSettingsButtonEnabled() {
+ return settings_button_enabled_;
+}
+
+bool NotificationHeaderView::IsCloseButtonEnabled() {
+ return close_button_enabled_;
+}
+
+std::unique_ptr<views::InkDrop> NotificationHeaderView::CreateInkDrop() {
+ auto ink_drop = base::MakeUnique<views::InkDropImpl>(this, size());
+ ink_drop->SetAutoHighlightMode(
+ views::InkDropImpl::AutoHighlightMode::SHOW_ON_RIPPLE);
+ ink_drop->SetShowHighlightOnHover(false);
+ return ink_drop;
+}
+
+std::unique_ptr<views::InkDropRipple>
+NotificationHeaderView::CreateInkDropRipple() const {
+ return base::MakeUnique<views::FloodFillInkDropRipple>(
+ size(), GetInkDropCenterBasedOnLastEvent(), GetInkDropBaseColor(),
+ ink_drop_visible_opacity());
+}
+
+std::unique_ptr<views::InkDropHighlight>
+NotificationHeaderView::CreateInkDropHighlight() const {
+ auto highlight = base::MakeUnique<views::InkDropHighlight>(
+ size(), kInkDropSmallCornerRadius,
+ gfx::RectF(GetLocalBounds()).CenterPoint(), GetInkDropBaseColor());
+ highlight->set_visible_opacity(kInkDropHighlightVisibleOpacity);
+ return highlight;
+}
+
+void NotificationHeaderView::UpdateControlButtonsVisibility() {
+ settings_button_->SetVisible(settings_button_enabled_ &&
+ is_control_buttons_visible_);
+ close_button_->SetVisible(close_button_enabled_ &&
+ is_control_buttons_visible_);
+ Layout();
+}
+
+void NotificationHeaderView::UpdateSummaryTextVisibility() {
+ const bool visible = has_progress_ || has_overflow_indicator_;
+ summary_text_divider_->SetVisible(visible);
+ summary_text_view_->SetVisible(visible);
+ timestamp_divider_->SetVisible(!has_progress_ && has_timestamp_);
+ timestamp_view_->SetVisible(!has_progress_ && has_timestamp_);
+ Layout();
+}
+
+} // namespace message_center
diff --git a/chromium/ui/message_center/views/notification_header_view.h b/chromium/ui/message_center/views/notification_header_view.h
new file mode 100644
index 00000000000..2471c67a405
--- /dev/null
+++ b/chromium/ui/message_center/views/notification_header_view.h
@@ -0,0 +1,78 @@
+// Copyright 2017 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_NOTIFICATION_HEADER_VIEW_H_
+#define UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_HEADER_VIEW_H_
+
+#include "base/macros.h"
+#include "ui/message_center/views/padded_button.h"
+#include "ui/views/controls/button/custom_button.h"
+
+namespace views {
+class ImageButton;
+class ImageView;
+class Label;
+}
+
+namespace message_center {
+
+class NotificationHeaderView : public views::CustomButton {
+ public:
+ NotificationHeaderView(views::ButtonListener* listener);
+ void SetAppIcon(const gfx::ImageSkia& img);
+ void SetAppName(const base::string16& name);
+ void SetProgress(int progress);
+ void SetOverflowIndicator(int count);
+ void SetTimestamp(base::Time past);
+ void SetExpandButtonEnabled(bool enabled);
+ void SetExpanded(bool expanded);
+ void SetSettingsButtonEnabled(bool enabled);
+ void SetCloseButtonEnabled(bool enabled);
+ void SetControlButtonsVisible(bool visible);
+ void ClearProgress();
+ void ClearOverflowIndicator();
+ void ClearTimestamp();
+ bool IsExpandButtonEnabled();
+ bool IsSettingsButtonEnabled();
+ bool IsCloseButtonEnabled();
+ bool IsCloseButtonFocused();
+
+ // CustomButton override:
+ std::unique_ptr<views::InkDrop> CreateInkDrop() override;
+ std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
+ std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
+ const override;
+
+ views::ImageView* expand_button() { return expand_button_; }
+ views::ImageButton* settings_button() { return settings_button_; }
+ views::ImageButton* close_button() { return close_button_; }
+
+ private:
+ void UpdateControlButtonsVisibility();
+ // Update visibility for both |summary_text_view_| and |timestamp_view_|.
+ void UpdateSummaryTextVisibility();
+
+ views::Label* app_name_view_ = nullptr;
+ views::Label* summary_text_divider_ = nullptr;
+ views::Label* summary_text_view_ = nullptr;
+ views::Label* timestamp_divider_ = nullptr;
+ views::Label* timestamp_view_ = nullptr;
+ views::ImageView* app_icon_view_ = nullptr;
+ views::ImageView* expand_button_ = nullptr;
+ PaddedButton* settings_button_ = nullptr;
+ PaddedButton* close_button_ = nullptr;
+
+ bool settings_button_enabled_ = false;
+ bool close_button_enabled_ = false;
+ bool is_control_buttons_visible_ = false;
+ bool has_progress_ = false;
+ bool has_overflow_indicator_ = false;
+ bool has_timestamp_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(NotificationHeaderView);
+};
+
+} // namespace message_center
+
+#endif // UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_HEADER_VIEW_H_
diff --git a/chromium/ui/message_center/views/notification_view.cc b/chromium/ui/message_center/views/notification_view.cc
index d4c55effee6..0a1ad48595f 100644
--- a/chromium/ui/message_center/views/notification_view.cc
+++ b/chromium/ui/message_center/views/notification_view.cc
@@ -17,6 +17,7 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/layout.h"
#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/text_elider.h"
@@ -28,10 +29,10 @@
#include "ui/message_center/views/constants.h"
#include "ui/message_center/views/message_center_controller.h"
#include "ui/message_center/views/notification_button.h"
+#include "ui/message_center/views/notification_control_buttons_view.h"
#include "ui/message_center/views/padded_button.h"
#include "ui/message_center/views/proportional_image_view.h"
#include "ui/native_theme/native_theme.h"
-#include "ui/resources/grit/ui_resources.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
@@ -103,8 +104,9 @@ class ItemView : public views::View {
};
ItemView::ItemView(const message_center::NotificationItem& item) {
- SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
- 0, 0, message_center::kItemTitleToMessagePadding));
+ SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kHorizontal, gfx::Insets(),
+ message_center::kItemTitleToMessagePadding));
views::Label* title = new views::Label(item.title);
title->set_collapse_when_hidden(true);
@@ -152,10 +154,10 @@ views::View* NotificationView::TargetForRect(views::View* root,
// called. But buttons are exceptions, they'll have their own event handlings.
std::vector<views::View*> buttons(action_buttons_.begin(),
action_buttons_.end());
- if (settings_button_view_)
- buttons.push_back(settings_button_view_);
- if (close_button())
- buttons.push_back(close_button());
+ if (control_buttons_view_->settings_button())
+ buttons.push_back(control_buttons_view_->settings_button());
+ if (control_buttons_view_->close_button())
+ buttons.push_back(control_buttons_view_->close_button());
for (size_t i = 0; i < buttons.size(); ++i) {
gfx::Point point_in_child = point;
@@ -176,7 +178,6 @@ void NotificationView::CreateOrUpdateViews(const Notification& notification) {
CreateOrUpdateSmallIconView(notification);
CreateOrUpdateImageView(notification);
CreateOrUpdateContextMessageView(notification);
- CreateOrUpdateSettingsButtonView(notification);
CreateOrUpdateActionButtonViews(notification);
}
@@ -189,7 +190,7 @@ NotificationView::NotificationView(MessageCenterController* controller,
// close button.
top_view_ = new views::View();
top_view_->SetLayoutManager(
- new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
+ new views::BoxLayout(views::BoxLayout::kVertical));
top_view_->SetBorder(
MakeEmptyBorder(kTextTopPadding - 8, 0, kTextBottomPadding - 5, 0));
AddChildView(top_view_);
@@ -197,9 +198,12 @@ NotificationView::NotificationView(MessageCenterController* controller,
// below the notification icon.
bottom_view_ = new views::View();
bottom_view_->SetLayoutManager(
- new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
+ new views::BoxLayout(views::BoxLayout::kVertical));
AddChildView(bottom_view_);
+ control_buttons_view_ = new NotificationControlButtonsView(this);
+ AddChildView(control_buttons_view_);
+
views::ImageView* small_image_view = new views::ImageView();
small_image_view->SetImageSize(gfx::Size(kSmallImageSize, kSmallImageSize));
small_image_view->set_owned_by_client();
@@ -208,11 +212,11 @@ NotificationView::NotificationView(MessageCenterController* controller,
CreateOrUpdateViews(notification);
// Put together the different content and control views. Layering those allows
- // for proper layout logic and it also allows the close button and small
+ // for proper layout logic and it also allows the control buttons 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_view_.get());
- CreateOrUpdateCloseButtonView(notification);
+ UpdateControlButtonsVisibilityWithNotification(notification);
SetEventTargeter(
std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
@@ -290,31 +294,17 @@ void NotificationView::Layout() {
icon_view_->SetBounds(insets.left(), insets.top(), kNotificationIconSize,
kNotificationIconSize);
- // Settings & Bottom views.
- int bottom_y = insets.top() + std::max(top_height, kNotificationIconSize);
- int bottom_height = bottom_view_->GetHeightForWidth(content_width);
-
- if (settings_button_view_) {
- const gfx::Size settings_size(settings_button_view_->GetPreferredSize());
- int marginFromRight = settings_size.width() + kControlButtonPadding;
- if (close_button_)
- marginFromRight += close_button_->GetPreferredSize().width();
- gfx::Rect settings_rect(insets.left() + content_width - marginFromRight,
- GetContentsBounds().y() + kControlButtonPadding,
- settings_size.width(), settings_size.height());
- settings_button_view_->SetBoundsRect(settings_rect);
- }
-
- // Close button.
- if (close_button_) {
- gfx::Rect content_bounds = GetContentsBounds();
- gfx::Size close_size(close_button_->GetPreferredSize());
- gfx::Rect close_rect(
- content_bounds.right() - close_size.width() - kControlButtonPadding,
- content_bounds.y() + kControlButtonPadding, close_size.width(),
- close_size.height());
- close_button_->SetBoundsRect(close_rect);
- }
+ // Control buttons (close and settings buttons).
+ gfx::Rect control_buttons_bounds(content_bounds);
+ int buttons_width = control_buttons_view_->GetPreferredSize().width();
+ int buttons_height = control_buttons_view_->GetPreferredSize().height();
+ control_buttons_bounds.set_x(control_buttons_bounds.right() - buttons_width -
+ message_center::kControlButtonPadding);
+ control_buttons_bounds.set_y(control_buttons_bounds.y() +
+ message_center::kControlButtonPadding);
+ control_buttons_bounds.set_width(buttons_width);
+ control_buttons_bounds.set_height(buttons_height);
+ control_buttons_view_->SetBoundsRect(control_buttons_bounds);
// Small icon.
gfx::Size small_image_size(small_image_view_->GetPreferredSize());
@@ -325,6 +315,9 @@ void NotificationView::Layout() {
kSmallImagePadding));
small_image_view_->SetBoundsRect(small_image_rect);
+ // Bottom views.
+ int bottom_y = insets.top() + std::max(top_height, kNotificationIconSize);
+ int bottom_height = bottom_view_->GetHeightForWidth(content_width);
bottom_view_->SetBounds(insets.left(), bottom_y,
content_width, bottom_height);
}
@@ -367,7 +360,7 @@ void NotificationView::UpdateWithNotification(
MessageView::UpdateWithNotification(notification);
CreateOrUpdateViews(notification);
- CreateOrUpdateCloseButtonView(notification);
+ UpdateControlButtonsVisibilityWithNotification(notification);
Layout();
SchedulePaint();
}
@@ -379,18 +372,6 @@ void NotificationView::ButtonPressed(views::Button* sender,
// TODO(dewittj): Remove this hack.
std::string id(notification_id());
- if (close_button_ && sender == close_button_.get()) {
- // Warning: This causes the NotificationView itself to be deleted, so don't
- // do anything afterwards.
- OnCloseButtonPressed();
- return;
- }
-
- if (sender == settings_button_view_) {
- controller()->ClickOnSettingsButton(id);
- return;
- }
-
// See if the button pressed was an action button.
for (size_t i = 0; i < action_buttons_.size(); ++i) {
if (sender == action_buttons_[i]) {
@@ -398,20 +379,16 @@ void NotificationView::ButtonPressed(views::Button* sender,
return;
}
}
+
+ NOTREACHED();
}
bool NotificationView::IsCloseButtonFocused() const {
- if (!close_button_)
- return false;
-
- const views::FocusManager* focus_manager = GetFocusManager();
- return focus_manager &&
- focus_manager->GetFocusedView() == close_button_.get();
+ return control_buttons_view_->IsCloseButtonFocused();
}
void NotificationView::RequestFocusOnCloseButton() {
- if (close_button_)
- close_button_->RequestFocus();
+ control_buttons_view_->RequestFocusOnCloseButton();
}
void NotificationView::CreateOrUpdateTitleView(
@@ -522,25 +499,6 @@ void NotificationView::CreateOrUpdateContextMessageView(
}
}
-void NotificationView::CreateOrUpdateSettingsButtonView(
- const Notification& notification) {
- delete settings_button_view_;
- settings_button_view_ = nullptr;
-
- if (!settings_button_view_ && notification.delegate() &&
- notification.delegate()->ShouldDisplaySettingsButton()) {
- PaddedButton* settings = new PaddedButton(this);
- settings->SetImage(views::Button::STATE_NORMAL, GetSettingsIcon());
- settings->SetAccessibleName(l10n_util::GetStringUTF16(
- IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
- settings->SetTooltipText(l10n_util::GetStringUTF16(
- IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
- settings_button_view_ = settings;
- AddChildView(settings_button_view_);
- }
- UpdateControlButtonsVisibility();
-}
-
void NotificationView::CreateOrUpdateProgressBarView(
const Notification& notification) {
if (notification.type() != NOTIFICATION_TYPE_PROGRESS) {
@@ -625,8 +583,8 @@ void NotificationView::CreateOrUpdateImageView(
image_container_ = new views::View();
image_container_->SetLayoutManager(new views::FillLayout());
- image_container_->set_background(views::Background::CreateSolidBackground(
- message_center::kImageBackgroundColor));
+ image_container_->SetBackground(
+ views::CreateSolidBackground(message_center::kImageBackgroundColor));
image_view_ = new message_center::ProportionalImageView(ideal_size);
image_container_->AddChildView(image_view_);
@@ -692,48 +650,35 @@ void NotificationView::CreateOrUpdateActionButtonViews(
}
}
-void NotificationView::CreateOrUpdateCloseButtonView(
+void NotificationView::UpdateControlButtonsVisibilityWithNotification(
const Notification& notification) {
- if (!notification.pinned() && !close_button_) {
- close_button_ = base::MakeUnique<PaddedButton>(this);
- close_button_->SetImage(views::Button::STATE_NORMAL, GetCloseIcon());
- close_button_->SetAccessibleName(l10n_util::GetStringUTF16(
- IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME));
- close_button_->SetTooltipText(l10n_util::GetStringUTF16(
- IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_TOOLTIP));
- close_button_->set_owned_by_client();
- AddChildView(close_button_.get());
- UpdateControlButtonsVisibility();
- } else if (notification.pinned() && close_button_) {
- close_button_.reset();
- }
+ control_buttons_view_->ShowSettingsButton(
+ notification.delegate() &&
+ notification.delegate()->ShouldDisplaySettingsButton());
+ control_buttons_view_->ShowCloseButton(!notification.pinned());
+ UpdateControlButtonsVisibility();
}
void NotificationView::UpdateControlButtonsVisibility() {
#if defined(OS_CHROMEOS)
- // On Chrome OS, the settings button and the close button are shown only when
- // the mouse is hovering on the notification.
+ // On Chrome OS, the settings button and the close button are shown when:
+ // (1) the mouse is hovering on the notification.
+ // (2) the focus is on the control buttons.
const bool target_visibility =
- IsMouseHovered() || HasFocus() ||
- (close_button_ &&
- (close_button_->HasFocus() || close_button_->IsMouseHovered())) ||
- (settings_button_view_ && (settings_button_view_->HasFocus() ||
- settings_button_view_->IsMouseHovered()));
+ IsMouseHovered() || control_buttons_view_->IsCloseButtonFocused() ||
+ control_buttons_view_->IsSettingsButtonFocused();
#else
// On non Chrome OS, the settings button and the close button are always
// shown.
const bool target_visibility = true;
#endif
- if (close_button_) {
- if (target_visibility != close_button_->visible())
- close_button_->SetVisible(target_visibility);
- }
+ control_buttons_view_->SetVisible(target_visibility);
+}
- if (settings_button_view_) {
- if (target_visibility != settings_button_view_->visible())
- settings_button_view_->SetVisible(target_visibility);
- }
+NotificationControlButtonsView* NotificationView::GetControlButtonsView()
+ const {
+ return control_buttons_view_;
}
int NotificationView::GetMessageLineLimit(int title_lines, int width) const {
diff --git a/chromium/ui/message_center/views/notification_view.h b/chromium/ui/message_center/views/notification_view.h
index 7fa691aff25..23e18509b0e 100644
--- a/chromium/ui/message_center/views/notification_view.h
+++ b/chromium/ui/message_center/views/notification_view.h
@@ -24,6 +24,7 @@ namespace message_center {
class BoundedLabel;
class NotificationButton;
+class NotificationControlButtonsView;
class ProportionalImageView;
// View that displays all current types of notification (web, basic, image, and
@@ -56,9 +57,7 @@ class MESSAGE_CENTER_EXPORT NotificationView
bool IsCloseButtonFocused() const override;
void RequestFocusOnCloseButton() override;
void UpdateControlButtonsVisibility() override;
-
- protected:
- views::ImageButton* close_button() { return close_button_.get(); }
+ NotificationControlButtonsView* GetControlButtonsView() const override;
private:
FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, CreateOrUpdateTest);
@@ -82,14 +81,14 @@ class MESSAGE_CENTER_EXPORT NotificationView
void CreateOrUpdateTitleView(const Notification& notification);
void CreateOrUpdateMessageView(const Notification& notification);
void CreateOrUpdateContextMessageView(const Notification& notification);
- void CreateOrUpdateSettingsButtonView(const Notification& notification);
void CreateOrUpdateProgressBarView(const Notification& notification);
void CreateOrUpdateListItemViews(const Notification& notification);
void CreateOrUpdateIconView(const Notification& notification);
void CreateOrUpdateSmallIconView(const Notification& notification);
void CreateOrUpdateImageView(const Notification& notification);
void CreateOrUpdateActionButtonViews(const Notification& notification);
- void CreateOrUpdateCloseButtonView(const Notification& notification);
+ void UpdateControlButtonsVisibilityWithNotification(
+ const Notification& notification);
int GetMessageLineLimit(int title_lines, int width) const;
int GetMessageHeight(int width, int limit) const;
@@ -108,7 +107,6 @@ class MESSAGE_CENTER_EXPORT NotificationView
BoundedLabel* title_view_ = nullptr;
BoundedLabel* message_view_ = nullptr;
BoundedLabel* context_message_view_ = nullptr;
- views::ImageButton* settings_button_view_ = nullptr;
std::vector<views::View*> item_views_;
ProportionalImageView* icon_view_ = nullptr;
views::View* bottom_view_ = nullptr;
@@ -117,8 +115,8 @@ class MESSAGE_CENTER_EXPORT NotificationView
views::ProgressBar* progress_bar_view_ = nullptr;
std::vector<NotificationButton*> action_buttons_;
std::vector<views::View*> separators_;
- std::unique_ptr<views::ImageButton> close_button_ = nullptr;
std::unique_ptr<views::ImageView> small_image_view_;
+ NotificationControlButtonsView* control_buttons_view_;
DISALLOW_COPY_AND_ASSIGN(NotificationView);
};
diff --git a/chromium/ui/message_center/views/notification_view_md.cc b/chromium/ui/message_center/views/notification_view_md.cc
new file mode 100644
index 00000000000..39555c6e1a5
--- /dev/null
+++ b/chromium/ui/message_center/views/notification_view_md.cc
@@ -0,0 +1,932 @@
+// Copyright 2017 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_md.h"
+
+#include <stddef.h>
+
+#include "base/i18n/case_conversion.h"
+#include "base/strings/string_util.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/paint_vector_icon.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/notification.h"
+#include "ui/message_center/notification_types.h"
+#include "ui/message_center/vector_icons.h"
+#include "ui/message_center/views/bounded_label.h"
+#include "ui/message_center/views/constants.h"
+#include "ui/message_center/views/message_center_controller.h"
+#include "ui/message_center/views/notification_header_view.h"
+#include "ui/message_center/views/padded_button.h"
+#include "ui/message_center/views/proportional_image_view.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/views/animation/ink_drop_highlight.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/progress_bar.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/native_cursor.h"
+#include "ui/views/view_targeter.h"
+#include "ui/views/widget/widget.h"
+
+namespace message_center {
+
+namespace {
+
+// Dimensions.
+constexpr gfx::Insets kContentRowPadding(4, 12, 12, 12);
+constexpr gfx::Insets kActionsRowPadding(8, 8, 8, 8);
+constexpr int kActionsRowHorizontalSpacing = 8;
+constexpr gfx::Insets kActionButtonPadding(0, 12, 0, 12);
+constexpr gfx::Size kActionButtonMinSize(88, 32);
+constexpr gfx::Size kIconViewSize(30, 30);
+constexpr gfx::Insets kLargeImageContainerPadding(0, 12, 12, 12);
+constexpr gfx::Size kLargeImageMinSize(328, 0);
+constexpr gfx::Size kLargeImageMaxSize(328, 218);
+
+// Foreground of small icon image.
+constexpr SkColor kSmallImageBackgroundColor = SK_ColorWHITE;
+// Background of small icon image.
+const SkColor kSmallImageColor = SkColorSetRGB(0x43, 0x43, 0x43);
+// Background of inline actions area.
+const SkColor kActionsRowBackgroundColor = SkColorSetRGB(0xee, 0xee, 0xee);
+// Base ink drop color of action buttons.
+const SkColor kActionButtonInkDropBaseColor = SkColorSetRGB(0x0, 0x0, 0x0);
+// Ripple ink drop opacity of action buttons.
+const float kActionButtonInkDropRippleVisibleOpacity = 0.08f;
+// Highlight (hover) ink drop opacity of action buttons.
+const float kActionButtonInkDropHighlightVisibleOpacity = 0.08f;
+// Text color of action button.
+const SkColor kActionButtonTextColor = SkColorSetRGB(0x33, 0x67, 0xD6);
+// Background color of the large image.
+const SkColor kLargeImageBackgroundColor = SkColorSetRGB(0xf5, 0xf5, 0xf5);
+
+// Max number of lines for message_view_.
+constexpr int kMaxLinesForMessageView = 1;
+constexpr int kMaxLinesForExpandedMessageView = 4;
+
+constexpr int kCompactTitleMessageViewSpacing = 12;
+
+constexpr int kProgressBarHeight = 4;
+
+constexpr int kMessageViewWidth =
+ message_center::kNotificationWidth - kIconViewSize.width() -
+ kContentRowPadding.left() - kContentRowPadding.right();
+
+const gfx::ImageSkia CreateSolidColorImage(int width,
+ int height,
+ SkColor color) {
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(width, height);
+ bitmap.eraseColor(color);
+ return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+}
+
+// Take the alpha channel of icon, mask it with the foreground,
+// then add the masked foreground on top of the background
+const gfx::ImageSkia GetMaskedIcon(const gfx::ImageSkia& icon) {
+ int width = icon.width();
+ int height = icon.height();
+
+ // Background color grey
+ const gfx::ImageSkia background = CreateSolidColorImage(
+ width, height, message_center::kSmallImageBackgroundColor);
+ // Foreground color white
+ const gfx::ImageSkia foreground =
+ CreateSolidColorImage(width, height, message_center::kSmallImageColor);
+ const gfx::ImageSkia masked_small_image =
+ gfx::ImageSkiaOperations::CreateMaskedImage(foreground, icon);
+ return gfx::ImageSkiaOperations::CreateSuperimposedImage(background,
+ masked_small_image);
+}
+
+const gfx::ImageSkia GetProductIcon() {
+ return gfx::CreateVectorIcon(kProductIcon, kSmallImageColor);
+}
+
+// ItemView ////////////////////////////////////////////////////////////////////
+
+// ItemViews are responsible for drawing each list notification item's title and
+// message next to each other within a single column.
+class ItemView : public views::View {
+ public:
+ explicit ItemView(const message_center::NotificationItem& item);
+ ~ItemView() override;
+
+ const char* GetClassName() const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ItemView);
+};
+
+ItemView::ItemView(const message_center::NotificationItem& item) {
+ SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kHorizontal, gfx::Insets(), 0));
+
+ views::Label* title = new views::Label(item.title);
+ title->set_collapse_when_hidden(true);
+ title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ title->SetEnabledColor(message_center::kRegularTextColor);
+ title->SetBackgroundColor(message_center::kDimTextBackgroundColor);
+ AddChildView(title);
+
+ views::Label* message = new views::Label(l10n_util::GetStringFUTF16(
+ IDS_MESSAGE_CENTER_LIST_NOTIFICATION_MESSAGE_WITH_DIVIDER, item.message));
+ message->set_collapse_when_hidden(true);
+ message->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ message->SetEnabledColor(message_center::kDimTextColor);
+ message->SetBackgroundColor(message_center::kDimTextBackgroundColor);
+ AddChildView(message);
+}
+
+ItemView::~ItemView() = default;
+
+const char* ItemView::GetClassName() const {
+ return "ItemView";
+}
+
+// CompactTitleMessageView /////////////////////////////////////////////////////
+
+// CompactTitleMessageView shows notification title and message in a single
+// line. This view is used for NOTIFICATION_TYPE_PROGRESS.
+class CompactTitleMessageView : public views::View {
+ public:
+ explicit CompactTitleMessageView();
+ ~CompactTitleMessageView() override;
+
+ const char* GetClassName() const override;
+
+ void OnPaint(gfx::Canvas* canvas) override;
+
+ void set_title(const base::string16& title) { title_ = title; }
+ void set_message(const base::string16& message) { message_ = message; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompactTitleMessageView);
+
+ base::string16 title_;
+ base::string16 message_;
+
+ views::Label* title_view_ = nullptr;
+ views::Label* message_view_ = nullptr;
+};
+
+CompactTitleMessageView::~CompactTitleMessageView() = default;
+
+const char* CompactTitleMessageView::GetClassName() const {
+ return "CompactTitleMessageView";
+}
+
+CompactTitleMessageView::CompactTitleMessageView() {
+ SetLayoutManager(new views::FillLayout());
+
+ const gfx::FontList& font_list = views::Label().font_list().Derive(
+ 1, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL);
+
+ title_view_ = new views::Label();
+ title_view_->SetFontList(font_list);
+ title_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ title_view_->SetEnabledColor(message_center::kRegularTextColor);
+ AddChildView(title_view_);
+
+ message_view_ = new views::Label();
+ message_view_->SetFontList(font_list);
+ message_view_->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
+ message_view_->SetEnabledColor(message_center::kDimTextColor);
+ AddChildView(message_view_);
+}
+
+void CompactTitleMessageView::OnPaint(gfx::Canvas* canvas) {
+ base::string16 title = title_;
+ base::string16 message = message_;
+
+ const gfx::FontList& font_list = views::Label().font_list().Derive(
+ 1, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL);
+
+ // Elides title and message. The behavior is based on Android's one.
+ // * If the title is too long, only the title is shown.
+ // * If the message is too long, the full content of the title is shown,
+ // kCompactTitleMessageViewSpacing is added between them, and the elided
+ // message is shown.
+ // * If they are short enough, the title is left-aligned and the message is
+ // right-aligned.
+ const int original_title_width =
+ gfx::Canvas::GetStringWidthF(title, font_list);
+ if (original_title_width >= width())
+ message.clear();
+ title = gfx::ElideText(title, font_list, width(), gfx::ELIDE_TAIL);
+ const int title_width = gfx::Canvas::GetStringWidthF(title, font_list);
+ const int message_width =
+ std::max(0, width() - title_width - kCompactTitleMessageViewSpacing);
+ message = gfx::ElideText(message, font_list, message_width, gfx::ELIDE_TAIL);
+
+ title_view_->SetText(title);
+ message_view_->SetText(message);
+
+ views::View::OnPaint(canvas);
+}
+
+// NotificationButtonMD ////////////////////////////////////////////////////////
+
+// This class is needed in addition to LabelButton mainly becuase we want to set
+// visible_opacity of InkDropHighlight.
+// This button capitalizes the given label string.
+class NotificationButtonMD : public views::LabelButton {
+ public:
+ NotificationButtonMD(views::ButtonListener* listener,
+ const base::string16& text);
+ ~NotificationButtonMD() override;
+
+ void SetText(const base::string16& text) override;
+ const char* GetClassName() const override;
+
+ std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
+ const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NotificationButtonMD);
+};
+
+NotificationButtonMD::NotificationButtonMD(views::ButtonListener* listener,
+ const base::string16& text)
+ : views::LabelButton(listener,
+ base::i18n::ToUpper(text),
+ views::style::CONTEXT_BUTTON_MD) {
+ SetHorizontalAlignment(gfx::ALIGN_CENTER);
+ SetInkDropMode(views::LabelButton::InkDropMode::ON);
+ set_has_ink_drop_action_on_click(true);
+ set_ink_drop_base_color(kActionButtonInkDropBaseColor);
+ set_ink_drop_visible_opacity(kActionButtonInkDropRippleVisibleOpacity);
+ SetEnabledTextColors(kActionButtonTextColor);
+ SetBorder(views::CreateEmptyBorder(kActionButtonPadding));
+ SetMinSize(kActionButtonMinSize);
+ SetFocusForPlatform();
+}
+
+NotificationButtonMD::~NotificationButtonMD() = default;
+
+void NotificationButtonMD::SetText(const base::string16& text) {
+ views::LabelButton::SetText(base::i18n::ToUpper(text));
+}
+
+const char* NotificationButtonMD::GetClassName() const {
+ return "NotificationButtonMD";
+}
+
+std::unique_ptr<views::InkDropHighlight>
+NotificationButtonMD::CreateInkDropHighlight() const {
+ std::unique_ptr<views::InkDropHighlight> highlight =
+ views::LabelButton::CreateInkDropHighlight();
+ highlight->set_visible_opacity(kActionButtonInkDropHighlightVisibleOpacity);
+ return highlight;
+}
+
+// LargeImageView //////////////////////////////////////////////////////////////
+
+class LargeImageView : public views::View {
+ public:
+ LargeImageView();
+ ~LargeImageView() override;
+
+ void SetImage(const gfx::ImageSkia& image);
+
+ void OnPaint(gfx::Canvas* canvas) override;
+ const char* GetClassName() const override;
+
+ private:
+ gfx::Size GetResizedImageSize();
+
+ gfx::ImageSkia image_;
+
+ DISALLOW_COPY_AND_ASSIGN(LargeImageView);
+};
+
+LargeImageView::LargeImageView() {
+ SetBackground(views::CreateSolidBackground(kLargeImageBackgroundColor));
+}
+
+LargeImageView::~LargeImageView() = default;
+
+void LargeImageView::SetImage(const gfx::ImageSkia& image) {
+ image_ = image;
+ gfx::Size preferred_size = GetResizedImageSize();
+ preferred_size.SetToMax(kLargeImageMinSize);
+ preferred_size.SetToMin(kLargeImageMaxSize);
+ SetPreferredSize(preferred_size);
+ SchedulePaint();
+ Layout();
+}
+
+void LargeImageView::OnPaint(gfx::Canvas* canvas) {
+ views::View::OnPaint(canvas);
+
+ gfx::Size resized_size = GetResizedImageSize();
+ gfx::Size drawn_size = resized_size;
+ drawn_size.SetToMin(kLargeImageMaxSize);
+ gfx::Rect drawn_bounds = GetContentsBounds();
+ drawn_bounds.ClampToCenteredSize(drawn_size);
+
+ gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage(
+ image_, skia::ImageOperations::RESIZE_BEST, resized_size);
+
+ // Cut off the overflown part.
+ gfx::ImageSkia drawn_image = gfx::ImageSkiaOperations::ExtractSubset(
+ resized_image, gfx::Rect(drawn_size));
+
+ canvas->DrawImageInt(drawn_image, drawn_bounds.x(), drawn_bounds.y());
+}
+
+const char* LargeImageView::GetClassName() const {
+ return "LargeImageView";
+}
+
+// Returns expected size of the image right after resizing.
+// The GetResizedImageSize().width() <= kLargeImageMaxSize.width() holds, but
+// GetResizedImageSize().height() may be larger than kLargeImageMaxSize.height()
+// In this case, the overflown part will be just cutted off from the view.
+gfx::Size LargeImageView::GetResizedImageSize() {
+ gfx::Size original_size = image_.size();
+ if (original_size.width() <= kLargeImageMaxSize.width())
+ return image_.size();
+
+ const double proportion =
+ original_size.height() / static_cast<double>(original_size.width());
+ gfx::Size resized_size;
+ resized_size.SetSize(kLargeImageMaxSize.width(),
+ kLargeImageMaxSize.width() * proportion);
+ return resized_size;
+}
+
+// LargeImageContainerView /////////////////////////////////////////////////////
+
+// We have a container view outside LargeImageView, because we want to fill
+// area that is not coverted by the image by background color.
+class LargeImageContainerView : public views::View {
+ public:
+ LargeImageContainerView();
+ ~LargeImageContainerView() override;
+
+ void SetImage(const gfx::ImageSkia& image);
+ const char* GetClassName() const override;
+
+ private:
+ LargeImageView* const image_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(LargeImageContainerView);
+};
+
+LargeImageContainerView::LargeImageContainerView()
+ : image_view_(new LargeImageView()) {
+ SetLayoutManager(new views::FillLayout());
+ SetBorder(views::CreateEmptyBorder(kLargeImageContainerPadding));
+ SetBackground(
+ views::CreateSolidBackground(message_center::kImageBackgroundColor));
+ AddChildView(image_view_);
+}
+
+LargeImageContainerView::~LargeImageContainerView() = default;
+
+void LargeImageContainerView::SetImage(const gfx::ImageSkia& image) {
+ image_view_->SetImage(image);
+}
+
+const char* LargeImageContainerView::GetClassName() const {
+ return "LargeImageContainerView";
+}
+
+} // anonymous namespace
+
+// ////////////////////////////////////////////////////////////
+// NotificationViewMD
+// ////////////////////////////////////////////////////////////
+
+views::View* NotificationViewMD::TargetForRect(views::View* root,
+ const gfx::Rect& rect) {
+ CHECK_EQ(root, this);
+
+ // TODO(tdanderson): Modify this function to support rect-based event
+ // targeting. Using the center point of |rect| preserves this function's
+ // expected behavior for the time being.
+ gfx::Point point = rect.CenterPoint();
+
+ // 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_.begin(),
+ action_buttons_.end());
+ if (header_row_->settings_button())
+ buttons.push_back(header_row_->settings_button());
+ if (header_row_->close_button())
+ buttons.push_back(header_row_->close_button());
+ if (header_row_->expand_button())
+ buttons.push_back(header_row_->expand_button());
+ buttons.push_back(header_row_);
+
+ for (size_t i = 0; i < buttons.size(); ++i) {
+ gfx::Point point_in_child = point;
+ ConvertPointToTarget(this, buttons[i], &point_in_child);
+ if (buttons[i]->HitTestPoint(point_in_child))
+ return buttons[i]->GetEventHandlerForPoint(point_in_child);
+ }
+
+ return root;
+}
+
+void NotificationViewMD::CreateOrUpdateViews(const Notification& notification) {
+ CreateOrUpdateContextTitleView(notification);
+ CreateOrUpdateTitleView(notification);
+ CreateOrUpdateMessageView(notification);
+ CreateOrUpdateCompactTitleMessageView(notification);
+ CreateOrUpdateProgressBarView(notification);
+ CreateOrUpdateListItemViews(notification);
+ CreateOrUpdateIconView(notification);
+ CreateOrUpdateSmallIconView(notification);
+ CreateOrUpdateImageView(notification);
+ CreateOrUpdateCloseButtonView(notification);
+ CreateOrUpdateSettingsButtonView(notification);
+ UpdateViewForExpandedState(expanded_);
+ // Should be called at the last because SynthesizeMouseMoveEvent() requires
+ // everything is in the right location when called.
+ CreateOrUpdateActionButtonViews(notification);
+}
+
+NotificationViewMD::NotificationViewMD(MessageCenterController* controller,
+ const Notification& notification)
+ : MessageView(controller, notification),
+ clickable_(notification.clickable()) {
+ SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kVertical, gfx::Insets(), 0));
+
+ // |header_row_| contains app_icon, app_name, control buttons, etc...
+ header_row_ = new NotificationHeaderView(this);
+ AddChildView(header_row_);
+
+ // |content_row_| contains title, message, image, progressbar, etc...
+ content_row_ = new views::View();
+ views::BoxLayout* content_row_layout = new views::BoxLayout(
+ views::BoxLayout::kHorizontal, kContentRowPadding, 0);
+ content_row_layout->set_cross_axis_alignment(
+ views::BoxLayout::CROSS_AXIS_ALIGNMENT_START);
+ content_row_->SetLayoutManager(content_row_layout);
+ AddChildView(content_row_);
+
+ // |left_content_| contains most contents like title, message, etc...
+ left_content_ = new views::View();
+ left_content_->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kVertical, gfx::Insets(), 0));
+ content_row_->AddChildView(left_content_);
+ content_row_layout->SetFlexForView(left_content_, 1);
+
+ // |right_content_| contains notification icon and small image.
+ right_content_ = new views::View();
+ right_content_->SetLayoutManager(new views::FillLayout());
+ content_row_->AddChildView(right_content_);
+
+ // |action_row_| contains inline action button.
+ actions_row_ = new views::View();
+ actions_row_->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kHorizontal, kActionsRowPadding,
+ kActionsRowHorizontalSpacing));
+ actions_row_->SetBackground(
+ views::CreateSolidBackground(kActionsRowBackgroundColor));
+ actions_row_->SetVisible(false);
+ AddChildView(actions_row_);
+
+ CreateOrUpdateViews(notification);
+
+ SetEventTargeter(
+ std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
+}
+
+NotificationViewMD::~NotificationViewMD() {}
+
+void NotificationViewMD::Layout() {
+ MessageView::Layout();
+
+ // We need to call IsExpandable() at the end of Layout() call, since whether
+ // we should show expand button or not depends on the current view layout.
+ // (e.g. Show expand button when |message_view_| exceeds one line.)
+ header_row_->SetExpandButtonEnabled(IsExpandable());
+}
+
+void NotificationViewMD::OnFocus() {
+ MessageView::OnFocus();
+ ScrollRectToVisible(GetLocalBounds());
+}
+
+void NotificationViewMD::ScrollRectToVisible(const gfx::Rect& rect) {
+ // Notification want to show the whole notification when a part of it (like
+ // a button) gets focused.
+ views::View::ScrollRectToVisible(GetLocalBounds());
+}
+
+gfx::NativeCursor NotificationViewMD::GetCursor(const ui::MouseEvent& event) {
+ if (!clickable_ || !controller()->HasClickedListener(notification_id()))
+ return views::View::GetCursor(event);
+
+ return views::GetNativeHandCursor();
+}
+
+void NotificationViewMD::OnMouseMoved(const ui::MouseEvent& event) {
+ MessageView::OnMouseMoved(event);
+ UpdateControlButtonsVisibility();
+}
+
+void NotificationViewMD::OnMouseEntered(const ui::MouseEvent& event) {
+ MessageView::OnMouseEntered(event);
+ UpdateControlButtonsVisibility();
+}
+
+void NotificationViewMD::OnMouseExited(const ui::MouseEvent& event) {
+ MessageView::OnMouseExited(event);
+ UpdateControlButtonsVisibility();
+}
+
+void NotificationViewMD::UpdateWithNotification(
+ const Notification& notification) {
+ MessageView::UpdateWithNotification(notification);
+
+ CreateOrUpdateViews(notification);
+ Layout();
+ SchedulePaint();
+}
+
+void NotificationViewMD::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());
+
+ if (header_row_->IsCloseButtonEnabled() &&
+ sender == header_row_->close_button()) {
+ // Warning: This causes the NotificationViewMD itself to be deleted, so
+ // don't do anything afterwards.
+ OnCloseButtonPressed();
+ return;
+ }
+
+ if (header_row_->IsSettingsButtonEnabled() &&
+ sender == header_row_->settings_button()) {
+ controller()->ClickOnSettingsButton(id);
+ return;
+ }
+
+ // Tapping anywhere on |header_row_| can expand the notification, though only
+ // |expand_button| can be focused by TAB.
+ if (IsExpandable() && sender == header_row_) {
+ ToggleExpanded();
+ Layout();
+ SchedulePaint();
+ return;
+ }
+
+ // 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(id, i);
+ return;
+ }
+ }
+}
+
+bool NotificationViewMD::IsCloseButtonFocused() const {
+ if (!header_row_->IsCloseButtonEnabled())
+ return false;
+
+ const views::FocusManager* focus_manager = GetFocusManager();
+ return focus_manager &&
+ focus_manager->GetFocusedView() == header_row_->close_button();
+}
+
+void NotificationViewMD::RequestFocusOnCloseButton() {
+ if (header_row_->IsCloseButtonEnabled())
+ header_row_->close_button()->RequestFocus();
+}
+
+void NotificationViewMD::CreateOrUpdateContextTitleView(
+ const Notification& notification) {
+ header_row_->SetAppName(notification.display_source());
+ header_row_->SetTimestamp(notification.timestamp());
+}
+
+void NotificationViewMD::CreateOrUpdateTitleView(
+ const Notification& notification) {
+ if (notification.title().empty() ||
+ notification.type() == NOTIFICATION_TYPE_PROGRESS) {
+ DCHECK(!title_view_ || left_content_->Contains(title_view_));
+ delete title_view_;
+ title_view_ = nullptr;
+ return;
+ }
+ const gfx::FontList& font_list = views::Label().font_list().Derive(
+ 1, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL);
+
+ int title_character_limit =
+ kNotificationWidth * kMaxTitleLines / kMinPixelsPerTitleCharacter;
+
+ base::string16 title = gfx::TruncateString(
+ notification.title(), title_character_limit, gfx::WORD_BREAK);
+ if (!title_view_) {
+ title_view_ = new views::Label(title);
+ title_view_->SetFontList(font_list);
+ title_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ title_view_->SetEnabledColor(message_center::kRegularTextColor);
+ left_content_->AddChildView(title_view_);
+ } else {
+ title_view_->SetText(title);
+ }
+}
+
+void NotificationViewMD::CreateOrUpdateMessageView(
+ const Notification& notification) {
+ if (notification.type() == NOTIFICATION_TYPE_PROGRESS ||
+ notification.message().empty()) {
+ // Deletion will also remove |message_view_| from its parent.
+ delete message_view_;
+ message_view_ = nullptr;
+ return;
+ }
+
+ base::string16 text = gfx::TruncateString(
+ notification.message(), kMessageCharacterLimit, gfx::WORD_BREAK);
+
+ const gfx::FontList& font_list = views::Label().font_list().Derive(
+ 1, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL);
+
+ if (!message_view_) {
+ message_view_ = new BoundedLabel(text, font_list);
+ message_view_->SetLineLimit(kMaxLinesForMessageView);
+ message_view_->SetColors(message_center::kDimTextColor,
+ kContextTextBackgroundColor);
+
+ // TODO(tetsui): Workaround https://crbug.com/682266 by explicitly setting
+ // the width.
+ // Ideally, we should fix the original bug, but it seems there's no obvious
+ // solution for the bug according to https://crbug.com/678337#c7, we should
+ // ensure that the change won't break any of the users of BoxLayout class.
+ DCHECK(right_content_);
+ message_view_->SizeToFit(kMessageViewWidth);
+
+ left_content_->AddChildView(message_view_);
+ } else {
+ message_view_->SetText(text);
+ }
+
+ message_view_->SetVisible(notification.items().empty());
+}
+
+void NotificationViewMD::CreateOrUpdateCompactTitleMessageView(
+ const Notification& notification) {
+ if (notification.type() != NOTIFICATION_TYPE_PROGRESS) {
+ DCHECK(!compact_title_message_view_ ||
+ left_content_->Contains(compact_title_message_view_));
+ delete compact_title_message_view_;
+ compact_title_message_view_ = nullptr;
+ return;
+ }
+ if (!compact_title_message_view_) {
+ compact_title_message_view_ = new CompactTitleMessageView();
+ left_content_->AddChildView(compact_title_message_view_);
+ }
+
+ compact_title_message_view_->set_title(notification.title());
+ compact_title_message_view_->set_message(notification.message());
+ left_content_->InvalidateLayout();
+}
+
+void NotificationViewMD::CreateOrUpdateProgressBarView(
+ const Notification& notification) {
+ if (notification.type() != NOTIFICATION_TYPE_PROGRESS) {
+ DCHECK(!progress_bar_view_ || left_content_->Contains(progress_bar_view_));
+ delete progress_bar_view_;
+ progress_bar_view_ = nullptr;
+ header_row_->ClearProgress();
+ return;
+ }
+
+ DCHECK(left_content_);
+
+ if (!progress_bar_view_) {
+ progress_bar_view_ = new views::ProgressBar(kProgressBarHeight,
+ /* allow_round_corner */ false);
+ progress_bar_view_->SetBorder(views::CreateEmptyBorder(
+ message_center::kProgressBarTopPadding, 0, 0, 0));
+ left_content_->AddChildView(progress_bar_view_);
+ }
+
+ progress_bar_view_->SetValue(notification.progress() / 100.0);
+ progress_bar_view_->SetVisible(notification.items().empty());
+
+ header_row_->SetProgress(notification.progress());
+}
+
+void NotificationViewMD::CreateOrUpdateListItemViews(
+ const Notification& notification) {
+ for (auto* item_view : item_views_)
+ delete item_view;
+ item_views_.clear();
+
+ const std::vector<NotificationItem>& items = notification.items();
+
+ for (size_t i = 0; i < items.size() && i < kMaxLinesForExpandedMessageView;
+ ++i) {
+ ItemView* item_view = new ItemView(items[i]);
+ item_views_.push_back(item_view);
+ left_content_->AddChildView(item_view);
+ }
+
+ list_items_count_ = items.size();
+
+ // Needed when CreateOrUpdateViews is called for update.
+ if (!item_views_.empty())
+ left_content_->InvalidateLayout();
+}
+
+void NotificationViewMD::CreateOrUpdateIconView(
+ const Notification& notification) {
+ if (notification.type() == NOTIFICATION_TYPE_PROGRESS ||
+ notification.type() == NOTIFICATION_TYPE_MULTIPLE ||
+ notification.type() == NOTIFICATION_TYPE_IMAGE) {
+ DCHECK(!icon_view_ || right_content_->Contains(icon_view_));
+ delete icon_view_;
+ icon_view_ = nullptr;
+ return;
+ }
+
+ if (!icon_view_) {
+ icon_view_ = new ProportionalImageView(kIconViewSize);
+ right_content_->AddChildView(icon_view_);
+ }
+
+ gfx::ImageSkia icon = notification.icon().AsImageSkia();
+ icon_view_->SetImage(icon, icon.size());
+}
+
+void NotificationViewMD::CreateOrUpdateSmallIconView(
+ const Notification& notification) {
+ gfx::ImageSkia icon =
+ notification.small_image().IsEmpty()
+ ? GetProductIcon()
+ : GetMaskedIcon(notification.small_image().AsImageSkia());
+ header_row_->SetAppIcon(icon);
+}
+
+void NotificationViewMD::CreateOrUpdateImageView(
+ const Notification& notification) {
+ if (notification.image().IsEmpty()) {
+ if (image_container_view_) {
+ DCHECK(Contains(image_container_view_));
+ delete image_container_view_;
+ image_container_view_ = nullptr;
+ }
+ return;
+ }
+
+ if (!image_container_view_) {
+ image_container_view_ = new LargeImageContainerView();
+ // Insert the created image container just after the |content_row_|.
+ AddChildViewAt(image_container_view_, GetIndexOf(content_row_) + 1);
+ }
+
+ image_container_view_->SetImage(notification.image().AsImageSkia());
+}
+
+void NotificationViewMD::CreateOrUpdateActionButtonViews(
+ const Notification& notification) {
+ std::vector<ButtonInfo> buttons = notification.buttons();
+ bool new_buttons = action_buttons_.size() != buttons.size();
+
+ if (new_buttons || buttons.size() == 0) {
+ for (auto* item : action_buttons_)
+ delete item;
+ action_buttons_.clear();
+ }
+
+ DCHECK_EQ(this, actions_row_->parent());
+
+ for (size_t i = 0; i < buttons.size(); ++i) {
+ ButtonInfo button_info = buttons[i];
+ if (new_buttons) {
+ NotificationButtonMD* button =
+ new NotificationButtonMD(this, button_info.title);
+ action_buttons_.push_back(button);
+ actions_row_->AddChildView(button);
+ } else {
+ action_buttons_[i]->SetText(button_info.title);
+ action_buttons_[i]->SchedulePaint();
+ action_buttons_[i]->Layout();
+ }
+ }
+
+ // Inherit mouse hover state when action button views reset.
+ // If the view is not expanded, there should be no hover state.
+ if (new_buttons && expanded_) {
+ views::Widget* widget = GetWidget();
+ if (widget) {
+ // This Layout() is needed because button should be in the right location
+ // in the view hierarchy when SynthesizeMouseMoveEvent() is called.
+ Layout();
+ widget->SetSize(widget->GetContentsView()->GetPreferredSize());
+ GetWidget()->SynthesizeMouseMoveEvent();
+ }
+ }
+}
+
+void NotificationViewMD::CreateOrUpdateCloseButtonView(
+ const Notification& notification) {
+ if (!notification.pinned()) {
+ header_row_->SetCloseButtonEnabled(true);
+ } else {
+ header_row_->SetCloseButtonEnabled(false);
+ }
+}
+
+void NotificationViewMD::CreateOrUpdateSettingsButtonView(
+ const Notification& notification) {
+ if (notification.delegate() &&
+ notification.delegate()->ShouldDisplaySettingsButton())
+ header_row_->SetSettingsButtonEnabled(true);
+ else
+ header_row_->SetSettingsButtonEnabled(false);
+}
+
+bool NotificationViewMD::IsExpandable() {
+ // Expandable if the message exceeds one line.
+ if (message_view_ && message_view_->visible() &&
+ message_view_->GetLinesForWidthAndLimit(message_view_->width(), -1) > 1) {
+ return true;
+ }
+ // Expandable if there is at least one inline action.
+ if (actions_row_->has_children())
+ return true;
+
+ // Expandable if the notification has image.
+ if (image_container_view_)
+ return true;
+
+ // Expandable if there are multiple list items.
+ if (item_views_.size() > 1)
+ return true;
+
+ // TODO(fukino): Expandable if both progress bar and message exist.
+
+ return false;
+}
+
+void NotificationViewMD::ToggleExpanded() {
+ expanded_ = !expanded_;
+ UpdateViewForExpandedState(expanded_);
+ content_row_->InvalidateLayout();
+ if (controller())
+ controller()->UpdateNotificationSize(notification_id());
+}
+
+void NotificationViewMD::UpdateViewForExpandedState(bool expanded) {
+ header_row_->SetExpanded(expanded);
+ if (message_view_) {
+ message_view_->SetLineLimit(expanded ? kMaxLinesForExpandedMessageView
+ : kMaxLinesForMessageView);
+ }
+ if (image_container_view_)
+ image_container_view_->SetVisible(expanded);
+ actions_row_->SetVisible(expanded && actions_row_->has_children());
+ for (size_t i = kMaxLinesForMessageView; i < item_views_.size(); ++i) {
+ item_views_[i]->SetVisible(expanded);
+ }
+ header_row_->SetOverflowIndicator(
+ list_items_count_ -
+ (expanded ? item_views_.size() : kMaxLinesForMessageView));
+}
+
+void NotificationViewMD::UpdateControlButtonsVisibility() {
+ const bool target_visibility = IsMouseHovered() || HasFocus() ||
+ (header_row_->IsExpandButtonEnabled() &&
+ header_row_->expand_button()->HasFocus()) ||
+ (header_row_->IsCloseButtonEnabled() &&
+ header_row_->close_button()->HasFocus()) ||
+ (header_row_->IsSettingsButtonEnabled() &&
+ header_row_->settings_button()->HasFocus());
+
+ header_row_->SetControlButtonsVisible(target_visibility);
+}
+
+NotificationControlButtonsView* NotificationViewMD::GetControlButtonsView()
+ const {
+ // TODO(yoshiki): have this view use NotificationControlButtonsView.
+ return nullptr;
+}
+
+} // namespace message_center
diff --git a/chromium/ui/message_center/views/notification_view_md.h b/chromium/ui/message_center/views/notification_view_md.h
new file mode 100644
index 00000000000..8d595d92f1f
--- /dev/null
+++ b/chromium/ui/message_center/views/notification_view_md.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_VIEW_MD_H_
+#define UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_VIEW_MD_H_
+
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "ui/message_center/message_center_export.h"
+#include "ui/message_center/views/message_view.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/view_targeter_delegate.h"
+
+namespace views {
+class Label;
+class LabelButton;
+class ProgressBar;
+}
+
+namespace message_center {
+
+class BoundedLabel;
+class NotificationHeaderView;
+class ProportionalImageView;
+
+namespace {
+class CompactTitleMessageView;
+class ItemView;
+class LargeImageContainerView;
+}
+
+// View that displays all current types of notification (web, basic, image, and
+// list) except the custom notification. Future notification types may be
+// handled by other classes, in which case instances of those classes would be
+// returned by the Create() factory method below.
+class MESSAGE_CENTER_EXPORT NotificationViewMD
+ : public MessageView,
+ public views::ButtonListener,
+ public views::ViewTargeterDelegate {
+ public:
+ NotificationViewMD(MessageCenterController* controller,
+ const Notification& notification);
+ ~NotificationViewMD() override;
+
+ // Overridden from views::View:
+ void Layout() override;
+ void OnFocus() override;
+ void ScrollRectToVisible(const gfx::Rect& rect) override;
+ gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override;
+ void OnMouseMoved(const ui::MouseEvent& event) override;
+ void OnMouseEntered(const ui::MouseEvent& event) override;
+ void OnMouseExited(const ui::MouseEvent& event) override;
+
+ // Overridden from MessageView:
+ void UpdateWithNotification(const Notification& notification) override;
+ void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+ bool IsCloseButtonFocused() const override;
+ void RequestFocusOnCloseButton() override;
+ void UpdateControlButtonsVisibility() override;
+ NotificationControlButtonsView* GetControlButtonsView() const override;
+
+ // views::ViewTargeterDelegate:
+ views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, CreateOrUpdateTest);
+ FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, TestIconSizing);
+ FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UpdateButtonsStateTest);
+ FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UpdateButtonCountTest);
+ FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, ExpandLongMessage);
+
+ friend class NotificationViewMDTest;
+
+ void CreateOrUpdateViews(const Notification& notification);
+
+ void CreateOrUpdateContextTitleView(const Notification& notification);
+ void CreateOrUpdateTitleView(const Notification& notification);
+ void CreateOrUpdateMessageView(const Notification& notification);
+ void CreateOrUpdateCompactTitleMessageView(const Notification& notification);
+ void CreateOrUpdateProgressBarView(const Notification& notification);
+ void CreateOrUpdateListItemViews(const Notification& notification);
+ void CreateOrUpdateIconView(const Notification& notification);
+ void CreateOrUpdateSmallIconView(const Notification& notification);
+ void CreateOrUpdateImageView(const Notification& notification);
+ void CreateOrUpdateActionButtonViews(const Notification& notification);
+ void CreateOrUpdateCloseButtonView(const Notification& notification);
+ void CreateOrUpdateSettingsButtonView(const Notification& notification);
+
+ bool IsExpandable();
+ void ToggleExpanded();
+ void UpdateViewForExpandedState(bool expanded);
+
+ // Whether this notification is expanded or not.
+ bool expanded_ = false;
+
+ // Number of total list items in the given Notification class.
+ int list_items_count_ = 0;
+
+ // Describes whether the view should display a hand pointer or not.
+ bool clickable_;
+
+ // Container views directly attached to this view.
+ NotificationHeaderView* header_row_ = nullptr;
+ views::View* content_row_ = nullptr;
+ views::View* actions_row_ = nullptr;
+
+ // Containers for left and right side on |content_row_|
+ views::View* left_content_ = nullptr;
+ views::View* right_content_ = nullptr;
+
+ // Views which are dinamicallly created inside view hierarchy.
+ views::Label* title_view_ = nullptr;
+ BoundedLabel* message_view_ = nullptr;
+ ProportionalImageView* icon_view_ = nullptr;
+ LargeImageContainerView* image_container_view_ = nullptr;
+ std::vector<views::LabelButton*> action_buttons_;
+ std::vector<ItemView*> item_views_;
+ views::ProgressBar* progress_bar_view_ = nullptr;
+ CompactTitleMessageView* compact_title_message_view_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(NotificationViewMD);
+};
+
+} // namespace message_center
+
+#endif // UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_VIEW_MD_H_
diff --git a/chromium/ui/message_center/views/notification_view_md_unittest.cc b/chromium/ui/message_center/views/notification_view_md_unittest.cc
new file mode 100644
index 00000000000..f39b2d565fb
--- /dev/null
+++ b/chromium/ui/message_center/views/notification_view_md_unittest.cc
@@ -0,0 +1,535 @@
+// Copyright 2017 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_md.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/events/event_processor.h"
+#include "ui/events/event_utils.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/gfx/canvas.h"
+#include "ui/message_center/message_center_style.h"
+#include "ui/message_center/views/bounded_label.h"
+#include "ui/message_center/views/message_center_controller.h"
+#include "ui/message_center/views/notification_header_view.h"
+#include "ui/message_center/views/proportional_image_view.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/test/widget_test.h"
+
+namespace message_center {
+
+/* Test fixture ***************************************************************/
+
+// Used to fill bitmaps returned by CreateBitmap().
+static const SkColor kBitmapColor = SK_ColorGREEN;
+
+class NotificationViewMDTest : public views::ViewsTestBase,
+ public MessageCenterController {
+ public:
+ NotificationViewMDTest();
+ ~NotificationViewMDTest() override;
+
+ // Overridden from ViewsTestBase:
+ void SetUp() override;
+ void TearDown() override;
+
+ // Overridden from MessageCenterController:
+ void ClickOnNotification(const std::string& notification_id) override;
+ void RemoveNotification(const std::string& notification_id,
+ bool by_user) override;
+ std::unique_ptr<ui::MenuModel> CreateMenuModel(
+ const NotifierId& notifier_id,
+ const base::string16& display_source) override;
+ bool HasClickedListener(const std::string& notification_id) override;
+ void ClickOnNotificationButton(const std::string& notification_id,
+ int button_index) override;
+ void ClickOnSettingsButton(const std::string& notification_id) override;
+ void UpdateNotificationSize(const std::string& notification_id) override;
+
+ NotificationViewMD* notification_view() const {
+ return notification_view_.get();
+ }
+ Notification* notification() const { return notification_.get(); }
+ views::Widget* widget() const {
+ DCHECK_EQ(widget_, notification_view()->GetWidget());
+ return widget_;
+ }
+
+ protected:
+ const gfx::Image CreateTestImage(int width, int height);
+ const SkBitmap CreateBitmap(int width, int height);
+ std::vector<ButtonInfo> CreateButtons(int number);
+
+ // Paints |view| and returns the size that the original image (which must have
+ // been created by CreateBitmap()) was scaled to.
+ gfx::Size GetImagePaintSize(ProportionalImageView* view);
+
+ void UpdateNotificationViews();
+ float GetNotificationSlideAmount() const;
+ bool IsRemoved(const std::string& notification_id) const;
+ void DispatchGesture(const ui::GestureEventDetails& details);
+ void BeginScroll();
+ void EndScroll();
+ void ScrollBy(int dx);
+ views::ImageButton* GetCloseButton();
+
+ private:
+ std::set<std::string> removed_ids_;
+
+ std::unique_ptr<RichNotificationData> data_;
+ std::unique_ptr<Notification> notification_;
+ std::unique_ptr<NotificationViewMD> notification_view_;
+ views::Widget* widget_;
+
+ DISALLOW_COPY_AND_ASSIGN(NotificationViewMDTest);
+};
+
+NotificationViewMDTest::NotificationViewMDTest() = default;
+NotificationViewMDTest::~NotificationViewMDTest() = default;
+
+void NotificationViewMDTest::SetUp() {
+ views::ViewsTestBase::SetUp();
+ // Create a dummy notification.
+ 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"), GURL(),
+ NotifierId(NotifierId::APPLICATION, "extension_id"), *data_, nullptr));
+ notification_->set_small_image(CreateTestImage(16, 16));
+ notification_->set_image(CreateTestImage(320, 240));
+
+ // Then create a new NotificationView with that single notification.
+ // In the actual code path, this is instantiated by
+ // MessageViewFactory::Create.
+ // TODO(tetsui): Confirm that NotificationViewMD options are same as one
+ // created by the method.
+ notification_view_.reset(new NotificationViewMD(this, *notification_));
+ notification_view_->SetIsNested();
+ notification_view_->set_owned_by_client();
+
+ views::Widget::InitParams init_params(
+ CreateParams(views::Widget::InitParams::TYPE_POPUP));
+ widget_ = new views::Widget();
+ widget_->Init(init_params);
+ widget_->SetContentsView(notification_view_.get());
+ widget_->SetSize(notification_view_->GetPreferredSize());
+ widget_->Show();
+}
+
+void NotificationViewMDTest::TearDown() {
+ widget()->Close();
+ notification_view_.reset();
+ views::ViewsTestBase::TearDown();
+}
+
+void NotificationViewMDTest::ClickOnNotification(
+ const std::string& notification_id) {
+ // For this test, this method should not be invoked.
+ NOTREACHED();
+}
+
+void NotificationViewMDTest::RemoveNotification(
+ const std::string& notification_id,
+ bool by_user) {
+ removed_ids_.insert(notification_id);
+}
+
+std::unique_ptr<ui::MenuModel> NotificationViewMDTest::CreateMenuModel(
+ const NotifierId& notifier_id,
+ const base::string16& display_source) {
+ // For this test, this method should not be invoked.
+ NOTREACHED();
+ return nullptr;
+}
+
+bool NotificationViewMDTest::HasClickedListener(
+ const std::string& notification_id) {
+ return true;
+}
+
+void NotificationViewMDTest::ClickOnNotificationButton(
+ const std::string& notification_id,
+ int button_index) {
+ // For this test, this method should not be invoked.
+ NOTREACHED();
+}
+
+void NotificationViewMDTest::ClickOnSettingsButton(
+ const std::string& notification_id) {
+ // For this test, this method should not be invoked.
+ NOTREACHED();
+}
+
+void NotificationViewMDTest::UpdateNotificationSize(
+ const std::string& notification_id) {
+ widget()->SetSize(notification_view()->GetPreferredSize());
+}
+
+const gfx::Image NotificationViewMDTest::CreateTestImage(int width,
+ int height) {
+ return gfx::Image::CreateFrom1xBitmap(CreateBitmap(width, height));
+}
+
+const SkBitmap NotificationViewMDTest::CreateBitmap(int width, int height) {
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(width, height);
+ bitmap.eraseColor(kBitmapColor);
+ return bitmap;
+}
+
+std::vector<ButtonInfo> NotificationViewMDTest::CreateButtons(int number) {
+ ButtonInfo info(base::ASCIIToUTF16("Test button."));
+ info.icon = CreateTestImage(4, 4);
+ return std::vector<ButtonInfo>(number, info);
+}
+
+gfx::Size NotificationViewMDTest::GetImagePaintSize(
+ ProportionalImageView* view) {
+ CHECK(view);
+ if (view->bounds().IsEmpty())
+ return gfx::Size();
+
+ gfx::Size canvas_size = view->bounds().size();
+ gfx::Canvas canvas(canvas_size, 1.0 /* image_scale */, true /* is_opaque */);
+ static_assert(kBitmapColor != SK_ColorBLACK,
+ "The bitmap color must match the background color");
+ canvas.DrawColor(SK_ColorBLACK);
+ view->OnPaint(&canvas);
+
+ SkBitmap bitmap = canvas.GetBitmap();
+ // Incrementally inset each edge at its midpoint to find the bounds of the
+ // rect containing the image's color. This assumes that the image is
+ // centered in the canvas.
+ const int kHalfWidth = canvas_size.width() / 2;
+ const int kHalfHeight = canvas_size.height() / 2;
+ gfx::Rect rect(canvas_size);
+ while (rect.width() > 0 &&
+ bitmap.getColor(rect.x(), kHalfHeight) != kBitmapColor)
+ rect.Inset(1, 0, 0, 0);
+ while (rect.height() > 0 &&
+ bitmap.getColor(kHalfWidth, rect.y()) != kBitmapColor)
+ rect.Inset(0, 1, 0, 0);
+ while (rect.width() > 0 &&
+ bitmap.getColor(rect.right() - 1, kHalfHeight) != kBitmapColor)
+ rect.Inset(0, 0, 1, 0);
+ while (rect.height() > 0 &&
+ bitmap.getColor(kHalfWidth, rect.bottom() - 1) != kBitmapColor)
+ rect.Inset(0, 0, 0, 1);
+
+ return rect.size();
+}
+
+void NotificationViewMDTest::UpdateNotificationViews() {
+ notification_view()->UpdateWithNotification(*notification());
+}
+
+float NotificationViewMDTest::GetNotificationSlideAmount() const {
+ return notification_view_->GetSlideOutLayer()
+ ->transform()
+ .To2dTranslation()
+ .x();
+}
+
+bool NotificationViewMDTest::IsRemoved(
+ const std::string& notification_id) const {
+ return (removed_ids_.find(notification_id) != removed_ids_.end());
+}
+
+void NotificationViewMDTest::DispatchGesture(
+ const ui::GestureEventDetails& details) {
+ ui::test::EventGenerator generator(
+ notification_view()->GetWidget()->GetNativeWindow());
+ ui::GestureEvent event(0, 0, 0, ui::EventTimeForNow(), details);
+ generator.Dispatch(&event);
+}
+
+void NotificationViewMDTest::BeginScroll() {
+ DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
+}
+
+void NotificationViewMDTest::EndScroll() {
+ DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
+}
+
+void NotificationViewMDTest::ScrollBy(int dx) {
+ DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, dx, 0));
+}
+
+views::ImageButton* NotificationViewMDTest::GetCloseButton() {
+ return notification_view()->header_row_->close_button();
+}
+
+/* Unit tests *****************************************************************/
+
+// TODO(tetsui): Following tests are not yet ported from NotificationViewTest.
+// * CreateOrUpdateTestSettingsButton
+// * TestLineLimits
+// * TestImageSizing
+// * SettingsButtonTest
+// * ViewOrderingTest
+// * FormatContextMessageTest
+
+TEST_F(NotificationViewMDTest, CreateOrUpdateTest) {
+ EXPECT_NE(nullptr, notification_view()->title_view_);
+ EXPECT_NE(nullptr, notification_view()->message_view_);
+ EXPECT_NE(nullptr, notification_view()->icon_view_);
+ EXPECT_NE(nullptr, notification_view()->image_container_view_);
+
+ notification()->set_image(gfx::Image());
+ notification()->set_title(base::string16());
+ notification()->set_message(base::string16());
+ notification()->set_icon(gfx::Image());
+
+ notification_view()->CreateOrUpdateViews(*notification());
+
+ EXPECT_EQ(nullptr, notification_view()->title_view_);
+ EXPECT_EQ(nullptr, notification_view()->message_view_);
+ EXPECT_EQ(nullptr, notification_view()->image_container_view_);
+ // We still expect an icon view for all layouts.
+ EXPECT_NE(nullptr, notification_view()->icon_view_);
+}
+
+TEST_F(NotificationViewMDTest, TestIconSizing) {
+ // TODO(tetsui): Remove duplicated integer literal in CreateOrUpdateIconView.
+ const int kNotificationIconSize = 30;
+
+ notification()->set_type(NOTIFICATION_TYPE_SIMPLE);
+ ProportionalImageView* view = notification_view()->icon_view_;
+
+ // Icons smaller than the maximum size should remain unscaled.
+ notification()->set_icon(
+ CreateTestImage(kNotificationIconSize / 2, kNotificationIconSize / 4));
+ UpdateNotificationViews();
+ EXPECT_EQ(gfx::Size(kNotificationIconSize / 2, kNotificationIconSize / 4)
+ .ToString(),
+ GetImagePaintSize(view).ToString());
+
+ // Icons of exactly the intended icon size should remain unscaled.
+ notification()->set_icon(
+ CreateTestImage(kNotificationIconSize, kNotificationIconSize));
+ UpdateNotificationViews();
+ EXPECT_EQ(gfx::Size(kNotificationIconSize, kNotificationIconSize).ToString(),
+ GetImagePaintSize(view).ToString());
+
+ // Icons over the maximum size should be scaled down, maintaining proportions.
+ notification()->set_icon(
+ CreateTestImage(2 * kNotificationIconSize, 2 * kNotificationIconSize));
+ UpdateNotificationViews();
+ EXPECT_EQ(gfx::Size(kNotificationIconSize, kNotificationIconSize).ToString(),
+ GetImagePaintSize(view).ToString());
+
+ notification()->set_icon(
+ CreateTestImage(4 * kNotificationIconSize, 2 * kNotificationIconSize));
+ UpdateNotificationViews();
+ EXPECT_EQ(
+ gfx::Size(kNotificationIconSize, kNotificationIconSize / 2).ToString(),
+ GetImagePaintSize(view).ToString());
+}
+
+TEST_F(NotificationViewMDTest, UpdateButtonsStateTest) {
+ notification()->set_buttons(CreateButtons(2));
+ notification_view()->CreateOrUpdateViews(*notification());
+ widget()->Show();
+
+ // Action buttons are hidden by collapsed state.
+ if (!notification_view()->expanded_)
+ notification_view()->ToggleExpanded();
+ EXPECT_TRUE(notification_view()->actions_row_->visible());
+
+ EXPECT_EQ(views::CustomButton::STATE_NORMAL,
+ notification_view()->action_buttons_[0]->state());
+
+ // 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::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
+ widget()->OnMouseEvent(&move);
+
+ EXPECT_EQ(views::CustomButton::STATE_HOVERED,
+ notification_view()->action_buttons_[0]->state());
+
+ notification_view()->CreateOrUpdateViews(*notification());
+
+ EXPECT_EQ(views::CustomButton::STATE_HOVERED,
+ notification_view()->action_buttons_[0]->state());
+
+ // 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::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
+ widget()->OnMouseEvent(&move);
+
+ EXPECT_EQ(views::CustomButton::STATE_NORMAL,
+ notification_view()->action_buttons_[0]->state());
+}
+
+TEST_F(NotificationViewMDTest, UpdateButtonCountTest) {
+ notification()->set_buttons(CreateButtons(2));
+ notification_view()->UpdateWithNotification(*notification());
+ widget()->Show();
+
+ // Action buttons are hidden by collapsed state.
+ if (!notification_view()->expanded_)
+ notification_view()->ToggleExpanded();
+ EXPECT_TRUE(notification_view()->actions_row_->visible());
+
+ EXPECT_EQ(views::CustomButton::STATE_NORMAL,
+ notification_view()->action_buttons_[0]->state());
+ EXPECT_EQ(views::CustomButton::STATE_NORMAL,
+ notification_view()->action_buttons_[1]->state());
+
+ // Now construct a mouse move event 1 pixel inside the boundary of the action
+ // button.
+ gfx::Point cursor_location(1, 1);
+ views::View::ConvertPointToScreen(notification_view()->action_buttons_[0],
+ &cursor_location);
+ ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
+ ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
+ ui::EventDispatchDetails details =
+ views::test::WidgetTest::GetEventSink(widget())->OnEventFromSource(&move);
+ EXPECT_FALSE(details.dispatcher_destroyed);
+
+ EXPECT_EQ(views::CustomButton::STATE_HOVERED,
+ notification_view()->action_buttons_[0]->state());
+ EXPECT_EQ(views::CustomButton::STATE_NORMAL,
+ notification_view()->action_buttons_[1]->state());
+
+ notification()->set_buttons(CreateButtons(1));
+ notification_view()->UpdateWithNotification(*notification());
+
+ EXPECT_EQ(views::CustomButton::STATE_HOVERED,
+ notification_view()->action_buttons_[0]->state());
+ 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::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
+ widget()->OnMouseEvent(&move);
+
+ EXPECT_EQ(views::CustomButton::STATE_NORMAL,
+ notification_view()->action_buttons_[0]->state());
+}
+
+TEST_F(NotificationViewMDTest, SlideOut) {
+ ui::ScopedAnimationDurationScaleMode zero_duration_scope(
+ ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
+
+ UpdateNotificationViews();
+ std::string notification_id = notification()->id();
+
+ BeginScroll();
+ ScrollBy(-10);
+ EXPECT_FALSE(IsRemoved(notification_id));
+ EXPECT_EQ(-10.f, GetNotificationSlideAmount());
+ EndScroll();
+ EXPECT_FALSE(IsRemoved(notification_id));
+ EXPECT_EQ(0.f, GetNotificationSlideAmount());
+
+ BeginScroll();
+ ScrollBy(-200);
+ EXPECT_FALSE(IsRemoved(notification_id));
+ EXPECT_EQ(-200.f, GetNotificationSlideAmount());
+ EndScroll();
+ EXPECT_TRUE(IsRemoved(notification_id));
+}
+
+TEST_F(NotificationViewMDTest, SlideOutNested) {
+ ui::ScopedAnimationDurationScaleMode zero_duration_scope(
+ ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
+
+ UpdateNotificationViews();
+ notification_view()->SetIsNested();
+ std::string notification_id = notification()->id();
+
+ BeginScroll();
+ ScrollBy(-10);
+ EXPECT_FALSE(IsRemoved(notification_id));
+ EXPECT_EQ(-10.f, GetNotificationSlideAmount());
+ EndScroll();
+ EXPECT_FALSE(IsRemoved(notification_id));
+ EXPECT_EQ(0.f, GetNotificationSlideAmount());
+
+ BeginScroll();
+ ScrollBy(-200);
+ EXPECT_FALSE(IsRemoved(notification_id));
+ EXPECT_EQ(-200.f, GetNotificationSlideAmount());
+ EndScroll();
+ EXPECT_TRUE(IsRemoved(notification_id));
+}
+
+// Pinning notification is ChromeOS only feature.
+#if defined(OS_CHROMEOS)
+
+TEST_F(NotificationViewMDTest, SlideOutPinned) {
+ ui::ScopedAnimationDurationScaleMode zero_duration_scope(
+ ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
+
+ notification()->set_pinned(true);
+ UpdateNotificationViews();
+ std::string notification_id = notification()->id();
+
+ BeginScroll();
+ ScrollBy(-200);
+ EXPECT_FALSE(IsRemoved(notification_id));
+ EXPECT_LT(-200.f, GetNotificationSlideAmount());
+ EndScroll();
+ EXPECT_FALSE(IsRemoved(notification_id));
+}
+
+TEST_F(NotificationViewMDTest, Pinned) {
+ notification()->set_pinned(true);
+
+ UpdateNotificationViews();
+ EXPECT_FALSE(GetCloseButton()->visible());
+}
+
+#endif // defined(OS_CHROMEOS)
+
+TEST_F(NotificationViewMDTest, ExpandLongMessage) {
+ notification()->set_type(NotificationType::NOTIFICATION_TYPE_SIMPLE);
+ // Test in a case where left_content_ does not have views other than
+ // message_view_.
+ // Without doing this, inappropriate fix such as
+ // message_view_->GetPreferredSize() returning gfx::Size() can pass.
+ notification()->set_title(base::string16());
+ notification()->set_message(base::ASCIIToUTF16(
+ "consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore "
+ "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud "
+ "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."));
+
+ UpdateNotificationViews();
+ EXPECT_FALSE(notification_view()->expanded_);
+ const int collapsed_height = notification_view()->message_view_->height();
+ const int collapsed_preferred_height =
+ notification_view()->GetPreferredSize().height();
+ EXPECT_LT(0, collapsed_height);
+ EXPECT_LT(0, collapsed_preferred_height);
+
+ notification_view()->ToggleExpanded();
+ EXPECT_TRUE(notification_view()->expanded_);
+ EXPECT_LT(collapsed_height, notification_view()->message_view_->height());
+ EXPECT_LT(collapsed_preferred_height,
+ notification_view()->GetPreferredSize().height());
+
+ notification_view()->ToggleExpanded();
+ EXPECT_FALSE(notification_view()->expanded_);
+ EXPECT_EQ(collapsed_height, notification_view()->message_view_->height());
+ EXPECT_EQ(collapsed_preferred_height,
+ notification_view()->GetPreferredSize().height());
+}
+
+} // namespace message_center
diff --git a/chromium/ui/message_center/views/notification_view_unittest.cc b/chromium/ui/message_center/views/notification_view_unittest.cc
index 299a1068279..4e5ac153ed8 100644
--- a/chromium/ui/message_center/views/notification_view_unittest.cc
+++ b/chromium/ui/message_center/views/notification_view_unittest.cc
@@ -29,6 +29,8 @@
#include "ui/message_center/views/message_center_controller.h"
#include "ui/message_center/views/message_view_factory.h"
#include "ui/message_center/views/notification_button.h"
+#include "ui/message_center/views/notification_control_buttons_view.h"
+#include "ui/message_center/views/padded_button.h"
#include "ui/message_center/views/proportional_image_view.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/layout/fill_layout.h"
@@ -160,8 +162,12 @@ class NotificationViewTest : public views::ViewsTestBase,
}
}
- views::ImageButton* GetCloseButton() {
- return notification_view()->close_button();
+ PaddedButton* GetCloseButton() {
+ return notification_view()->control_buttons_view_->close_button();
+ }
+
+ PaddedButton* GetSettingsButton() {
+ return notification_view()->control_buttons_view_->settings_button();
}
void UpdateNotificationViews() {
@@ -308,11 +314,16 @@ TEST_F(NotificationViewTest, CreateOrUpdateTest) {
notification()->set_message(base::ASCIIToUTF16(""));
notification()->set_icon(gfx::Image());
- notification_view()->CreateOrUpdateViews(*notification());
+ notification_view()->UpdateWithNotification(*notification());
EXPECT_TRUE(NULL == notification_view()->title_view_);
EXPECT_TRUE(NULL == notification_view()->message_view_);
EXPECT_TRUE(NULL == notification_view()->image_view_);
- EXPECT_TRUE(NULL == notification_view()->settings_button_view_);
+ // Notification must have a control buttons view.
+ EXPECT_TRUE(NULL != notification_view()->control_buttons_view_);
+ // Notification is not pinned and have a close button by default.
+ EXPECT_TRUE(NULL != GetCloseButton());
+ // Notification doesn't have a settings button by default.
+ EXPECT_TRUE(NULL == GetSettingsButton());
// We still expect an icon view for all layouts.
EXPECT_TRUE(NULL != notification_view()->icon_view_);
}
@@ -328,11 +339,12 @@ TEST_F(NotificationViewTest, CreateOrUpdateTestSettingsButton) {
NotifierId(NotifierId::APPLICATION, "extension_id"),
*data(), delegate.get());
- notification_view()->CreateOrUpdateViews(notf);
+ notification_view()->UpdateWithNotification(notf);
EXPECT_TRUE(NULL != notification_view()->title_view_);
EXPECT_TRUE(NULL != notification_view()->message_view_);
EXPECT_TRUE(NULL != notification_view()->context_message_view_);
- EXPECT_TRUE(NULL != notification_view()->settings_button_view_);
+ EXPECT_TRUE(NULL != GetCloseButton());
+ EXPECT_TRUE(NULL != GetSettingsButton());
EXPECT_TRUE(NULL != notification_view()->icon_view_);
EXPECT_TRUE(NULL == notification_view()->image_view_);
@@ -341,21 +353,21 @@ TEST_F(NotificationViewTest, CreateOrUpdateTestSettingsButton) {
TEST_F(NotificationViewTest, TestLineLimits) {
notification()->set_image(CreateTestImage(0, 0));
notification()->set_context_message(base::ASCIIToUTF16(""));
- notification_view()->CreateOrUpdateViews(*notification());
+ notification_view()->UpdateWithNotification(*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());
+ notification_view()->UpdateWithNotification(*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::ASCIIToUTF16("foo"));
- notification_view()->CreateOrUpdateViews(*notification());
+ notification_view()->UpdateWithNotification(*notification());
EXPECT_TRUE(notification_view()->context_message_view_ != NULL);
@@ -448,7 +460,7 @@ TEST_F(NotificationViewTest, TestImageSizing) {
TEST_F(NotificationViewTest, UpdateButtonsStateTest) {
notification()->set_buttons(CreateButtons(2));
- notification_view()->CreateOrUpdateViews(*notification());
+ notification_view()->UpdateWithNotification(*notification());
widget()->Show();
EXPECT_EQ(views::CustomButton::STATE_NORMAL,
@@ -466,7 +478,7 @@ TEST_F(NotificationViewTest, UpdateButtonsStateTest) {
EXPECT_EQ(views::CustomButton::STATE_HOVERED,
notification_view()->action_buttons_[0]->state());
- notification_view()->CreateOrUpdateViews(*notification());
+ notification_view()->UpdateWithNotification(*notification());
EXPECT_EQ(views::CustomButton::STATE_HOVERED,
notification_view()->action_buttons_[0]->state());
@@ -484,7 +496,7 @@ TEST_F(NotificationViewTest, UpdateButtonsStateTest) {
TEST_F(NotificationViewTest, UpdateButtonCountTest) {
notification()->set_buttons(CreateButtons(2));
- notification_view()->CreateOrUpdateViews(*notification());
+ notification_view()->UpdateWithNotification(*notification());
widget()->Show();
EXPECT_EQ(views::CustomButton::STATE_NORMAL,
@@ -509,7 +521,7 @@ TEST_F(NotificationViewTest, UpdateButtonCountTest) {
notification_view()->action_buttons_[1]->state());
notification()->set_buttons(CreateButtons(1));
- notification_view()->CreateOrUpdateViews(*notification());
+ notification_view()->UpdateWithNotification(*notification());
EXPECT_EQ(views::CustomButton::STATE_HOVERED,
notification_view()->action_buttons_[0]->state());
@@ -536,19 +548,16 @@ TEST_F(NotificationViewTest, SettingsButtonTest) {
GURL("https://hello.com"),
NotifierId(NotifierId::APPLICATION, "extension_id"),
*data(), delegate.get());
- notification_view()->CreateOrUpdateViews(notf);
+ notification_view()->UpdateWithNotification(notf);
widget()->Show();
- notification_view()->Layout();
- EXPECT_TRUE(NULL != notification_view()->settings_button_view_);
- EXPECT_EQ(views::CustomButton::STATE_NORMAL,
- notification_view()->settings_button_view_->state());
+ EXPECT_TRUE(NULL != GetSettingsButton());
+ EXPECT_EQ(views::CustomButton::STATE_NORMAL, GetSettingsButton()->state());
// Now construct a mouse move event 1 pixel inside the boundary of the action
// button.
gfx::Point cursor_location(1, 1);
- views::View::ConvertPointToScreen(notification_view()->settings_button_view_,
- &cursor_location);
+ views::View::ConvertPointToScreen(GetSettingsButton(), &cursor_location);
ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
widget()->OnMouseEvent(&move);
@@ -556,8 +565,7 @@ TEST_F(NotificationViewTest, SettingsButtonTest) {
views::test::WidgetTest::GetEventSink(widget())->OnEventFromSource(&move);
EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_EQ(views::CustomButton::STATE_HOVERED,
- notification_view()->settings_button_view_->state());
+ EXPECT_EQ(views::CustomButton::STATE_HOVERED, GetSettingsButton()->state());
// Now construct a mouse move event 1 pixel outside the boundary of the
// widget.
@@ -566,8 +574,7 @@ TEST_F(NotificationViewTest, SettingsButtonTest) {
ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
widget()->OnMouseEvent(&move);
- EXPECT_EQ(views::CustomButton::STATE_NORMAL,
- notification_view()->settings_button_view_->state());
+ EXPECT_EQ(views::CustomButton::STATE_NORMAL, GetSettingsButton()->state());
}
TEST_F(NotificationViewTest, ViewOrderingTest) {
diff --git a/chromium/ui/message_center/views/notifier_settings_view.cc b/chromium/ui/message_center/views/notifier_settings_view.cc
index 3e541c754a2..7958f037b9f 100644
--- a/chromium/ui/message_center/views/notifier_settings_view.cc
+++ b/chromium/ui/message_center/views/notifier_settings_view.cc
@@ -21,6 +21,7 @@
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
#include "ui/message_center/message_center_style.h"
@@ -424,8 +425,7 @@ NotifierSettingsView::NotifierSettingsView(NotifierSettingsProvider* provider)
provider_->AddObserver(this);
SetFocusBehavior(FocusBehavior::ALWAYS);
- set_background(
- views::Background::CreateSolidBackground(kMessageCenterBackgroundColor));
+ SetBackground(views::CreateSolidBackground(kMessageCenterBackgroundColor));
SetPaintToLayer();
title_label_ = new views::Label(
@@ -440,6 +440,7 @@ NotifierSettingsView::NotifierSettingsView(NotifierSettingsProvider* provider)
AddChildView(title_label_);
scroller_ = new views::ScrollView();
+ scroller_->SetBackgroundColor(kMessageCenterBackgroundColor);
scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false));
scroller_->SetHorizontalScrollBar(new views::OverlayScrollBar(true));
AddChildView(scroller_);
@@ -489,12 +490,14 @@ void NotifierSettingsView::UpdateContentsView(
buttons_.clear();
views::View* contents_view = new views::View();
- contents_view->SetLayoutManager(new views::BoxLayout(
- views::BoxLayout::kVertical, settings::kHorizontalMargin, 0, 0));
+ contents_view->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kVertical,
+ gfx::Insets(0, settings::kHorizontalMargin)));
views::View* contents_title_view = new views::View();
- contents_title_view->SetLayoutManager(new views::BoxLayout(
- views::BoxLayout::kVertical, 0, 0, kComputedTitleElementSpacing));
+ contents_title_view->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kVertical, gfx::Insets(),
+ kComputedTitleElementSpacing));
bool need_account_switcher =
provider_ && provider_->GetNotifierGroupCount() > 1;
diff --git a/chromium/ui/message_center/views/padded_button.cc b/chromium/ui/message_center/views/padded_button.cc
index 08e3e6f1620..b85350446e1 100644
--- a/chromium/ui/message_center/views/padded_button.cc
+++ b/chromium/ui/message_center/views/padded_button.cc
@@ -4,6 +4,7 @@
#include "ui/message_center/views/padded_button.h"
+#include "base/memory/ptr_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/message_center/message_center_style.h"
@@ -22,8 +23,7 @@ PaddedButton::PaddedButton(views::ButtonListener* listener)
SetFocusPainter(views::Painter::CreateSolidFocusPainter(
kFocusBorderColor,
gfx::Insets(1, 2, 2, 2)));
- set_background(
- views::Background::CreateSolidBackground(kControlButtonBackgroundColor));
+ SetBackground(views::CreateSolidBackground(kControlButtonBackgroundColor));
SetBorder(views::CreateEmptyBorder(gfx::Insets(kControlButtonBorderSize)));
set_animate_on_state_change(false);
diff --git a/chromium/ui/message_center/views/toast_contents_view.cc b/chromium/ui/message_center/views/toast_contents_view.cc
index 785ffa6c05e..20c1ec930e8 100644
--- a/chromium/ui/message_center/views/toast_contents_view.cc
+++ b/chromium/ui/message_center/views/toast_contents_view.cc
@@ -12,6 +12,8 @@
#include "base/time/time.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_node_data.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_targeter.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/animation/animation_delegate.h"
@@ -65,7 +67,7 @@ ToastContentsView::ToastContentsView(
// Sets the transparent background. Then, when the message view is slid out,
// the whole toast seems to slide although the actual bound of the widget
// remains. This is hacky but easier to keep the consistency.
- set_background(views::Background::CreateSolidBackground(0, 0, 0, 0));
+ SetBackground(views::CreateSolidBackground(SK_ColorTRANSPARENT));
fade_animation_.reset(new gfx::SlideAnimation(this));
fade_animation_->SetSlideDuration(kFadeInOutDuration);
@@ -171,12 +173,6 @@ void ToastContentsView::SetBoundsWithAnimation(gfx::Rect new_bounds) {
bounds_animation_->Show();
}
-void ToastContentsView::ActivateToast() {
- set_can_activate(true);
- if (GetWidget())
- GetWidget()->Activate();
-}
-
void ToastContentsView::StartFadeIn() {
// The decrement is done in OnBoundsAnimationEndedOrCancelled callback.
if (collection_)
@@ -408,6 +404,15 @@ void ToastContentsView::CreateWidget(
#endif
widget->Init(params);
+
+#if defined(OS_CHROMEOS)
+ // On Chrome OS, this widget is shown in the shelf container. It means this
+ // widget would inherit the parent's window targeter (ShelfWindowTarget) by
+ // default. But it is not good for popup. So we override it with the normal
+ // WindowTargeter.
+ gfx::NativeWindow native_window = widget->GetNativeWindow();
+ native_window->SetEventTargeter(base::MakeUnique<aura::WindowTargeter>());
+#endif
}
gfx::Rect ToastContentsView::GetClosedToastBounds(gfx::Rect bounds) {
diff --git a/chromium/ui/message_center/views/toast_contents_view.h b/chromium/ui/message_center/views/toast_contents_view.h
index 7c762d145b2..ee1895584c9 100644
--- a/chromium/ui/message_center/views/toast_contents_view.h
+++ b/chromium/ui/message_center/views/toast_contents_view.h
@@ -75,9 +75,6 @@ class MESSAGE_CENTER_EXPORT ToastContentsView
void SetBoundsWithAnimation(gfx::Rect new_bounds);
- // Makes the toast activatable, then activate.
- void ActivateToast();
-
// Origin and bounds are not 'instant', but rather 'current stable values',
// there could be animation in progress that targets these values.
gfx::Point origin() { return origin_; }