1 // Copyright (c) 2012 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.
5 #include "ui/message_center/message_center_bubble.h"
7 #include "grit/ui_strings.h"
8 #include "ui/base/l10n/l10n_util.h"
9 #include "ui/base/resource/resource_bundle.h"
10 #include "ui/gfx/size.h"
11 #include "ui/message_center/message_view.h"
12 #include "ui/message_center/message_view_factory.h"
13 #include "ui/views/controls/button/text_button.h"
14 #include "ui/views/controls/label.h"
15 #include "ui/views/controls/scroll_view.h"
16 #include "ui/views/layout/box_layout.h"
17 #include "ui/views/layout/grid_layout.h"
18 #include "ui/views/painter.h"
19 #include "ui/views/view.h"
20 #include "ui/views/widget/widget.h"
22 namespace message_center
{
26 const int kMessageBubbleBaseMinHeight
= 80;
27 const int kMessageBubbleBaseMaxHeight
= 400;
28 const SkColor kBorderDarkColor
= SkColorSetRGB(0xaa, 0xaa, 0xaa);
30 // The view for the buttons at the bottom of the web notification tray.
31 class WebNotificationButtonView
: public views::View
,
32 public views::ButtonListener
{
34 explicit WebNotificationButtonView(NotificationList::Delegate
* list_delegate
)
35 : list_delegate_(list_delegate
),
36 close_all_button_(NULL
) {
37 set_background(views::Background::CreateBackgroundPainter(
39 views::Painter::CreateVerticalGradient(
40 MessageBubbleBase::kHeaderBackgroundColorLight
,
41 MessageBubbleBase::kHeaderBackgroundColorDark
)));
42 set_border(views::Border::CreateSolidSidedBorder(
43 2, 0, 0, 0, kBorderDarkColor
));
45 views::GridLayout
* layout
= new views::GridLayout(this);
46 SetLayoutManager(layout
);
47 views::ColumnSet
* columns
= layout
->AddColumnSet(0);
48 columns
->AddPaddingColumn(100, 0);
49 columns
->AddColumn(views::GridLayout::TRAILING
, views::GridLayout::CENTER
,
50 0, /* resize percent */
51 views::GridLayout::USE_PREF
, 0, 0);
52 columns
->AddPaddingColumn(0, 4);
54 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
55 close_all_button_
= new views::TextButton(
56 this, rb
.GetLocalizedString(IDS_MESSAGE_CENTER_CLEAR_ALL
));
57 close_all_button_
->set_alignment(views::TextButton::ALIGN_CENTER
);
58 close_all_button_
->set_focusable(true);
59 close_all_button_
->set_request_focus_on_press(false);
61 layout
->AddPaddingRow(0, 4);
62 layout
->StartRow(0, 0);
63 layout
->AddView(close_all_button_
);
66 virtual ~WebNotificationButtonView() {}
68 void SetCloseAllVisible(bool visible
) {
69 close_all_button_
->SetVisible(visible
);
72 // Overridden from ButtonListener.
73 virtual void ButtonPressed(views::Button
* sender
,
74 const ui::Event
& event
) OVERRIDE
{
75 if (sender
== close_all_button_
)
76 list_delegate_
->SendRemoveAllNotifications();
80 NotificationList::Delegate
* list_delegate_
;
81 views::TextButton
* close_all_button_
;
83 DISALLOW_COPY_AND_ASSIGN(WebNotificationButtonView
);
86 // A custom scroll-view that has a specified size.
87 class FixedSizedScrollView
: public views::ScrollView
{
89 FixedSizedScrollView() {
91 set_notify_enter_exit_on_child(true);
94 virtual ~FixedSizedScrollView() {}
96 void SetFixedSize(const gfx::Size
& size
) {
97 if (fixed_size_
== size
)
100 PreferredSizeChanged();
103 // views::View overrides.
104 virtual gfx::Size
GetPreferredSize() OVERRIDE
{
105 gfx::Size size
= fixed_size_
.IsEmpty() ?
106 contents()->GetPreferredSize() : fixed_size_
;
107 gfx::Insets insets
= GetInsets();
108 size
.Enlarge(insets
.width(), insets
.height());
112 virtual void Layout() OVERRIDE
{
113 gfx::Rect bounds
= gfx::Rect(contents()->GetPreferredSize());
114 bounds
.set_width(std::max(0, width() - GetScrollBarWidth()));
115 contents()->SetBoundsRect(bounds
);
117 views::ScrollView::Layout();
118 if (!vertical_scroll_bar()->visible()) {
119 gfx::Rect bounds
= contents()->bounds();
120 bounds
.set_width(bounds
.width() + GetScrollBarWidth());
121 contents()->SetBoundsRect(bounds
);
125 virtual void OnBoundsChanged(const gfx::Rect
& previous_bounds
) OVERRIDE
{
126 gfx::Rect bounds
= gfx::Rect(contents()->GetPreferredSize());
127 bounds
.set_width(std::max(0, width() - GetScrollBarWidth()));
128 contents()->SetBoundsRect(bounds
);
132 gfx::Size fixed_size_
;
134 DISALLOW_COPY_AND_ASSIGN(FixedSizedScrollView
);
137 // Container for the messages.
138 class ScrollContentView
: public views::View
{
140 ScrollContentView() {
141 views::BoxLayout
* layout
=
142 new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 1);
143 layout
->set_spread_blank_space(true);
144 SetLayoutManager(layout
);
147 virtual ~ScrollContentView() {
150 virtual gfx::Size
GetPreferredSize() OVERRIDE
{
151 if (!preferred_size_
.IsEmpty())
152 return preferred_size_
;
153 return views::View::GetPreferredSize();
156 void set_preferred_size(const gfx::Size
& size
) { preferred_size_
= size
; }
159 gfx::Size preferred_size_
;
160 DISALLOW_COPY_AND_ASSIGN(ScrollContentView
);
165 // Message Center contents.
166 class MessageCenterContentsView
: public views::View
{
168 explicit MessageCenterContentsView(NotificationList::Delegate
* list_delegate
)
169 : list_delegate_(list_delegate
) {
171 new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 1));
173 scroll_content_
= new ScrollContentView
;
174 scroller_
= new FixedSizedScrollView
;
175 scroller_
->SetContents(scroll_content_
);
176 AddChildView(scroller_
);
178 scroller_
->SetPaintToLayer(true);
179 scroller_
->SetFillsBoundsOpaquely(false);
180 scroller_
->layer()->SetMasksToBounds(true);
182 button_view_
= new WebNotificationButtonView(list_delegate
);
183 AddChildView(button_view_
);
186 void FocusContents() {
187 scroller_
->RequestFocus();
190 void Update(const NotificationList::Notifications
& notifications
) {
191 scroll_content_
->RemoveAllChildViews(true);
192 scroll_content_
->set_preferred_size(gfx::Size());
193 size_t num_children
= 0;
194 for (NotificationList::Notifications::const_iterator iter
=
195 notifications
.begin(); iter
!= notifications
.end(); ++iter
) {
197 MessageViewFactory::ViewForNotification(*iter
, list_delegate_
);
198 view
->set_scroller(scroller_
);
200 scroll_content_
->AddChildView(view
);
201 if (++num_children
>=
202 NotificationList::kMaxVisibleMessageCenterNotifications
) {
206 if (num_children
== 0) {
207 views::Label
* label
= new views::Label(l10n_util::GetStringUTF16(
208 IDS_MESSAGE_CENTER_NO_MESSAGES
));
209 label
->SetFont(label
->font().DeriveFont(1));
210 label
->SetEnabledColor(SK_ColorGRAY
);
211 scroll_content_
->AddChildView(label
);
212 button_view_
->SetCloseAllVisible(false);
214 button_view_
->SetCloseAllVisible(true);
219 GetWidget()->GetRootView()->SchedulePaint();
222 size_t NumMessageViews() const {
223 return scroll_content_
->child_count();
227 void SizeScrollContent() {
228 gfx::Size scroll_size
= scroll_content_
->GetPreferredSize();
229 const int button_height
= button_view_
->GetPreferredSize().height();
230 const int min_height
= kMessageBubbleBaseMinHeight
- button_height
;
231 const int max_height
= kMessageBubbleBaseMaxHeight
- button_height
;
232 int scroll_height
= std::min(std::max(
233 scroll_size
.height(), min_height
), max_height
);
234 scroll_size
.set_height(scroll_height
);
235 if (scroll_height
== min_height
)
236 scroll_content_
->set_preferred_size(scroll_size
);
238 scroll_content_
->set_preferred_size(gfx::Size());
239 scroller_
->SetFixedSize(scroll_size
);
240 scroller_
->SizeToPreferredSize();
241 scroll_content_
->InvalidateLayout();
244 NotificationList::Delegate
* list_delegate_
;
245 FixedSizedScrollView
* scroller_
;
246 ScrollContentView
* scroll_content_
;
247 WebNotificationButtonView
* button_view_
;
249 DISALLOW_COPY_AND_ASSIGN(MessageCenterContentsView
);
252 // Message Center Bubble.
253 MessageCenterBubble::MessageCenterBubble(NotificationList::Delegate
* delegate
)
254 : MessageBubbleBase(delegate
),
255 contents_view_(NULL
) {
258 MessageCenterBubble::~MessageCenterBubble() {}
260 views::TrayBubbleView::InitParams
MessageCenterBubble::GetInitParams(
261 views::TrayBubbleView::AnchorAlignment anchor_alignment
) {
262 views::TrayBubbleView::InitParams init_params
=
263 GetDefaultInitParams(anchor_alignment
);
264 init_params
.max_height
= kMessageBubbleBaseMaxHeight
;
265 init_params
.can_activate
= true;
269 void MessageCenterBubble::InitializeContents(
270 views::TrayBubbleView
* new_bubble_view
) {
271 set_bubble_view(new_bubble_view
);
272 contents_view_
= new MessageCenterContentsView(list_delegate());
273 bubble_view()->AddChildView(contents_view_
);
275 contents_view_
->FocusContents();
278 void MessageCenterBubble::OnBubbleViewDestroyed() {
279 contents_view_
= NULL
;
282 void MessageCenterBubble::UpdateBubbleView() {
284 return; // Could get called after view is closed
285 NotificationList::Notifications notifications
;
286 list_delegate()->GetNotificationList()->GetNotifications(¬ifications
);
287 contents_view_
->Update(notifications
);
288 bubble_view()->Show();
289 bubble_view()->UpdateBubble();
292 void MessageCenterBubble::OnMouseEnteredView() {
295 void MessageCenterBubble::OnMouseExitedView() {
298 size_t MessageCenterBubble::NumMessageViewsForTest() const {
299 return contents_view_
->NumMessageViews();
302 } // namespace message_center