stacked_notification_bar.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. // Copyright 2019 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/system/message_center/stacked_notification_bar.h"
  5. #include "ash/constants/ash_features.h"
  6. #include "ash/strings/grit/ash_strings.h"
  7. #include "ash/style/ash_color_provider.h"
  8. #include "ash/style/pill_button.h"
  9. #include "ash/style/style_util.h"
  10. #include "ash/system/message_center/message_center_constants.h"
  11. #include "ash/system/message_center/message_center_style.h"
  12. #include "ash/system/tray/tray_constants.h"
  13. #include "base/bind.h"
  14. #include "ui/base/l10n/l10n_util.h"
  15. #include "ui/base/metadata/metadata_impl_macros.h"
  16. #include "ui/color/color_id.h"
  17. #include "ui/color/color_provider.h"
  18. #include "ui/compositor/layer.h"
  19. #include "ui/compositor/layer_animation_observer.h"
  20. #include "ui/compositor/layer_animation_sequence.h"
  21. #include "ui/compositor/layer_animator.h"
  22. #include "ui/compositor/scoped_layer_animation_settings.h"
  23. #include "ui/gfx/canvas.h"
  24. #include "ui/gfx/interpolated_transform.h"
  25. #include "ui/message_center/message_center.h"
  26. #include "ui/message_center/public/cpp/message_center_constants.h"
  27. #include "ui/message_center/vector_icons.h"
  28. #include "ui/views/controls/label.h"
  29. #include "ui/views/layout/box_layout.h"
  30. namespace ash {
  31. namespace {
  32. // The label button in the stacked notification bar, can be either a "Clear all"
  33. // or "See all notifications" button.
  34. class StackingBarLabelButton : public PillButton {
  35. public:
  36. METADATA_HEADER(StackingBarLabelButton);
  37. StackingBarLabelButton(PressedCallback callback,
  38. const std::u16string& text,
  39. UnifiedMessageCenterView* message_center_view)
  40. : PillButton(
  41. std::move(callback),
  42. text,
  43. PillButton::Type::kIconlessAccentFloating,
  44. /*icon=*/nullptr,
  45. kNotificationPillButtonHorizontalSpacing,
  46. /*use_light_colors=*/!features::IsNotificationsRefreshEnabled(),
  47. /*rounded_highlight_path=*/
  48. features::IsNotificationsRefreshEnabled()),
  49. message_center_view_(message_center_view) {
  50. const SkColor bg_color =
  51. features::IsNotificationsRefreshEnabled()
  52. ? gfx::kPlaceholderColor
  53. : message_center_style::kUnifiedMenuButtonColorActive;
  54. StyleUtil::SetUpInkDropForButton(this, gfx::Insets(),
  55. /*highlight_on_hover=*/true,
  56. /*highlight_on_focus=*/true, bg_color);
  57. }
  58. StackingBarLabelButton(const StackingBarLabelButton&) = delete;
  59. StackingBarLabelButton& operator=(const StackingBarLabelButton&) = delete;
  60. ~StackingBarLabelButton() override = default;
  61. // PillButton:
  62. void AboutToRequestFocusFromTabTraversal(bool reverse) override {
  63. if (message_center_view_->collapsed() && HasFocus())
  64. message_center_view_->FocusOut(reverse);
  65. }
  66. private:
  67. UnifiedMessageCenterView* message_center_view_;
  68. };
  69. BEGIN_METADATA(StackingBarLabelButton, PillButton)
  70. END_METADATA
  71. } // namespace
  72. class StackedNotificationBar::StackedNotificationBarIcon
  73. : public views::ImageView,
  74. public ui::LayerAnimationObserver {
  75. public:
  76. explicit StackedNotificationBarIcon(const std::string& id)
  77. : views::ImageView(), id_(id) {
  78. SetPaintToLayer();
  79. layer()->SetFillsBoundsOpaquely(false);
  80. }
  81. ~StackedNotificationBarIcon() override {
  82. StopObserving();
  83. if (is_animating_out())
  84. layer()->GetAnimator()->StopAnimating();
  85. }
  86. void OnThemeChanged() override {
  87. views::ImageView::OnThemeChanged();
  88. const auto* color_provider = GetColorProvider();
  89. auto* notification =
  90. message_center::MessageCenter::Get()->FindVisibleNotificationById(id_);
  91. // The notification icon could be waiting to be cleaned up after the
  92. // notification removal animation completes.
  93. if (!notification)
  94. return;
  95. SkColor accent_color;
  96. gfx::Image masked_small_icon;
  97. if (features::IsNotificationsRefreshEnabled()) {
  98. accent_color = AshColorProvider::Get()->GetContentLayerColor(
  99. AshColorProvider::ContentLayerType::kIconColorPrimary);
  100. masked_small_icon = notification->GenerateMaskedSmallIcon(
  101. kStackedNotificationIconSize, accent_color, SK_ColorTRANSPARENT,
  102. accent_color);
  103. } else {
  104. accent_color =
  105. color_provider->GetColor(ui::kColorNotificationHeaderForeground);
  106. masked_small_icon = notification->GenerateMaskedSmallIcon(
  107. kStackedNotificationIconSize, accent_color,
  108. color_provider->GetColor(ui::kColorNotificationIconBackground),
  109. color_provider->GetColor(ui::kColorNotificationIconForeground));
  110. }
  111. if (masked_small_icon.IsEmpty()) {
  112. SetImage(gfx::CreateVectorIcon(message_center::kProductIcon,
  113. kStackedNotificationIconSize,
  114. accent_color));
  115. } else {
  116. SetImage(masked_small_icon.AsImageSkia());
  117. }
  118. }
  119. void AnimateIn() {
  120. DCHECK(!is_animating_out());
  121. std::unique_ptr<ui::InterpolatedTransform> scale =
  122. std::make_unique<ui::InterpolatedScale>(
  123. gfx::Point3F(kNotificationIconAnimationScaleFactor,
  124. kNotificationIconAnimationScaleFactor, 1),
  125. gfx::Point3F(1, 1, 1));
  126. std::unique_ptr<ui::InterpolatedTransform> scale_about_pivot =
  127. std::make_unique<ui::InterpolatedTransformAboutPivot>(
  128. GetLocalBounds().CenterPoint(), std::move(scale));
  129. scale_about_pivot->SetChild(std::make_unique<ui::InterpolatedTranslation>(
  130. gfx::PointF(0, kNotificationIconAnimationLowPosition),
  131. gfx::PointF(0, kNotificationIconAnimationHighPosition)));
  132. std::unique_ptr<ui::LayerAnimationElement> scale_and_move_up =
  133. ui::LayerAnimationElement::CreateInterpolatedTransformElement(
  134. std::move(scale_about_pivot),
  135. base::Milliseconds(kNotificationIconAnimationUpDurationMs));
  136. scale_and_move_up->set_tween_type(gfx::Tween::EASE_IN);
  137. std::unique_ptr<ui::LayerAnimationElement> move_down =
  138. ui::LayerAnimationElement::CreateInterpolatedTransformElement(
  139. std::make_unique<ui::InterpolatedTranslation>(
  140. gfx::PointF(0, kNotificationIconAnimationHighPosition),
  141. gfx::PointF(0, 0)),
  142. base::Milliseconds(kNotificationIconAnimationDownDurationMs));
  143. std::unique_ptr<ui::LayerAnimationSequence> sequence =
  144. std::make_unique<ui::LayerAnimationSequence>();
  145. sequence->AddElement(std::move(scale_and_move_up));
  146. sequence->AddElement(std::move(move_down));
  147. layer()->GetAnimator()->StartAnimation(sequence.release());
  148. }
  149. using AnimationCompleteCallback = base::OnceCallback<void(views::View*)>;
  150. void AnimateOut(AnimationCompleteCallback animation_complete_callback) {
  151. DCHECK(animation_complete_callback_.is_null());
  152. animation_complete_callback_ = std::move(animation_complete_callback);
  153. layer()->GetAnimator()->StopAnimating();
  154. std::unique_ptr<ui::InterpolatedTransform> scale =
  155. std::make_unique<ui::InterpolatedScale>(
  156. gfx::Point3F(1, 1, 1),
  157. gfx::Point3F(kNotificationIconAnimationScaleFactor,
  158. kNotificationIconAnimationScaleFactor, 1));
  159. std::unique_ptr<ui::InterpolatedTransform> scale_about_pivot =
  160. std::make_unique<ui::InterpolatedTransformAboutPivot>(
  161. gfx::Point(bounds().width() * 0.5, bounds().height() * 0.5),
  162. std::move(scale));
  163. scale_about_pivot->SetChild(std::make_unique<ui::InterpolatedTranslation>(
  164. gfx::PointF(0, 0),
  165. gfx::PointF(0, kNotificationIconAnimationLowPosition)));
  166. std::unique_ptr<ui::LayerAnimationElement> scale_and_move_down =
  167. ui::LayerAnimationElement::CreateInterpolatedTransformElement(
  168. std::move(scale_about_pivot),
  169. base::Milliseconds(kNotificationIconAnimationOutDurationMs));
  170. scale_and_move_down->set_tween_type(gfx::Tween::EASE_IN);
  171. std::unique_ptr<ui::LayerAnimationSequence> sequence =
  172. std::make_unique<ui::LayerAnimationSequence>();
  173. sequence->AddElement(std::move(scale_and_move_down));
  174. sequence->AddObserver(this);
  175. set_animating_out(true);
  176. layer()->GetAnimator()->StartAnimation(sequence.release());
  177. // Note |this| may be deleted after this point.
  178. }
  179. // ui::LayerAnimationObserver:
  180. void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {
  181. set_animating_out(false);
  182. std::move(animation_complete_callback_).Run(this);
  183. // Note |this| is deleted after this point.
  184. }
  185. void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {}
  186. void OnLayerAnimationScheduled(
  187. ui::LayerAnimationSequence* sequence) override {}
  188. const std::string& id() const { return id_; }
  189. bool is_animating_out() const { return animating_out_; }
  190. void set_animating_out(bool animating_out) { animating_out_ = animating_out; }
  191. private:
  192. std::string id_;
  193. bool animating_out_ = false;
  194. // Used to notify the parent of animation completion. This is deleted after
  195. // the callback is run.
  196. // Registered in `AnimateOut()`.
  197. AnimationCompleteCallback animation_complete_callback_;
  198. };
  199. StackedNotificationBar::StackedNotificationBar(
  200. UnifiedMessageCenterView* message_center_view)
  201. : message_center_view_(message_center_view),
  202. notification_icons_container_(
  203. AddChildView(std::make_unique<views::View>())),
  204. count_label_(AddChildView(std::make_unique<views::Label>())),
  205. spacer_(AddChildView(std::make_unique<views::View>())),
  206. clear_all_button_(AddChildView(std::make_unique<StackingBarLabelButton>(
  207. base::BindRepeating(&UnifiedMessageCenterView::ClearAllNotifications,
  208. base::Unretained(message_center_view_)),
  209. l10n_util::GetStringUTF16(
  210. IDS_ASH_MESSAGE_CENTER_CLEAR_ALL_BUTTON_LABEL),
  211. message_center_view))),
  212. expand_all_button_(AddChildView(std::make_unique<StackingBarLabelButton>(
  213. base::BindRepeating(&UnifiedMessageCenterView::ExpandMessageCenter,
  214. base::Unretained(message_center_view_)),
  215. l10n_util::GetStringUTF16(
  216. IDS_ASH_MESSAGE_CENTER_EXPAND_ALL_NOTIFICATIONS_BUTTON_LABEL),
  217. message_center_view))),
  218. layout_manager_(SetLayoutManager(std::make_unique<views::BoxLayout>(
  219. views::BoxLayout::Orientation::kHorizontal,
  220. features::IsNotificationsRefreshEnabled() ? kNotificationBarPadding
  221. : gfx::Insets()))) {
  222. SetVisible(false);
  223. layout_manager_->set_cross_axis_alignment(
  224. views::BoxLayout::CrossAxisAlignment::kStretch);
  225. notification_icons_container_->SetLayoutManager(
  226. std::make_unique<views::BoxLayout>(
  227. views::BoxLayout::Orientation::kHorizontal,
  228. kStackedNotificationIconsContainerPadding,
  229. kStackedNotificationBarIconSpacing));
  230. message_center::MessageCenter::Get()->AddObserver(this);
  231. SkColor label_color =
  232. features::IsNotificationsRefreshEnabled()
  233. ? AshColorProvider::Get()->GetContentLayerColor(
  234. AshColorProvider::ContentLayerType::kIconColorPrimary)
  235. : message_center_style::kCountLabelColor;
  236. count_label_->SetEnabledColor(label_color);
  237. count_label_->SetFontList(views::Label::GetDefaultFontList().Derive(
  238. 1, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
  239. layout_manager_->SetFlexForView(spacer_, 1);
  240. clear_all_button_->SetTooltipText(l10n_util::GetStringUTF16(
  241. IDS_ASH_MESSAGE_CENTER_CLEAR_ALL_BUTTON_TOOLTIP));
  242. expand_all_button_->SetVisible(false);
  243. if (!features::IsNotificationsRefreshEnabled())
  244. SetPaintToLayer();
  245. }
  246. StackedNotificationBar::~StackedNotificationBar() {
  247. // The MessageCenter may be destroyed already during shutdown. See
  248. // crbug.com/946153.
  249. if (message_center::MessageCenter::Get())
  250. message_center::MessageCenter::Get()->RemoveObserver(this);
  251. }
  252. bool StackedNotificationBar::Update(
  253. int total_notification_count,
  254. int pinned_notification_count,
  255. std::vector<message_center::Notification*> stacked_notifications) {
  256. int stacked_notification_count = stacked_notifications.size();
  257. if (total_notification_count == total_notification_count_ &&
  258. pinned_notification_count == pinned_notification_count_ &&
  259. stacked_notification_count == stacked_notification_count_) {
  260. return false;
  261. }
  262. total_notification_count_ = total_notification_count;
  263. pinned_notification_count_ = pinned_notification_count;
  264. UpdateStackedNotifications(stacked_notifications);
  265. UpdateVisibility();
  266. const int unpinned_count =
  267. total_notification_count_ - pinned_notification_count_;
  268. auto tooltip = l10n_util::GetStringFUTF16Int(
  269. IDS_ASH_MESSAGE_CENTER_STACKING_BAR_CLEAR_ALL_BUTTON_TOOLTIP,
  270. unpinned_count);
  271. clear_all_button_->SetTooltipText(tooltip);
  272. clear_all_button_->SetAccessibleName(tooltip);
  273. clear_all_button_->SetState(unpinned_count == 0
  274. ? views::Button::STATE_DISABLED
  275. : views::Button::STATE_NORMAL);
  276. return true;
  277. }
  278. void StackedNotificationBar::SetAnimationState(
  279. UnifiedMessageCenterAnimationState animation_state) {
  280. animation_state_ = animation_state;
  281. UpdateVisibility();
  282. }
  283. void StackedNotificationBar::SetCollapsed() {
  284. if (features::IsNotificationsRefreshEnabled())
  285. layout_manager_->set_inside_border_insets(gfx::Insets());
  286. clear_all_button_->SetVisible(false);
  287. expand_all_button_->SetVisible(true);
  288. UpdateVisibility();
  289. }
  290. void StackedNotificationBar::SetExpanded() {
  291. if (features::IsNotificationsRefreshEnabled())
  292. layout_manager_->set_inside_border_insets(kNotificationBarPadding);
  293. clear_all_button_->SetVisible(true);
  294. expand_all_button_->SetVisible(false);
  295. }
  296. void StackedNotificationBar::AddNotificationIcon(
  297. message_center::Notification* notification,
  298. bool at_front) {
  299. if (at_front)
  300. notification_icons_container_->AddChildViewAt(
  301. std::make_unique<StackedNotificationBarIcon>(notification->id()), 0);
  302. else
  303. notification_icons_container_->AddChildView(
  304. std::make_unique<StackedNotificationBarIcon>(notification->id()));
  305. }
  306. void StackedNotificationBar::OnIconAnimatedOut(std::string notification_id,
  307. views::View* icon) {
  308. delete icon;
  309. auto* notification =
  310. message_center::MessageCenter::Get()->FindVisibleNotificationById(
  311. notification_id);
  312. // This is only called when icons animate out, so never add icons to the
  313. // front.
  314. if (notification)
  315. AddNotificationIcon(notification, /*at_front=*/false);
  316. Layout();
  317. }
  318. StackedNotificationBar::StackedNotificationBarIcon*
  319. StackedNotificationBar::GetFrontIcon(bool animating_out) {
  320. const auto i = std::find_if(
  321. notification_icons_container_->children().cbegin(),
  322. notification_icons_container_->children().cend(), [&](const auto* v) {
  323. return animating_out ==
  324. static_cast<const StackedNotificationBarIcon*>(v)
  325. ->is_animating_out();
  326. });
  327. return (i == notification_icons_container_->children().cend()
  328. ? nullptr
  329. : static_cast<StackedNotificationBarIcon*>(*i));
  330. }
  331. const StackedNotificationBar::StackedNotificationBarIcon*
  332. StackedNotificationBar::GetIconFromId(const std::string& id) const {
  333. for (auto* v : notification_icons_container_->children()) {
  334. const StackedNotificationBarIcon* icon =
  335. static_cast<const StackedNotificationBarIcon*>(v);
  336. if (icon->id() == id)
  337. return icon;
  338. }
  339. return nullptr;
  340. }
  341. void StackedNotificationBar::ShiftIconsLeft(
  342. std::vector<message_center::Notification*> stacked_notifications) {
  343. auto* front_animating_out_icon = GetFrontIcon(/*animating_out=*/true);
  344. bool is_already_animating_a_left_shift = front_animating_out_icon != nullptr;
  345. // If we need to animate a second icon, the scroll is faster than the icon can
  346. // animate out (this is possible with a very fast scroll), so immediately
  347. // finish that animation before starting a new one.
  348. if (is_already_animating_a_left_shift) {
  349. front_animating_out_icon->layer()->GetAnimator()->StopAnimating();
  350. // `front_animating_out_icon` is now deleted, and StackedNotificationBar has
  351. // been reloaded with another icon in the back.
  352. }
  353. int stacked_notification_count = stacked_notifications.size();
  354. int removed_icons_count =
  355. std::min(stacked_notification_count_ - stacked_notification_count,
  356. kStackedNotificationBarMaxIcons);
  357. stacked_notification_count_ = stacked_notification_count;
  358. // Remove required number of icons from the front.
  359. // Only animate if we're removing one icon.
  360. int backfill_start = kStackedNotificationBarMaxIcons - removed_icons_count;
  361. int backfill_end =
  362. std::min(kStackedNotificationBarMaxIcons, stacked_notification_count);
  363. const bool will_animate = removed_icons_count == 1;
  364. if (will_animate) {
  365. auto* icon = GetFrontIcon(/*animating_out=*/false);
  366. if (icon) {
  367. // If there are notifications to backfill, do not add the
  368. // icon until the animation completes, this avoids a jumping overflow
  369. // label/icons and having more than 3 icons in the stack.
  370. message_center::Notification* next_notification =
  371. backfill_start < backfill_end ? stacked_notifications[backfill_start]
  372. : nullptr;
  373. icon->AnimateOut(base::BindOnce(
  374. &StackedNotificationBar::OnIconAnimatedOut,
  375. weak_ptr_factory_.GetWeakPtr(),
  376. next_notification ? next_notification->id() : std::string()));
  377. }
  378. return;
  379. }
  380. // No animation.
  381. for (int i = 0; i < removed_icons_count; i++) {
  382. auto* icon = GetFrontIcon(/*animating_out=*/false);
  383. if (icon) {
  384. delete icon;
  385. }
  386. }
  387. for (int i = backfill_start; i < backfill_end; i++)
  388. AddNotificationIcon(stacked_notifications[i], false /*at_front*/);
  389. }
  390. void StackedNotificationBar::ShiftIconsRight(
  391. std::vector<message_center::Notification*> stacked_notifications) {
  392. int new_stacked_notification_count = stacked_notifications.size();
  393. while (stacked_notification_count_ < new_stacked_notification_count) {
  394. // Remove icon from the back in case there is an overflow.
  395. if (stacked_notification_count_ >= kStackedNotificationBarMaxIcons) {
  396. delete notification_icons_container_->children().back();
  397. }
  398. // Add icon to the front.
  399. AddNotificationIcon(stacked_notifications[new_stacked_notification_count -
  400. stacked_notification_count_ - 1],
  401. true /*at_front*/);
  402. ++stacked_notification_count_;
  403. }
  404. // Animate in the first stacked notification icon.
  405. auto* icon = GetFrontIcon(/*animating_out=*/false);
  406. if (icon)
  407. icon->AnimateIn();
  408. }
  409. void StackedNotificationBar::UpdateStackedNotifications(
  410. std::vector<message_center::Notification*> stacked_notifications) {
  411. int stacked_notification_count = stacked_notifications.size();
  412. int notification_overflow_count = 0;
  413. if (stacked_notification_count_ > stacked_notification_count)
  414. ShiftIconsLeft(stacked_notifications);
  415. else if (stacked_notification_count_ < stacked_notification_count)
  416. ShiftIconsRight(stacked_notifications);
  417. notification_overflow_count = std::max(
  418. stacked_notification_count_ - kStackedNotificationBarMaxIcons, 0);
  419. // Update overflow count label
  420. if (notification_overflow_count > 0) {
  421. count_label_->SetText(l10n_util::GetStringFUTF16Int(
  422. IDS_ASH_MESSAGE_CENTER_HIDDEN_NOTIFICATION_COUNT_LABEL,
  423. notification_overflow_count));
  424. count_label_->SetVisible(true);
  425. } else {
  426. count_label_->SetVisible(false);
  427. }
  428. }
  429. void StackedNotificationBar::OnPaint(gfx::Canvas* canvas) {
  430. // We don't need the custom border below in the new message center UI, since
  431. // the clear all button does not interfere with the border anymore. Also, the
  432. // message center bubble will have a highlight border that covers this view.
  433. if (features::IsNotificationsRefreshEnabled())
  434. return;
  435. cc::PaintFlags flags;
  436. flags.setColor(message_center_style::kNotificationBackgroundColor);
  437. flags.setStyle(cc::PaintFlags::kFill_Style);
  438. flags.setAntiAlias(true);
  439. gfx::Rect bounds = GetLocalBounds();
  440. canvas->DrawRect(bounds, flags);
  441. // We draw a border here than use a views::Border so the ink drop highlight
  442. // of the clear all button overlays the border.
  443. if (clear_all_button_->GetVisible()) {
  444. canvas->DrawSharpLine(
  445. gfx::PointF(bounds.bottom_left() - gfx::Vector2d(0, 1)),
  446. gfx::PointF(bounds.bottom_right() - gfx::Vector2d(0, 1)),
  447. message_center_style::kSeperatorColor);
  448. }
  449. }
  450. const char* StackedNotificationBar::GetClassName() const {
  451. return "StackedNotificationBar";
  452. }
  453. void StackedNotificationBar::UpdateVisibility() {
  454. // In the refreshed message center view the notification bar is always
  455. // visible.
  456. if (features::IsNotificationsRefreshEnabled()) {
  457. if (!GetVisible())
  458. SetVisible(true);
  459. return;
  460. }
  461. int unpinned_count = total_notification_count_ - pinned_notification_count_;
  462. // In expanded state, clear all button should be visible when (rule is subject
  463. // to change):
  464. // 1. There are more than one notification.
  465. // 2. There is at least one unpinned notification
  466. const bool show_clear_all =
  467. total_notification_count_ > 1 && unpinned_count >= 1;
  468. if (!expand_all_button_->GetVisible())
  469. clear_all_button_->SetVisible(show_clear_all);
  470. switch (animation_state_) {
  471. case UnifiedMessageCenterAnimationState::IDLE:
  472. SetVisible(
  473. (stacked_notification_count_ && total_notification_count_ > 1) ||
  474. show_clear_all || expand_all_button_->GetVisible());
  475. break;
  476. case UnifiedMessageCenterAnimationState::HIDE_STACKING_BAR:
  477. SetVisible(true);
  478. break;
  479. case UnifiedMessageCenterAnimationState::COLLAPSE:
  480. SetVisible(
  481. (stacked_notification_count_ && total_notification_count_ > 1) ||
  482. show_clear_all || expand_all_button_->GetVisible());
  483. break;
  484. }
  485. }
  486. void StackedNotificationBar::OnNotificationAdded(const std::string& id) {
  487. // Reset the stacked icons bar if a notification is added since we don't
  488. // know the position where it may have been added.
  489. notification_icons_container_->RemoveAllChildViews();
  490. stacked_notification_count_ = 0;
  491. UpdateStackedNotifications(message_center_view_->GetStackedNotifications());
  492. }
  493. void StackedNotificationBar::OnNotificationRemoved(const std::string& id,
  494. bool by_user) {
  495. const StackedNotificationBarIcon* icon = GetIconFromId(id);
  496. if (icon && !icon->is_animating_out()) {
  497. delete icon;
  498. stacked_notification_count_--;
  499. }
  500. }
  501. void StackedNotificationBar::OnNotificationUpdated(const std::string& id) {}
  502. } // namespace ash