notification_menu_view.cc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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_menu_view.h"
  5. #include "ash/app_menu/notification_item_view.h"
  6. #include "ash/app_menu/notification_menu_header_view.h"
  7. #include "ash/app_menu/notification_overflow_view.h"
  8. #include "ash/public/cpp/app_menu_constants.h"
  9. #include "ui/base/models/menu_separator_types.h"
  10. #include "ui/gfx/geometry/point.h"
  11. #include "ui/gfx/geometry/rect.h"
  12. #include "ui/gfx/geometry/size.h"
  13. #include "ui/message_center/public/cpp/notification.h"
  14. #include "ui/message_center/views/proportional_image_view.h"
  15. #include "ui/views/controls/menu/menu_config.h"
  16. #include "ui/views/controls/menu/menu_item_view.h"
  17. #include "ui/views/controls/menu/menu_separator.h"
  18. namespace ash {
  19. NotificationMenuView::NotificationMenuView(
  20. Delegate* notification_item_view_delegate,
  21. views::SlideOutControllerDelegate* slide_out_controller_delegate,
  22. const std::string& app_id)
  23. : app_id_(app_id),
  24. notification_item_view_delegate_(notification_item_view_delegate),
  25. slide_out_controller_delegate_(slide_out_controller_delegate),
  26. double_separator_(AddChildView(std::make_unique<views::MenuSeparator>(
  27. ui::MenuSeparatorType::DOUBLE_SEPARATOR))),
  28. header_view_(
  29. AddChildView(std::make_unique<NotificationMenuHeaderView>())) {
  30. DCHECK(notification_item_view_delegate_);
  31. DCHECK(slide_out_controller_delegate_);
  32. DCHECK(!app_id_.empty())
  33. << "Only context menus for applications can show notifications.";
  34. }
  35. NotificationMenuView::~NotificationMenuView() = default;
  36. gfx::Size NotificationMenuView::CalculatePreferredSize() const {
  37. return gfx::Size(
  38. views::MenuConfig::instance().touchable_menu_min_width,
  39. double_separator_->GetPreferredSize().height() +
  40. header_view_->GetPreferredSize().height() +
  41. kNotificationItemViewHeight +
  42. (overflow_view_ ? overflow_view_->GetPreferredSize().height() : 0));
  43. }
  44. void NotificationMenuView::Layout() {
  45. int y = 0;
  46. double_separator_->SetBoundsRect(gfx::Rect(
  47. gfx::Point(0, y),
  48. gfx::Size(views::MenuConfig::instance().touchable_menu_min_width,
  49. double_separator_->GetPreferredSize().height())));
  50. y += double_separator_->GetPreferredSize().height();
  51. header_view_->SetBoundsRect(
  52. gfx::Rect(gfx::Point(0, y), header_view_->GetPreferredSize()));
  53. y += header_view_->GetPreferredSize().height();
  54. auto* item = GetDisplayedNotificationItemView();
  55. if (item) {
  56. item->SetBoundsRect(gfx::Rect(gfx::Point(0, y), item->GetPreferredSize()));
  57. y = item->bounds().bottom();
  58. }
  59. if (overflow_view_) {
  60. overflow_view_->SetBoundsRect(
  61. gfx::Rect(gfx::Point(0, y), overflow_view_->GetPreferredSize()));
  62. }
  63. }
  64. bool NotificationMenuView::IsEmpty() const {
  65. return notification_item_views_.empty();
  66. }
  67. void NotificationMenuView::AddNotificationItemView(
  68. const message_center::Notification& notification) {
  69. auto* old_displayed_item = GetDisplayedNotificationItemView();
  70. auto notification_view = std::make_unique<NotificationItemView>(
  71. notification_item_view_delegate_, slide_out_controller_delegate_,
  72. notification.title(), notification.message(), notification.icon(),
  73. notification.id());
  74. notification_item_views_.push_front(
  75. AddChildView(std::move(notification_view)));
  76. header_view_->UpdateCounter(notification_item_views_.size());
  77. if (!old_displayed_item)
  78. return;
  79. // Push |old_displayed_notification_item_view| to |overflow_view_|.
  80. old_displayed_item->SetVisible(false);
  81. const bool overflow_view_created = !overflow_view_;
  82. if (!overflow_view_)
  83. overflow_view_ = AddChildView(std::make_unique<NotificationOverflowView>());
  84. overflow_view_->AddIcon(old_displayed_item->proportional_image_view(),
  85. old_displayed_item->notification_id());
  86. if (overflow_view_created) {
  87. PreferredSizeChanged();
  88. // OnOverflowAddedOrRemoved must be called after PreferredSizeChange to
  89. // ensure that enough room is allocated for the overflow view.
  90. notification_item_view_delegate_->OnOverflowAddedOrRemoved();
  91. }
  92. Layout();
  93. }
  94. void NotificationMenuView::UpdateNotificationItemView(
  95. const message_center::Notification& notification) {
  96. // Find the view which corresponds to |notification|.
  97. const auto i = NotificationIterForId(notification.id());
  98. if (i == notification_item_views_.end())
  99. return;
  100. (*i)->UpdateContents(notification.title(), notification.message(),
  101. notification.icon());
  102. }
  103. void NotificationMenuView::OnNotificationRemoved(
  104. const std::string& notification_id) {
  105. // Find the view which corresponds to |notification_id|.
  106. const auto i = NotificationIterForId(notification_id);
  107. if (i == notification_item_views_.end())
  108. return;
  109. const bool removed_displayed_notification =
  110. *i == GetDisplayedNotificationItemView();
  111. RemoveChildViewT(*i);
  112. notification_item_views_.erase(i);
  113. header_view_->UpdateCounter(notification_item_views_.size());
  114. if (removed_displayed_notification) {
  115. // Display the next notification.
  116. auto* item = GetDisplayedNotificationItemView();
  117. if (item) {
  118. item->SetVisible(true);
  119. if (overflow_view_)
  120. overflow_view_->RemoveIcon(item->notification_id());
  121. }
  122. } else if (overflow_view_) {
  123. overflow_view_->RemoveIcon(notification_id);
  124. }
  125. if (overflow_view_ && overflow_view_->is_empty()) {
  126. // Remove and delete |overflow_view_|.
  127. RemoveChildViewT(overflow_view_);
  128. overflow_view_ = nullptr;
  129. PreferredSizeChanged();
  130. notification_item_view_delegate_->OnOverflowAddedOrRemoved();
  131. }
  132. }
  133. ui::Layer* NotificationMenuView::GetSlideOutLayer() {
  134. auto* item = GetDisplayedNotificationItemView();
  135. return item ? item->layer() : nullptr;
  136. }
  137. const NotificationItemView*
  138. NotificationMenuView::GetDisplayedNotificationItemView() const {
  139. return notification_item_views_.empty() ? nullptr
  140. : notification_item_views_.front();
  141. }
  142. const std::string& NotificationMenuView::GetDisplayedNotificationID() const {
  143. DCHECK(!notification_item_views_.empty());
  144. return GetDisplayedNotificationItemView()->notification_id();
  145. }
  146. NotificationMenuView::NotificationItemViews::iterator
  147. NotificationMenuView::NotificationIterForId(const std::string& id) {
  148. return std::find_if(
  149. notification_item_views_.begin(), notification_item_views_.end(),
  150. [id](const auto& item) { return item->notification_id() == id; });
  151. }
  152. } // namespace ash