| Index: ui/message_center/views/notification_view_md.cc
|
| diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5bc79bc9a6ec7150cd32af78df7421cb7ea801c4
|
| --- /dev/null
|
| +++ b/ui/message_center/views/notification_view_md.cc
|
| @@ -0,0 +1,455 @@
|
| +// 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/strings/string_util.h"
|
| +#include "ui/base/cursor/cursor.h"
|
| +#include "ui/base/l10n/l10n_util.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_button.h"
|
| +#include "ui/message_center/views/padded_button.h"
|
| +#include "ui/strings/grit/ui_strings.h"
|
| +#include "ui/views/background.h"
|
| +#include "ui/views/border.h"
|
| +#include "ui/views/controls/image_view.h"
|
| +#include "ui/views/controls/label.h"
|
| +#include "ui/views/focus/focus_manager.h"
|
| +#include "ui/views/layout/box_layout.h"
|
| +#include "ui/views/native_cursor.h"
|
| +#include "ui/views/view_targeter.h"
|
| +
|
| +namespace message_center {
|
| +
|
| +namespace {
|
| +
|
| +// Dimensions.
|
| +constexpr int kNotificationRightPadding = 13;
|
| +constexpr int kNotificationLeftPadding = 13;
|
| +constexpr int kNotificationTopPadding = 6;
|
| +constexpr int kNotificationBottomPadding = 10;
|
| +constexpr gfx::Insets kNotificationPadding(kNotificationTopPadding,
|
| + kNotificationLeftPadding,
|
| + kNotificationBottomPadding,
|
| + kNotificationRightPadding);
|
| +
|
| +constexpr int kMaxContextTitleLines = 1;
|
| +
|
| +// Foreground of small icon image.
|
| +constexpr SkColor kSmallImageBackgroundColor = SK_ColorWHITE;
|
| +// Background of small icon image.
|
| +const SkColor kSmallImageColor = SkColorSetRGB(0x43, 0x43, 0x43);
|
| +
|
| +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);
|
| +}
|
| +
|
| +} // 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 (settings_button_)
|
| + buttons.push_back(settings_button_.get());
|
| + if (close_button_)
|
| + buttons.push_back(close_button_.get());
|
| +
|
| + 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);
|
| + CreateOrUpdateProgressBarView(notification);
|
| + CreateOrUpdateListItemViews(notification);
|
| + CreateOrUpdateIconView(notification);
|
| + CreateOrUpdateSmallIconView(notification);
|
| + CreateOrUpdateImageView(notification);
|
| + CreateOrUpdateActionButtonViews(notification);
|
| + CreateOrUpdateCloseButtonView(notification);
|
| + CreateOrUpdateSettingsButtonView(notification);
|
| +}
|
| +
|
| +NotificationViewMD::NotificationViewMD(MessageCenterController* controller,
|
| + const Notification& notification)
|
| + : MessageView(controller, notification),
|
| + clickable_(notification.clickable()) {
|
| + layout_ = new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 2);
|
| + layout_->set_inside_border_insets(kNotificationPadding);
|
| + SetLayoutManager(layout_);
|
| +
|
| + // Create the top_view_, which collects into a vertical box all content
|
| + // at the top of the notification (to the right of the icon) except for the
|
| + // close button.
|
| + top_view_ = new views::View();
|
| + views::BoxLayout* top_box_layout =
|
| + new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 1, 5);
|
| + top_box_layout->set_cross_axis_alignment(
|
| + views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
|
| + top_view_->SetLayoutManager(top_box_layout);
|
| + AddChildView(top_view_);
|
| +
|
| + main_view_ = new views::View();
|
| + main_view_->SetLayoutManager(
|
| + new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
|
| + AddChildView(main_view_);
|
| +
|
| + // Create the bottom_view_, which collects notification icon.
|
| + bottom_view_ = new views::View();
|
| + bottom_view_->SetLayoutManager(
|
| + new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
|
| + AddChildView(bottom_view_);
|
| +
|
| + views::ImageView* small_image_view = new views::ImageView();
|
| + small_image_view->SetImageSize(gfx::Size(kSmallImageSize, kSmallImageSize));
|
| + small_image_view->set_owned_by_client();
|
| + small_image_view_.reset(small_image_view);
|
| + top_view_->AddChildView(small_image_view_.get());
|
| +
|
| + CreateOrUpdateViews(notification);
|
| +
|
| + SetEventTargeter(
|
| + std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
|
| +}
|
| +
|
| +NotificationViewMD::~NotificationViewMD() {}
|
| +
|
| +void NotificationViewMD::Layout() {
|
| + MessageView::Layout();
|
| +
|
| + // Before any resizing, set or adjust the number of message lines.
|
| + int title_lines = 0;
|
| + if (title_view_) {
|
| + title_lines = title_view_->GetLinesForWidthAndLimit(title_view_->width(),
|
| + kMaxTitleLines);
|
| + }
|
| + if (message_view_) {
|
| + message_view_->SetLineLimit(
|
| + std::max(0, message_center::kMessageExpandedLineLimit - title_lines));
|
| + }
|
| +
|
| + // Settings & Bottom views.
|
| + if (settings_button_) {
|
| + gfx::Rect content_bounds = GetContentsBounds();
|
| + const gfx::Size settings_size(settings_button_->GetPreferredSize());
|
| + int marginFromRight = settings_size.width() + kControlButtonPadding;
|
| + if (close_button_)
|
| + marginFromRight += close_button_->GetPreferredSize().width();
|
| + gfx::Rect settings_rect(content_bounds.right() - marginFromRight,
|
| + GetContentsBounds().y() + kControlButtonPadding,
|
| + settings_size.width(), settings_size.height());
|
| + settings_button_->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);
|
| + }
|
| +}
|
| +
|
| +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::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 (close_button_ && sender == close_button_.get()) {
|
| + // Warning: This causes the NotificationViewMD itself to be deleted, so
|
| + // don't do anything afterwards.
|
| + OnCloseButtonPressed();
|
| + return;
|
| + }
|
| +
|
| + if (sender == settings_button_.get()) {
|
| + 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]) {
|
| + controller()->ClickOnNotificationButton(id, i);
|
| + return;
|
| + }
|
| + }
|
| +}
|
| +
|
| +bool NotificationViewMD::IsCloseButtonFocused() const {
|
| + if (!close_button_)
|
| + return false;
|
| +
|
| + const views::FocusManager* focus_manager = GetFocusManager();
|
| + return focus_manager &&
|
| + focus_manager->GetFocusedView() == close_button_.get();
|
| +}
|
| +
|
| +void NotificationViewMD::RequestFocusOnCloseButton() {
|
| + if (close_button_)
|
| + close_button_->RequestFocus();
|
| +}
|
| +
|
| +void NotificationViewMD::CreateOrUpdateContextTitleView(
|
| + const Notification& notification) {
|
| + DCHECK(top_view_);
|
| +
|
| + const gfx::FontList& font_list = views::Label().font_list().Derive(
|
| + -2, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL);
|
| +
|
| + base::string16 sub_title = notification.display_source();
|
| + if (!context_title_view_) {
|
| + context_title_view_ = new BoundedLabel(sub_title, font_list);
|
| + context_title_view_->SetLineHeight(kTitleLineHeight);
|
| + context_title_view_->SetLineLimit(kMaxContextTitleLines);
|
| + top_view_->AddChildView(context_title_view_);
|
| + } else {
|
| + context_title_view_->SetText(sub_title);
|
| + }
|
| +}
|
| +
|
| +void NotificationViewMD::CreateOrUpdateTitleView(
|
| + const Notification& notification) {
|
| + DCHECK(top_view_ != NULL);
|
| +
|
| + 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 BoundedLabel(title, font_list);
|
| + title_view_->SetLineHeight(kMessageLineHeight);
|
| + title_view_->SetColors(message_center::kRegularTextColor,
|
| + kDimTextBackgroundColor);
|
| + main_view_->AddChildView(title_view_);
|
| + } else {
|
| + title_view_->SetText(title);
|
| + }
|
| +}
|
| +
|
| +void NotificationViewMD::CreateOrUpdateMessageView(
|
| + const Notification& notification) {
|
| + if (notification.message().empty()) {
|
| + // Deletion will also remove |context_message_view_| from its parent.
|
| + delete message_view_;
|
| + message_view_ = nullptr;
|
| + return;
|
| + }
|
| +
|
| + DCHECK(top_view_ != NULL);
|
| +
|
| + 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(message_center::kMessageExpandedLineLimit);
|
| + message_view_->SetColors(message_center::kDimTextColor,
|
| + kContextTextBackgroundColor);
|
| + main_view_->AddChildView(message_view_);
|
| + } else {
|
| + message_view_->SetText(text);
|
| + }
|
| +}
|
| +
|
| +void NotificationViewMD::CreateOrUpdateProgressBarView(
|
| + const Notification& notification) {
|
| + // TODO(yoshiki): Implement this.
|
| +}
|
| +
|
| +void NotificationViewMD::CreateOrUpdateListItemViews(
|
| + const Notification& notification) {
|
| + // TODO(yoshiki): Implement this.
|
| +}
|
| +
|
| +void NotificationViewMD::CreateOrUpdateIconView(
|
| + const Notification& notification) {
|
| + // TODO(yoshiki): Implement this.
|
| +}
|
| +
|
| +void NotificationViewMD::CreateOrUpdateSmallIconView(
|
| + const Notification& notification) {
|
| + gfx::ImageSkia icon =
|
| + notification.small_image().IsEmpty()
|
| + ? GetProductIcon()
|
| + : GetMaskedIcon(notification.small_image().AsImageSkia());
|
| +
|
| + small_image_view_->SetImage(icon);
|
| +}
|
| +
|
| +void NotificationViewMD::CreateOrUpdateImageView(
|
| + const Notification& notification) {
|
| + // TODO(yoshiki): Implement this.
|
| +}
|
| +
|
| +void NotificationViewMD::CreateOrUpdateActionButtonViews(
|
| + const Notification& notification) {
|
| + // TODO(yoshiki): Implement this.
|
| +}
|
| +
|
| +void NotificationViewMD::CreateOrUpdateCloseButtonView(
|
| + 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();
|
| + }
|
| +}
|
| +
|
| +void NotificationViewMD::CreateOrUpdateSettingsButtonView(
|
| + const Notification& notification) {
|
| + if (!settings_button_ && notification.delegate() &&
|
| + notification.delegate()->ShouldDisplaySettingsButton()) {
|
| + settings_button_ = base::MakeUnique<PaddedButton>(this);
|
| + 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));
|
| + settings_button_->set_owned_by_client();
|
| + AddChildView(settings_button_.get());
|
| + } else {
|
| + settings_button_.reset();
|
| + }
|
| + UpdateControlButtonsVisibility();
|
| +}
|
| +
|
| +void NotificationViewMD::UpdateControlButtonsVisibility() {
|
| + const bool target_visibility =
|
| + IsMouseHovered() || HasFocus() ||
|
| + (close_button_ && close_button_->HasFocus()) ||
|
| + (settings_button_ && settings_button_->HasFocus());
|
| +
|
| + if (close_button_) {
|
| + if (target_visibility != close_button_->visible())
|
| + close_button_->SetVisible(target_visibility);
|
| + }
|
| +
|
| + if (settings_button_) {
|
| + if (target_visibility != settings_button_->visible())
|
| + settings_button_->SetVisible(target_visibility);
|
| + }
|
| +}
|
| +
|
| +} // namespace message_center
|
|
|