blob: f3c8e44f0dbeef2b238edbe0e5446d3b18daae9b [file] [log] [blame]
// Copyright 2018 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 "ash/app_menu/notification_menu_view.h"
#include "ash/app_menu/notification_item_view.h"
#include "ash/app_menu/notification_menu_header_view.h"
#include "ash/app_menu/notification_overflow_view.h"
#include "ash/public/cpp/app_menu_constants.h"
#include "ui/base/models/menu_separator_types.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/views/proportional_image_view.h"
#include "ui/views/controls/menu/menu_config.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_separator.h"
namespace ash {
NotificationMenuView::NotificationMenuView(
Delegate* notification_item_view_delegate,
message_center::SlideOutController::Delegate* slide_out_controller_delegate,
const std::string& app_id)
: app_id_(app_id),
notification_item_view_delegate_(notification_item_view_delegate),
slide_out_controller_delegate_(slide_out_controller_delegate),
double_separator_(
new views::MenuSeparator(ui::MenuSeparatorType::DOUBLE_SEPARATOR)),
header_view_(new NotificationMenuHeaderView()) {
DCHECK(notification_item_view_delegate_);
DCHECK(slide_out_controller_delegate_);
DCHECK(!app_id_.empty())
<< "Only context menus for applications can show notifications.";
AddChildView(double_separator_);
AddChildView(header_view_);
}
NotificationMenuView::~NotificationMenuView() = default;
gfx::Size NotificationMenuView::CalculatePreferredSize() const {
return gfx::Size(
views::MenuConfig::instance().touchable_menu_width,
double_separator_->GetPreferredSize().height() +
header_view_->GetPreferredSize().height() +
kNotificationItemViewHeight +
(overflow_view_ ? overflow_view_->GetPreferredSize().height() : 0));
}
void NotificationMenuView::Layout() {
int y = 0;
double_separator_->SetBoundsRect(
gfx::Rect(gfx::Point(0, y),
gfx::Size(views::MenuConfig::instance().touchable_menu_width,
double_separator_->GetPreferredSize().height())));
y += double_separator_->GetPreferredSize().height();
header_view_->SetBoundsRect(
gfx::Rect(gfx::Point(0, y), header_view_->GetPreferredSize()));
y += header_view_->GetPreferredSize().height();
auto* item = GetDisplayedNotificationItemView();
if (item) {
item->SetBoundsRect(gfx::Rect(gfx::Point(0, y), item->GetPreferredSize()));
y = item->bounds().bottom();
}
if (overflow_view_) {
overflow_view_->SetBoundsRect(
gfx::Rect(gfx::Point(0, y), overflow_view_->GetPreferredSize()));
}
}
bool NotificationMenuView::IsEmpty() const {
return notification_item_views_.empty();
}
void NotificationMenuView::AddNotificationItemView(
const message_center::Notification& notification) {
auto* old_displayed_item = GetDisplayedNotificationItemView();
auto notification_view = std::make_unique<NotificationItemView>(
notification_item_view_delegate_, slide_out_controller_delegate_,
notification.title(), notification.message(), notification.icon(),
notification.id());
notification_view->set_owned_by_client();
AddChildView(notification_view.get());
notification_item_views_.push_front(std::move(notification_view));
header_view_->UpdateCounter(notification_item_views_.size());
if (!old_displayed_item)
return;
// Push |old_displayed_notification_item_view| to |overflow_view_|.
RemoveChildView(old_displayed_item);
const bool overflow_view_created = !overflow_view_;
if (!overflow_view_) {
overflow_view_ = std::make_unique<NotificationOverflowView>();
overflow_view_->set_owned_by_client();
AddChildView(overflow_view_.get());
}
overflow_view_->AddIcon(old_displayed_item->proportional_image_view(),
old_displayed_item->notification_id());
if (overflow_view_created) {
PreferredSizeChanged();
// OnOverflowAddedOrRemoved must be called after PreferredSizeChange to
// ensure that enough room is allocated for the overflow view.
notification_item_view_delegate_->OnOverflowAddedOrRemoved();
}
Layout();
}
void NotificationMenuView::UpdateNotificationItemView(
const message_center::Notification& notification) {
// Find the view which corresponds to |notification|.
const auto i = NotificationIterForId(notification.id());
if (i == notification_item_views_.end())
return;
(*i)->UpdateContents(notification.title(), notification.message(),
notification.icon());
}
void NotificationMenuView::OnNotificationRemoved(
const std::string& notification_id) {
// Find the view which corresponds to |notification_id|.
const auto i = NotificationIterForId(notification_id);
if (i == notification_item_views_.end())
return;
const bool removed_displayed_notification =
i->get() == GetDisplayedNotificationItemView();
notification_item_views_.erase(i);
header_view_->UpdateCounter(notification_item_views_.size());
if (removed_displayed_notification) {
// Display the next notification.
auto* item = GetDisplayedNotificationItemView();
if (item) {
AddChildView(item);
if (overflow_view_)
overflow_view_->RemoveIcon(item->notification_id());
}
} else if (overflow_view_) {
overflow_view_->RemoveIcon(notification_id);
}
if (overflow_view_ && overflow_view_->is_empty()) {
overflow_view_.reset();
PreferredSizeChanged();
notification_item_view_delegate_->OnOverflowAddedOrRemoved();
}
}
ui::Layer* NotificationMenuView::GetSlideOutLayer() {
auto* item = GetDisplayedNotificationItemView();
return item ? item->layer() : nullptr;
}
const NotificationItemView*
NotificationMenuView::GetDisplayedNotificationItemView() const {
return notification_item_views_.empty()
? nullptr
: notification_item_views_.front().get();
}
const std::string& NotificationMenuView::GetDisplayedNotificationID() const {
DCHECK(!notification_item_views_.empty());
return GetDisplayedNotificationItemView()->notification_id();
}
NotificationMenuView::NotificationItemViews::iterator
NotificationMenuView::NotificationIterForId(const std::string& id) {
return std::find_if(
notification_item_views_.begin(), notification_item_views_.end(),
[id](const auto& item) { return item->notification_id() == id; });
}
} // namespace ash