notification_overflow_view.cc 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/app_menu/notification_overflow_view.h"
  5. #include "ash/public/cpp/app_menu_constants.h"
  6. #include "ui/base/models/menu_separator_types.h"
  7. #include "ui/gfx/geometry/rect.h"
  8. #include "ui/gfx/geometry/size.h"
  9. #include "ui/gfx/image/image.h"
  10. #include "ui/gfx/paint_vector_icon.h"
  11. #include "ui/message_center/views/proportional_image_view.h"
  12. #include "ui/views/background.h"
  13. #include "ui/views/border.h"
  14. #include "ui/views/controls/menu/menu_config.h"
  15. #include "ui/views/controls/menu/menu_separator.h"
  16. #include "ui/views/vector_icons.h"
  17. namespace {
  18. // Padding in dips between the overflow separator and overflow icons.
  19. constexpr int kOverflowSeparatorToIconPadding = 8;
  20. // Padding in dips below the overflow icons.
  21. constexpr int kOverflowAreaBottomPadding = 12;
  22. // Size of overflow icons in dips.
  23. constexpr int kIconSize = 16;
  24. // Size used for laying out overflow icons in dips to prevent clipping.
  25. constexpr int kIconLayoutSize = kIconSize + 1;
  26. // Padding between overflow icons in dips.
  27. constexpr int kInterIconPadding = 8;
  28. } // namespace
  29. namespace ash {
  30. // The icon which represents a notification.
  31. class NotificationOverflowImageView
  32. : public message_center::ProportionalImageView {
  33. public:
  34. NotificationOverflowImageView(const ui::ImageModel& image,
  35. const std::string& notification_id)
  36. : message_center::ProportionalImageView(gfx::Size(kIconSize, kIconSize)),
  37. notification_id_(notification_id) {
  38. SetID(kNotificationOverflowIconId);
  39. SetImage(image, gfx::Size(kIconSize, kIconSize));
  40. }
  41. NotificationOverflowImageView(const NotificationOverflowImageView&) = delete;
  42. NotificationOverflowImageView& operator=(
  43. const NotificationOverflowImageView&) = delete;
  44. ~NotificationOverflowImageView() override = default;
  45. const std::string& notification_id() const { return notification_id_; }
  46. private:
  47. std::string const notification_id_;
  48. };
  49. NotificationOverflowView::NotificationOverflowView()
  50. : separator_(AddChildView(std::make_unique<views::MenuSeparator>(
  51. ui::MenuSeparatorType::NORMAL_SEPARATOR))) {
  52. SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
  53. 0, kNotificationHorizontalPadding, kOverflowAreaBottomPadding,
  54. kNotificationHorizontalPadding)));
  55. SetBackground(views::CreateSolidBackground(SK_ColorWHITE));
  56. }
  57. NotificationOverflowView::~NotificationOverflowView() = default;
  58. void NotificationOverflowView::AddIcon(
  59. const message_center::ProportionalImageView& image_view,
  60. const std::string& notification_id) {
  61. // Insert the image at the front of the list, so that it appears on the right
  62. // side.
  63. image_views_.insert(
  64. image_views_.begin(),
  65. AddChildView(std::make_unique<NotificationOverflowImageView>(
  66. image_view.image(), notification_id)));
  67. if (image_views_.size() > kMaxOverflowIcons) {
  68. if (!overflow_icon_) {
  69. auto icon = ui::ImageModel::FromVectorIcon(views::kOptionsIcon,
  70. ui::kColorIcon, kIconSize);
  71. auto overflow_icon =
  72. std::make_unique<message_center::ProportionalImageView>(
  73. gfx::Size(kIconSize, kIconSize));
  74. overflow_icon->SetID(kOverflowIconId);
  75. overflow_icon->SetImage(icon, gfx::Size(kIconSize, kIconSize));
  76. overflow_icon_ = AddChildView(std::move(overflow_icon));
  77. }
  78. overflow_icon_->SetVisible(true);
  79. image_views_.at(kMaxOverflowIcons)->SetVisible(false);
  80. }
  81. Layout();
  82. }
  83. void NotificationOverflowView::RemoveIcon(const std::string& notification_id) {
  84. auto it = std::find_if(image_views_.begin(), image_views_.end(),
  85. [notification_id](const auto& item) {
  86. return item->notification_id() == notification_id;
  87. });
  88. if (it != image_views_.end()) {
  89. RemoveChildViewT(*it);
  90. image_views_.erase(it);
  91. MaybeRemoveOverflowIcon();
  92. Layout();
  93. }
  94. }
  95. void NotificationOverflowView::Layout() {
  96. separator_->SetBoundsRect(
  97. gfx::Rect(width(), separator_->GetPreferredSize().height()));
  98. int x = width() - GetInsets().right();
  99. const int y =
  100. separator_->GetPreferredSize().height() + kOverflowSeparatorToIconPadding;
  101. for (size_t i = 0; i < image_views_.size() && i <= kMaxOverflowIcons; ++i) {
  102. views::View* icon = image_views_.at(i);
  103. if (i == kMaxOverflowIcons)
  104. icon = overflow_icon_;
  105. x -= kIconLayoutSize;
  106. icon->SetBounds(x, y, kIconLayoutSize, kIconLayoutSize);
  107. x -= kInterIconPadding;
  108. }
  109. }
  110. gfx::Size NotificationOverflowView::CalculatePreferredSize() const {
  111. // This view is the last element in a MenuItemView, which means it has extra
  112. // padding on the bottom due to the corner radius of the root MenuItemView. If
  113. // the corner radius changes, |kOverflowSeparatorToIconPadding| must be
  114. // modified to vertically center the overflow icons.
  115. return gfx::Size(views::MenuConfig::instance().touchable_menu_min_width,
  116. separator_->GetPreferredSize().height() +
  117. kOverflowSeparatorToIconPadding + kIconLayoutSize);
  118. }
  119. void NotificationOverflowView::MaybeRemoveOverflowIcon() {
  120. if (!overflow_icon_ || image_views_.size() > kMaxOverflowIcons)
  121. return;
  122. overflow_icon_->SetVisible(false);
  123. }
  124. } // namespace ash