blob: 7a86c5bc7e9e7e2414f81900eaea6e48f5110a06 [file] [log] [blame]
// 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/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 kImageContainerPadding(0, 12, 12, 12);
constexpr gfx::Insets kActionButtonPadding(0, 12, 0, 12);
constexpr gfx::Size kActionButtonMinSize(88, 32);
// 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);
// Max number of lines for message_view_.
constexpr int kMaxLinesForMessageView = 1;
constexpr int kMaxLinesForExpandedMessageView = 4;
constexpr int kCompactTitleMessageViewSpacing = 12;
constexpr int kProgressBarHeight = 4;
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;
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;
// 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;
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() {}
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.
class NotificationButtonMD : public views::LabelButton {
public:
NotificationButtonMD(views::ButtonListener* listener,
const base::string16& text);
~NotificationButtonMD() 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, text, views::style::CONTEXT_BUTTON_MD) {
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;
std::unique_ptr<views::InkDropHighlight>
NotificationButtonMD::CreateInkDropHighlight() const {
std::unique_ptr<views::InkDropHighlight> highlight =
views::LabelButton::CreateInkDropHighlight();
highlight->set_visible_opacity(kActionButtonInkDropHighlightVisibleOpacity);
return highlight;
}
} // 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);
CreateOrUpdateActionButtonViews(notification);
CreateOrUpdateCloseButtonView(notification);
CreateOrUpdateSettingsButtonView(notification);
UpdateViewForExpandedState(expanded_);
}
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.type() == NOTIFICATION_TYPE_PROGRESS) {
left_content_->RemoveChildView(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);
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) {
left_content_->RemoveChildView(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) {
left_content_->RemoveChildView(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) {
right_content_->RemoveChildView(icon_view_);
icon_view_ = nullptr;
return;
}
gfx::Size image_view_size(30, 30);
if (!icon_view_) {
icon_view_ = new ProportionalImageView(image_view_size);
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) {
// |image_view_| is the view representing the area covered by the
// notification's image, including background and border. Its size can be
// specified in advance and images will be scaled to fit including a border if
// necessary.
if (notification.image().IsEmpty()) {
if (image_container_) {
DCHECK(image_view_);
left_content_->RemoveChildView(image_container_);
image_container_ = NULL;
image_view_ = NULL;
} else {
DCHECK(!image_view_);
}
return;
}
gfx::Size ideal_size(kNotificationPreferredImageWidth,
kNotificationPreferredImageHeight);
if (!image_container_) {
image_container_ = new views::View();
image_container_->SetLayoutManager(new views::FillLayout());
image_container_->SetBorder(
views::CreateEmptyBorder(kImageContainerPadding));
image_container_->SetBackground(
views::CreateSolidBackground(message_center::kImageBackgroundColor));
DCHECK(!image_view_);
image_view_ = new message_center::ProportionalImageView(ideal_size);
image_container_->AddChildView(image_view_);
// Insert the created image container just after the |content_row_|.
AddChildViewAt(image_container_, GetIndexOf(content_row_) + 1);
}
DCHECK(image_view_);
image_view_->SetImage(notification.image().AsImageSkia(), ideal_size);
}
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();
}
}
if (new_buttons) {
// TODO(fukino): Investigate if this Layout() is necessary.
Layout();
views::Widget* widget = GetWidget();
if (widget != NULL) {
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_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_)
image_container_->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);
}
} // namespace message_center