// 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_overflow_view.h" #include "ash/public/cpp/app_menu_constants.h" #include "ui/base/models/menu_separator_types.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/image/image.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/message_center/views/proportional_image_view.h" #include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_separator.h" #include "ui/views/vector_icons.h" namespace { // Padding in dips between the overflow separator and overflow icons. constexpr int kOverflowSeparatorToIconPadding = 8; // Padding in dips below the overflow icons. constexpr int kOverflowAreaBottomPadding = 12; // Size of overflow icons in dips. constexpr int kIconSize = 16; // Size used for laying out overflow icons in dips to prevent clipping. constexpr int kIconLayoutSize = kIconSize + 1; // Padding between overflow icons in dips. constexpr int kInterIconPadding = 8; } // namespace namespace ash { // The icon which represents a notification. class NotificationOverflowImageView : public message_center::ProportionalImageView { public: NotificationOverflowImageView(const ui::ImageModel& image, const std::string& notification_id) : message_center::ProportionalImageView(gfx::Size(kIconSize, kIconSize)), notification_id_(notification_id) { SetID(kNotificationOverflowIconId); SetImage(image, gfx::Size(kIconSize, kIconSize)); } NotificationOverflowImageView(const NotificationOverflowImageView&) = delete; NotificationOverflowImageView& operator=( const NotificationOverflowImageView&) = delete; ~NotificationOverflowImageView() override = default; const std::string& notification_id() const { return notification_id_; } private: std::string const notification_id_; }; NotificationOverflowView::NotificationOverflowView() : separator_(AddChildView(std::make_unique( ui::MenuSeparatorType::NORMAL_SEPARATOR))) { SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR( 0, kNotificationHorizontalPadding, kOverflowAreaBottomPadding, kNotificationHorizontalPadding))); SetBackground(views::CreateSolidBackground(SK_ColorWHITE)); } NotificationOverflowView::~NotificationOverflowView() = default; void NotificationOverflowView::AddIcon( const message_center::ProportionalImageView& image_view, const std::string& notification_id) { // Insert the image at the front of the list, so that it appears on the right // side. image_views_.insert( image_views_.begin(), AddChildView(std::make_unique( image_view.image(), notification_id))); if (image_views_.size() > kMaxOverflowIcons) { if (!overflow_icon_) { auto icon = ui::ImageModel::FromVectorIcon(views::kOptionsIcon, ui::kColorIcon, kIconSize); auto overflow_icon = std::make_unique( gfx::Size(kIconSize, kIconSize)); overflow_icon->SetID(kOverflowIconId); overflow_icon->SetImage(icon, gfx::Size(kIconSize, kIconSize)); overflow_icon_ = AddChildView(std::move(overflow_icon)); } overflow_icon_->SetVisible(true); image_views_.at(kMaxOverflowIcons)->SetVisible(false); } Layout(); } void NotificationOverflowView::RemoveIcon(const std::string& notification_id) { auto it = std::find_if(image_views_.begin(), image_views_.end(), [notification_id](const auto& item) { return item->notification_id() == notification_id; }); if (it != image_views_.end()) { RemoveChildViewT(*it); image_views_.erase(it); MaybeRemoveOverflowIcon(); Layout(); } } void NotificationOverflowView::Layout() { separator_->SetBoundsRect( gfx::Rect(width(), separator_->GetPreferredSize().height())); int x = width() - GetInsets().right(); const int y = separator_->GetPreferredSize().height() + kOverflowSeparatorToIconPadding; for (size_t i = 0; i < image_views_.size() && i <= kMaxOverflowIcons; ++i) { views::View* icon = image_views_.at(i); if (i == kMaxOverflowIcons) icon = overflow_icon_; x -= kIconLayoutSize; icon->SetBounds(x, y, kIconLayoutSize, kIconLayoutSize); x -= kInterIconPadding; } } gfx::Size NotificationOverflowView::CalculatePreferredSize() const { // This view is the last element in a MenuItemView, which means it has extra // padding on the bottom due to the corner radius of the root MenuItemView. If // the corner radius changes, |kOverflowSeparatorToIconPadding| must be // modified to vertically center the overflow icons. return gfx::Size(views::MenuConfig::instance().touchable_menu_min_width, separator_->GetPreferredSize().height() + kOverflowSeparatorToIconPadding + kIconLayoutSize); } void NotificationOverflowView::MaybeRemoveOverflowIcon() { if (!overflow_icon_ || image_views_.size() > kMaxOverflowIcons) return; overflow_icon_->SetVisible(false); } } // namespace ash