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 
5 #include "chrome/browser/ui/views/global_media_controls/media_notification_list_view.h"
6 
7 #include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h"
8 #include "chrome/browser/ui/views/global_media_controls/overlay_media_notification_view.h"
9 #include "ui/native_theme/native_theme.h"
10 #include "ui/views/border.h"
11 #include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
12 #include "ui/views/layout/box_layout.h"
13 
14 namespace {
15 
16 constexpr int kMediaListMaxHeight = 478;
17 
18 // Thickness of separator border.
19 constexpr int kMediaListSeparatorThickness = 2;
20 
CreateMediaListSeparatorBorder(SkColor color,int thickness)21 std::unique_ptr<views::Border> CreateMediaListSeparatorBorder(SkColor color,
22                                                               int thickness) {
23   return views::CreateSolidSidedBorder(/*top=*/thickness,
24                                        /*left=*/0,
25                                        /*bottom=*/0,
26                                        /*right=*/0, color);
27 }
28 
29 }  // anonymous namespace
30 
SeparatorStyle(SkColor separator_color,int separator_thickness)31 MediaNotificationListView::SeparatorStyle::SeparatorStyle(
32     SkColor separator_color,
33     int separator_thickness)
34     : separator_color(separator_color),
35       separator_thickness(separator_thickness) {}
36 
MediaNotificationListView()37 MediaNotificationListView::MediaNotificationListView()
38     : MediaNotificationListView(base::nullopt) {}
39 
MediaNotificationListView(const base::Optional<SeparatorStyle> & separator_style)40 MediaNotificationListView::MediaNotificationListView(
41     const base::Optional<SeparatorStyle>& separator_style)
42     : separator_style_(separator_style) {
43   SetBackgroundColor(base::nullopt);
44   SetContents(std::make_unique<views::View>());
45   contents()->SetLayoutManager(std::make_unique<views::BoxLayout>(
46       views::BoxLayout::Orientation::kVertical));
47   ClipHeightTo(0, kMediaListMaxHeight);
48 
49   SetVerticalScrollBar(
50       std::make_unique<views::OverlayScrollBar>(/*horizontal=*/false));
51   SetHorizontalScrollBar(
52       std::make_unique<views::OverlayScrollBar>(/*horizontal=*/true));
53 }
54 
55 MediaNotificationListView::~MediaNotificationListView() = default;
56 
ShowNotification(const std::string & id,std::unique_ptr<MediaNotificationContainerImplView> notification)57 void MediaNotificationListView::ShowNotification(
58     const std::string& id,
59     std::unique_ptr<MediaNotificationContainerImplView> notification) {
60   DCHECK(!base::Contains(notifications_, id));
61   DCHECK_NE(nullptr, notification.get());
62 
63   // If this isn't the first notification, then create a top-sided separator
64   // border.
65   if (!notifications_.empty()) {
66     if (separator_style_.has_value()) {
67       notification->SetBorder(CreateMediaListSeparatorBorder(
68           separator_style_->separator_color,
69           separator_style_->separator_thickness));
70     } else {
71       notification->SetBorder(CreateMediaListSeparatorBorder(
72           GetNativeTheme()->GetSystemColor(
73               ui::NativeTheme::kColorId_MenuSeparatorColor),
74           kMediaListSeparatorThickness));
75     }
76   }
77 
78   notifications_[id] = contents()->AddChildView(std::move(notification));
79 
80   contents()->InvalidateLayout();
81   PreferredSizeChanged();
82 }
83 
HideNotification(const std::string & id)84 void MediaNotificationListView::HideNotification(const std::string& id) {
85   RemoveNotification(id);
86 }
87 
PopOut(const std::string & id,gfx::Rect bounds)88 std::unique_ptr<OverlayMediaNotification> MediaNotificationListView::PopOut(
89     const std::string& id,
90     gfx::Rect bounds) {
91   std::unique_ptr<MediaNotificationContainerImplView> notification =
92       RemoveNotification(id);
93   if (!notification)
94     return nullptr;
95 
96   return std::make_unique<OverlayMediaNotificationView>(
97       id, std::move(notification), bounds, nullptr);
98 }
99 
100 std::unique_ptr<MediaNotificationContainerImplView>
RemoveNotification(const std::string & id)101 MediaNotificationListView::RemoveNotification(const std::string& id) {
102   if (!base::Contains(notifications_, id))
103     return nullptr;
104 
105   // If we're removing the topmost notification and there are others, then we
106   // need to remove the top-sided separator border from the new topmost
107   // notification.
108   if (contents()->children().size() > 1 &&
109       contents()->children().at(0) == notifications_[id]) {
110     contents()->children().at(1)->SetBorder(nullptr);
111   }
112 
113   // Remove the notification. Note that since |RemoveChildView()| does not
114   // delete the notification, we now have ownership.
115   contents()->RemoveChildView(notifications_[id]);
116   std::unique_ptr<MediaNotificationContainerImplView> notification(
117       notifications_[id]);
118   notifications_.erase(id);
119 
120   contents()->InvalidateLayout();
121   PreferredSizeChanged();
122 
123   return notification;
124 }
125