blob: 9f0d2fdf3186372d53a8d25dabccb4ceeb59de88 [file] [log] [blame]
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001// Copyright (c) 2013 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 "ui/message_center/views/message_center_view.h"
6
Ben Murdocheb525c52013-07-10 11:40:50 +01007#include <list>
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01008#include <map>
9
Ben Murdocheb525c52013-07-10 11:40:50 +010010#include "base/memory/weak_ptr.h"
Ben Murdochca12bfa2013-07-23 11:17:05 +010011#include "base/message_loop/message_loop.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010012#include "base/stl_util.h"
13#include "grit/ui_strings.h"
Ben Murdocheb525c52013-07-10 11:40:50 +010014#include "ui/base/animation/multi_animation.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010015#include "ui/base/animation/slide_animation.h"
16#include "ui/base/l10n/l10n_util.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010017#include "ui/gfx/canvas.h"
18#include "ui/gfx/insets.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010019#include "ui/gfx/rect.h"
20#include "ui/gfx/size.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010021#include "ui/message_center/message_center.h"
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010022#include "ui/message_center/message_center_style.h"
Ben Murdochbb1529c2013-08-08 10:24:53 +010023#include "ui/message_center/views/message_center_button_bar.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010024#include "ui/message_center/views/message_view.h"
25#include "ui/message_center/views/notification_view.h"
Ben Murdocheb525c52013-07-10 11:40:50 +010026#include "ui/message_center/views/notifier_settings_view.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010027#include "ui/views/animation/bounds_animator.h"
28#include "ui/views/animation/bounds_animator_observer.h"
29#include "ui/views/background.h"
30#include "ui/views/border.h"
31#include "ui/views/controls/button/button.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010032#include "ui/views/controls/label.h"
33#include "ui/views/controls/scroll_view.h"
Torne (Richard Coles)5e3f23d2013-06-11 16:24:11 +010034#include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010035#include "ui/views/layout/box_layout.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010036#include "ui/views/widget/widget.h"
37
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010038namespace message_center {
39
40namespace {
41
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010042const SkColor kBorderDarkColor = SkColorSetRGB(0xaa, 0xaa, 0xaa);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010043const SkColor kButtonTextHighlightColor = SkColorSetRGB(0x2a, 0x2a, 0x2a);
44const SkColor kButtonTextHoverColor = SkColorSetRGB(0x2a, 0x2a, 0x2a);
Ben Murdochbb1529c2013-08-08 10:24:53 +010045const SkColor kNoNotificationsTextColor = SkColorSetRGB(0xb4, 0xb4, 0xb4);
46const SkColor kTransparentColor = SkColorSetARGB(0, 0, 0, 0);
Ben Murdocheb525c52013-07-10 11:40:50 +010047const int kAnimateClearingNextNotificationDelayMS = 40;
Ben Murdochbb1529c2013-08-08 10:24:53 +010048const int kButtonBarBorderThickness = 1;
49const int kMinScrollViewHeight = 100;
Ben Murdocheb525c52013-07-10 11:40:50 +010050
Ben Murdocheb525c52013-07-10 11:40:50 +010051static const int kDefaultAnimationDurationMs = 120;
Ben Murdochbb1529c2013-08-08 10:24:53 +010052static const int kDefaultFrameRateHz = 60;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010053
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010054} // namespace
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010055
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010056// BoundedScrollView ///////////////////////////////////////////////////////////
57
58// A custom scroll view whose height has a minimum and maximum value and whose
59// scroll bar disappears when not needed.
60class BoundedScrollView : public views::ScrollView {
61 public:
62 BoundedScrollView(int min_height, int max_height);
63
64 // Overridden from views::View:
65 virtual gfx::Size GetPreferredSize() OVERRIDE;
66 virtual int GetHeightForWidth(int width) OVERRIDE;
67 virtual void Layout() OVERRIDE;
68
69 private:
70 int min_height_;
71 int max_height_;
72
73 DISALLOW_COPY_AND_ASSIGN(BoundedScrollView);
74};
75
76BoundedScrollView::BoundedScrollView(int min_height, int max_height)
77 : min_height_(min_height),
78 max_height_(max_height) {
79 set_notify_enter_exit_on_child(true);
80 // Cancels the default dashed focus border.
81 set_focus_border(NULL);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010082 set_background(
83 views::Background::CreateSolidBackground(kMessageCenterBackgroundColor));
84 SetVerticalScrollBar(new views::OverlayScrollBar(false));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010085}
86
87gfx::Size BoundedScrollView::GetPreferredSize() {
88 gfx::Size size = contents()->GetPreferredSize();
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010089 size.SetToMax(gfx::Size(size.width(), min_height_));
90 size.SetToMin(gfx::Size(size.width(), max_height_));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010091 gfx::Insets insets = GetInsets();
92 size.Enlarge(insets.width(), insets.height());
93 return size;
94}
95
96int BoundedScrollView::GetHeightForWidth(int width) {
97 gfx::Insets insets = GetInsets();
98 width = std::max(0, width - insets.width());
99 int height = contents()->GetHeightForWidth(width) + insets.height();
100 return std::min(std::max(height, min_height_), max_height_);
101}
102
103void BoundedScrollView::Layout() {
104 int content_width = width();
105 int content_height = contents()->GetHeightForWidth(content_width);
106 if (content_height > height()) {
107 content_width = std::max(content_width - GetScrollBarWidth(), 0);
108 content_height = contents()->GetHeightForWidth(content_width);
109 }
110 if (contents()->bounds().size() != gfx::Size(content_width, content_height))
111 contents()->SetBounds(0, 0, content_width, content_height);
112 views::ScrollView::Layout();
113}
114
115class NoNotificationMessageView : public views::View {
116 public:
117 NoNotificationMessageView();
118 virtual ~NoNotificationMessageView();
119
120 // Overridden from views::View.
121 virtual gfx::Size GetPreferredSize() OVERRIDE;
122 virtual int GetHeightForWidth(int width) OVERRIDE;
123 virtual void Layout() OVERRIDE;
124
125 private:
126 views::Label* label_;
127
128 DISALLOW_COPY_AND_ASSIGN(NoNotificationMessageView);
129};
130
131NoNotificationMessageView::NoNotificationMessageView() {
132 label_ = new views::Label(l10n_util::GetStringUTF16(
133 IDS_MESSAGE_CENTER_NO_MESSAGES));
134 label_->SetAutoColorReadabilityEnabled(false);
135 label_->SetEnabledColor(kNoNotificationsTextColor);
136 // Set transparent background to ensure that subpixel rendering
137 // is disabled. See crbug.com/169056
138#if defined(OS_LINUX) && defined(OS_CHROMEOS)
139 label_->SetBackgroundColor(kTransparentColor);
140#endif
141 AddChildView(label_);
142}
143
144NoNotificationMessageView::~NoNotificationMessageView() {
145}
146
147gfx::Size NoNotificationMessageView::GetPreferredSize() {
148 return gfx::Size(kMinScrollViewHeight, label_->GetPreferredSize().width());
149}
150
151int NoNotificationMessageView::GetHeightForWidth(int width) {
152 return kMinScrollViewHeight;
153}
154
155void NoNotificationMessageView::Layout() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100156 int text_height = label_->GetHeightForWidth(width());
157 int margin = (height() - text_height) / 2;
158 label_->SetBounds(0, margin, width(), text_height);
159}
160
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100161// Displays a list of messages for rich notifications. It also supports
162// repositioning.
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100163class MessageListView : public views::View,
164 public views::BoundsAnimatorObserver {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100165 public:
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100166 explicit MessageListView(MessageCenterView* message_center_view,
167 bool top_down);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100168 virtual ~MessageListView();
169
170 void AddNotificationAt(views::View* view, int i);
171 void RemoveNotificationAt(int i);
172 void UpdateNotificationAt(views::View* view, int i);
173 void SetRepositionTarget(const gfx::Rect& target_rect);
174 void ResetRepositionSession();
175 void ClearAllNotifications(const gfx::Rect& visible_scroll_rect);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100176
177 protected:
178 // Overridden from views::View.
179 virtual void Layout() OVERRIDE;
180 virtual gfx::Size GetPreferredSize() OVERRIDE;
181 virtual int GetHeightForWidth(int width) OVERRIDE;
Ben Murdocheb525c52013-07-10 11:40:50 +0100182 virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE;
183 virtual void ReorderChildLayers(ui::Layer* parent_layer) OVERRIDE;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100184
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100185 // Overridden from views::BoundsAnimatorObserver.
186 virtual void OnBoundsAnimatorProgressed(
187 views::BoundsAnimator* animator) OVERRIDE;
188 virtual void OnBoundsAnimatorDone(views::BoundsAnimator* animator) OVERRIDE;
189
190 private:
Ben Murdocheb525c52013-07-10 11:40:50 +0100191 // Returns the actual index for child of |index|.
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100192 // MessageListView allows to slide down upper notifications, which means
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100193 // that the upper ones should come above the lower ones if top_down is not
194 // enabled. To achieve this, inversed order is adopted. The top most
195 // notification is the last child, and the bottom most notification is the
196 // first child.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100197 int GetActualIndex(int index);
198 bool IsValidChild(views::View* child);
199 void DoUpdateIfPossible();
200
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100201 // Animates all notifications below target upwards to align with the top of
202 // the last closed notification.
203 void AnimateNotificationsBelowTarget();
204 // Animates all notifications above target downwards to align with the top of
205 // the last closed notification.
206 void AnimateNotificationsAboveTarget();
207
208 // Schedules animation for a child to the specified position. Returns false
209 // if |child| will disappear after the animation.
210 bool AnimateChild(views::View* child, int top, int height);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100211
Ben Murdocheb525c52013-07-10 11:40:50 +0100212 // Animate clearing one notification.
213 void AnimateClearingOneNotification();
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100214 MessageCenterView* message_center_view() const {
215 return message_center_view_;
216 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100217
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100218 MessageCenterView* message_center_view_; // Weak reference.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100219 // The top position of the reposition target rectangle.
220 int reposition_top_;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100221 int fixed_height_;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100222 bool has_deferred_task_;
Ben Murdocheb525c52013-07-10 11:40:50 +0100223 bool clear_all_started_;
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100224 bool top_down_;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100225 std::set<views::View*> adding_views_;
226 std::set<views::View*> deleting_views_;
227 std::set<views::View*> deleted_when_done_;
Ben Murdocheb525c52013-07-10 11:40:50 +0100228 std::list<views::View*> clearing_all_views_;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100229 scoped_ptr<views::BoundsAnimator> animator_;
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100230 base::WeakPtrFactory<MessageListView> weak_ptr_factory_;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100231
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100232 DISALLOW_COPY_AND_ASSIGN(MessageListView);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100233};
234
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100235MessageListView::MessageListView(MessageCenterView* message_center_view,
236 bool top_down)
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100237 : message_center_view_(message_center_view),
Ben Murdocheb525c52013-07-10 11:40:50 +0100238 reposition_top_(-1),
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100239 fixed_height_(0),
Ben Murdocheb525c52013-07-10 11:40:50 +0100240 has_deferred_task_(false),
241 clear_all_started_(false),
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100242 top_down_(top_down),
Ben Murdocheb525c52013-07-10 11:40:50 +0100243 weak_ptr_factory_(this) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100244 views::BoxLayout* layout =
245 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1);
246 layout->set_spread_blank_space(true);
247 SetLayoutManager(layout);
248
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100249 // Set the margin to 0 for the layout. BoxLayout assumes the same margin
250 // for top and bottom, but the bottom margin here should be smaller
251 // because of the shadow of message view. Use an empty border instead
252 // to provide this margin.
253 gfx::Insets shadow_insets = MessageView::GetShadowInsets();
254 set_background(views::Background::CreateSolidBackground(
255 kMessageCenterBackgroundColor));
256 set_border(views::Border::CreateEmptyBorder(
257 kMarginBetweenItems - shadow_insets.top(), /* top */
258 kMarginBetweenItems - shadow_insets.left(), /* left */
259 kMarginBetweenItems - shadow_insets.bottom(), /* bottom */
260 kMarginBetweenItems - shadow_insets.right() /* right */ ));
261}
262
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100263MessageListView::~MessageListView() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100264 if (animator_.get())
265 animator_->RemoveObserver(this);
266}
267
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100268void MessageListView::Layout() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100269 if (animator_.get())
270 return;
271
272 gfx::Rect child_area = GetContentsBounds();
273 int top = child_area.y();
274 int between_items =
275 kMarginBetweenItems - MessageView::GetShadowInsets().bottom();
276
277 for (int i = 0; i < child_count(); ++i) {
278 views::View* child = child_at(i);
279 if (!child->visible())
280 continue;
281 int height = child->GetHeightForWidth(child_area.width());
282 child->SetBounds(child_area.x(), top, child_area.width(), height);
283 top += height + between_items;
284 }
285}
286
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100287void MessageListView::AddNotificationAt(views::View* view, int i) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100288 AddChildViewAt(view, GetActualIndex(i));
289 if (GetContentsBounds().IsEmpty())
290 return;
291
292 adding_views_.insert(view);
293 DoUpdateIfPossible();
294}
295
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100296void MessageListView::RemoveNotificationAt(int i) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100297 views::View* child = child_at(GetActualIndex(i));
298 if (GetContentsBounds().IsEmpty()) {
299 delete child;
300 } else {
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100301 if (child->layer()) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100302 deleting_views_.insert(child);
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100303 } else {
304 if (animator_.get())
305 animator_->StopAnimatingView(child);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100306 delete child;
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100307 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100308 DoUpdateIfPossible();
309 }
310}
311
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100312void MessageListView::UpdateNotificationAt(views::View* view, int i) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100313 int actual_index = GetActualIndex(i);
314 views::View* child = child_at(actual_index);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100315 if (animator_.get())
316 animator_->StopAnimatingView(child);
317 gfx::Rect old_bounds = child->bounds();
318 if (deleting_views_.find(child) != deleting_views_.end())
319 deleting_views_.erase(child);
320 if (deleted_when_done_.find(child) != deleted_when_done_.end())
321 deleted_when_done_.erase(child);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100322 delete child;
Ben Murdocheb525c52013-07-10 11:40:50 +0100323 AddChildViewAt(view, actual_index);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100324 view->SetBounds(old_bounds.x(), old_bounds.y(), old_bounds.width(),
325 view->GetHeightForWidth(old_bounds.width()));
326 DoUpdateIfPossible();
327}
328
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100329gfx::Size MessageListView::GetPreferredSize() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100330 int width = 0;
331 for (int i = 0; i < child_count(); i++) {
332 views::View* child = child_at(i);
333 if (IsValidChild(child))
334 width = std::max(width, child->GetPreferredSize().width());
335 }
336
337 return gfx::Size(width + GetInsets().width(),
338 GetHeightForWidth(width + GetInsets().width()));
339}
340
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100341int MessageListView::GetHeightForWidth(int width) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100342 if (fixed_height_ > 0)
343 return fixed_height_;
344
345 width -= GetInsets().width();
346 int height = 0;
347 int padding = 0;
348 for (int i = 0; i < child_count(); ++i) {
349 views::View* child = child_at(i);
350 if (!IsValidChild(child))
351 continue;
352 height += child->GetHeightForWidth(width) + padding;
353 padding = kMarginBetweenItems - MessageView::GetShadowInsets().bottom();
354 }
355
356 return height + GetInsets().height();
357}
358
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100359void MessageListView::PaintChildren(gfx::Canvas* canvas) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100360 // Paint in the inversed order. Otherwise upper notification may be
361 // hidden by the lower one.
362 for (int i = child_count() - 1; i >= 0; --i) {
363 if (!child_at(i)->layer())
364 child_at(i)->Paint(canvas);
365 }
366}
367
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100368void MessageListView::ReorderChildLayers(ui::Layer* parent_layer) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100369 // Reorder children to stack the last child layer at the top. Otherwise
370 // upper notification may be hidden by the lower one.
371 for (int i = 0; i < child_count(); ++i) {
372 if (child_at(i)->layer())
373 parent_layer->StackAtBottom(child_at(i)->layer());
374 }
375}
376
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100377void MessageListView::SetRepositionTarget(const gfx::Rect& target) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100378 reposition_top_ = target.y();
379 fixed_height_ = GetHeightForWidth(width());
380}
381
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100382void MessageListView::ResetRepositionSession() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100383 // Don't call DoUpdateIfPossible(), but let Layout() do the task without
384 // animation. Reset will cause the change of the bubble size itself, and
385 // animation from the old location will look weird.
386 if (reposition_top_ >= 0 && animator_.get()) {
387 has_deferred_task_ = false;
388 // cancel cause OnBoundsAnimatorDone which deletes |deleted_when_done_|.
389 animator_->Cancel();
390 STLDeleteContainerPointers(deleting_views_.begin(), deleting_views_.end());
391 deleting_views_.clear();
392 adding_views_.clear();
393 animator_.reset();
394 }
395
396 reposition_top_ = -1;
397 fixed_height_ = 0;
398}
399
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100400void MessageListView::ClearAllNotifications(
Ben Murdocheb525c52013-07-10 11:40:50 +0100401 const gfx::Rect& visible_scroll_rect) {
402 for (int i = 0; i < child_count(); ++i) {
403 views::View* child = child_at(i);
404 if (!child->visible())
405 continue;
406 if (gfx::IntersectRects(child->bounds(), visible_scroll_rect).IsEmpty())
407 continue;
408 clearing_all_views_.push_back(child);
409 }
410 DoUpdateIfPossible();
411}
412
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100413void MessageListView::OnBoundsAnimatorProgressed(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100414 views::BoundsAnimator* animator) {
415 DCHECK_EQ(animator_.get(), animator);
416 for (std::set<views::View*>::iterator iter = deleted_when_done_.begin();
417 iter != deleted_when_done_.end(); ++iter) {
418 const ui::SlideAnimation* animation = animator->GetAnimationForView(*iter);
419 if (animation)
420 (*iter)->layer()->SetOpacity(animation->CurrentValueBetween(1.0, 0.0));
421 }
422}
423
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100424void MessageListView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100425 STLDeleteContainerPointers(
426 deleted_when_done_.begin(), deleted_when_done_.end());
427 deleted_when_done_.clear();
428
Ben Murdocheb525c52013-07-10 11:40:50 +0100429 if (clear_all_started_) {
430 clear_all_started_ = false;
431 message_center_view()->OnAllNotificationsCleared();
432 }
433
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100434 if (has_deferred_task_) {
435 has_deferred_task_ = false;
436 DoUpdateIfPossible();
437 }
438
439 if (GetWidget())
440 GetWidget()->SynthesizeMouseMoveEvent();
441}
442
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100443int MessageListView::GetActualIndex(int index) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100444 for (int i = 0; i < child_count() && i <= index; ++i)
445 index += IsValidChild(child_at(i)) ? 0 : 1;
446 return std::min(index, child_count());
447}
448
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100449bool MessageListView::IsValidChild(views::View* child) {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100450 return child->visible() &&
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100451 deleting_views_.find(child) == deleting_views_.end() &&
452 deleted_when_done_.find(child) == deleted_when_done_.end();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100453}
454
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100455void MessageListView::DoUpdateIfPossible() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100456 gfx::Rect child_area = GetContentsBounds();
457 if (child_area.IsEmpty())
458 return;
459
460 if (animator_.get() && animator_->IsAnimating()) {
461 has_deferred_task_ = true;
462 return;
463 }
464
465 if (!animator_.get()) {
466 animator_.reset(new views::BoundsAnimator(this));
467 animator_->AddObserver(this);
468 }
469
Ben Murdocheb525c52013-07-10 11:40:50 +0100470 if (!clearing_all_views_.empty()) {
471 AnimateClearingOneNotification();
472 return;
473 }
474
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100475 if (top_down_)
476 AnimateNotificationsBelowTarget();
477 else
478 AnimateNotificationsAboveTarget();
479
480 adding_views_.clear();
481 deleting_views_.clear();
482}
483
484void MessageListView::AnimateNotificationsBelowTarget() {
Ben Murdocheb525c52013-07-10 11:40:50 +0100485 int last_index = -1;
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100486 for (int i = 0; i < child_count(); ++i) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100487 views::View* child = child_at(i);
Ben Murdocheb525c52013-07-10 11:40:50 +0100488 if (!IsValidChild(child)) {
489 AnimateChild(child, child->y(), child->height());
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100490 } else if (reposition_top_ < 0 || child->y() > reposition_top_) {
491 // Find first notification below target (or all notifications if no
492 // target).
Ben Murdocheb525c52013-07-10 11:40:50 +0100493 last_index = i;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100494 break;
495 }
496 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100497 if (last_index > 0) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100498 int between_items =
499 kMarginBetweenItems - MessageView::GetShadowInsets().bottom();
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100500 int top = (reposition_top_ > 0) ? reposition_top_ : GetInsets().top();
501
502 for (int i = last_index; i < child_count(); ++i) {
503 // Animate notifications below target upwards.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100504 views::View* child = child_at(i);
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100505 if (AnimateChild(child, top, child->height()))
506 top += child->height() + between_items;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100507 }
508 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100509}
510
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100511void MessageListView::AnimateNotificationsAboveTarget() {
512 int last_index = -1;
513 for (int i = child_count() - 1; i >= 0; --i) {
514 views::View* child = child_at(i);
515 if (!IsValidChild(child)) {
516 AnimateChild(child, child->y(), child->height());
517 } else if (reposition_top_ < 0 || child->y() < reposition_top_) {
518 // Find first notification above target (or all notifications if no
519 // target).
520 last_index = i;
521 break;
522 }
523 }
524 if (last_index > 0) {
525 int between_items =
526 kMarginBetweenItems - MessageView::GetShadowInsets().bottom();
527 int bottom = (reposition_top_ > 0)
528 ? reposition_top_ + child_at(last_index)->height()
529 : GetHeightForWidth(width()) - GetInsets().bottom();
530 for (int i = last_index; i >= 0; --i) {
531 // Animate notifications above target downwards.
532 views::View* child = child_at(i);
533 if (AnimateChild(child, bottom - child->height(), child->height()))
534 bottom -= child->height() + between_items;
535 }
536 }
537}
538
539bool MessageListView::AnimateChild(views::View* child, int top, int height) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100540 gfx::Rect child_area = GetContentsBounds();
541 if (adding_views_.find(child) != adding_views_.end()) {
542 child->SetBounds(child_area.right(), top, child_area.width(), height);
543 animator_->AnimateViewTo(
544 child, gfx::Rect(child_area.x(), top, child_area.width(), height));
545 } else if (deleting_views_.find(child) != deleting_views_.end()) {
546 DCHECK(child->layer());
547 // No moves, but animate to fade-out.
548 animator_->AnimateViewTo(child, child->bounds());
549 deleted_when_done_.insert(child);
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100550 return false;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100551 } else {
552 gfx::Rect target(child_area.x(), top, child_area.width(), height);
553 if (child->bounds().origin() != target.origin())
554 animator_->AnimateViewTo(child, target);
555 else
556 child->SetBoundsRect(target);
557 }
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100558 return true;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100559}
560
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100561void MessageListView::AnimateClearingOneNotification() {
Ben Murdocheb525c52013-07-10 11:40:50 +0100562 DCHECK(!clearing_all_views_.empty());
563
564 clear_all_started_ = true;
565
566 views::View* child = clearing_all_views_.front();
567 clearing_all_views_.pop_front();
568
569 // Slide from left to right.
570 gfx::Rect new_bounds = child->bounds();
571 new_bounds.set_x(new_bounds.right() + kMarginBetweenItems);
572 animator_->AnimateViewTo(child, new_bounds);
573
574 // Schedule to start sliding out next notification after a short delay.
575 if (!clearing_all_views_.empty()) {
576 base::MessageLoop::current()->PostDelayedTask(
577 FROM_HERE,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100578 base::Bind(&MessageListView::AnimateClearingOneNotification,
579 weak_ptr_factory_.GetWeakPtr()),
Ben Murdocheb525c52013-07-10 11:40:50 +0100580 base::TimeDelta::FromMilliseconds(
581 kAnimateClearingNextNotificationDelayMS));
582 }
583}
584
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100585// MessageCenterView ///////////////////////////////////////////////////////////
586
587MessageCenterView::MessageCenterView(MessageCenter* message_center,
Ben Murdocheb525c52013-07-10 11:40:50 +0100588 MessageCenterTray* tray,
589 int max_height,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100590 bool initially_settings_visible,
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100591 bool top_down)
Ben Murdocheb525c52013-07-10 11:40:50 +0100592 : message_center_(message_center),
593 tray_(tray),
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100594 top_down_(top_down),
Ben Murdoch2385ea32013-08-06 11:01:04 +0100595 settings_visible_(initially_settings_visible),
Ben Murdochbb1529c2013-08-08 10:24:53 +0100596 source_view_(NULL),
597 source_height_(0),
598 target_view_(NULL),
599 target_height_(0),
Ben Murdoch2385ea32013-08-06 11:01:04 +0100600 is_closing_(false) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100601 message_center_->AddObserver(this);
602 set_notify_enter_exit_on_child(true);
Ben Murdocheb525c52013-07-10 11:40:50 +0100603 set_background(views::Background::CreateSolidBackground(
604 kMessageCenterBackgroundColor));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100605
Ben Murdochbb1529c2013-08-08 10:24:53 +0100606 NotifierSettingsProvider* notifier_settings_provider =
607 message_center_->GetNotifierSettingsProvider();
608 button_bar_ = new MessageCenterButtonBar(this,
609 message_center,
610 notifier_settings_provider,
611 initially_settings_visible);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100612
613 const int button_height = button_bar_->GetPreferredSize().height();
Ben Murdochbb1529c2013-08-08 10:24:53 +0100614 button_bar_->set_border(views::Border::CreateSolidSidedBorder(
615 top_down_ ? 0 : kButtonBarBorderThickness,
616 0,
617 top_down_ ? kButtonBarBorderThickness : 0,
618 0,
619 kFooterDelimiterColor));
620
621 scroller_ =
622 new BoundedScrollView(kMinScrollViewHeight, max_height - button_height);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100623
624 if (get_use_acceleration_when_possible()) {
625 scroller_->SetPaintToLayer(true);
626 scroller_->SetFillsBoundsOpaquely(false);
627 scroller_->layer()->SetMasksToBounds(true);
628 }
629
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100630 message_list_view_ = new MessageListView(this, top_down);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100631 no_notifications_message_view_ = new NoNotificationMessageView();
632 // Set the default visibility to false, otherwise the notification has slide
633 // in animation when the center is shown.
634 no_notifications_message_view_->SetVisible(false);
635 message_list_view_->AddChildView(no_notifications_message_view_);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100636 scroller_->SetContents(message_list_view_);
637
Ben Murdochbb1529c2013-08-08 10:24:53 +0100638 settings_view_ = new NotifierSettingsView(notifier_settings_provider);
Ben Murdocheb525c52013-07-10 11:40:50 +0100639
640 if (initially_settings_visible)
641 scroller_->SetVisible(false);
642 else
643 settings_view_->SetVisible(false);
644
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100645 AddChildView(scroller_);
Ben Murdocheb525c52013-07-10 11:40:50 +0100646 AddChildView(settings_view_);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100647 AddChildView(button_bar_);
648}
649
650MessageCenterView::~MessageCenterView() {
Ben Murdoch2385ea32013-08-06 11:01:04 +0100651 if (!is_closing_)
652 message_center_->RemoveObserver(this);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100653}
654
655void MessageCenterView::SetNotifications(
656 const NotificationList::Notifications& notifications) {
Ben Murdoch2385ea32013-08-06 11:01:04 +0100657 if (is_closing_)
658 return;
659
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100660 message_views_.clear();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100661 int index = 0;
662 for (NotificationList::Notifications::const_iterator iter =
663 notifications.begin(); iter != notifications.end();
664 ++iter, ++index) {
665 AddNotificationAt(*(*iter), index);
666 if (message_views_.size() >= kMaxVisibleMessageCenterNotifications)
667 break;
668 }
669 NotificationsChanged();
670 scroller_->RequestFocus();
671}
672
Ben Murdocheb525c52013-07-10 11:40:50 +0100673void MessageCenterView::SetSettingsVisible(bool visible) {
Ben Murdoch2385ea32013-08-06 11:01:04 +0100674 if (is_closing_)
675 return;
676
Ben Murdocheb525c52013-07-10 11:40:50 +0100677 if (visible == settings_visible_)
678 return;
679
680 settings_visible_ = visible;
681
682 if (visible) {
683 source_view_ = scroller_;
684 target_view_ = settings_view_;
685 } else {
686 source_view_ = settings_view_;
687 target_view_ = scroller_;
688 }
689 source_height_ = source_view_->GetHeightForWidth(width());
690 target_height_ = target_view_->GetHeightForWidth(width());
691
692 ui::MultiAnimation::Parts parts;
693 // First part: slide resize animation.
694 parts.push_back(ui::MultiAnimation::Part(
695 (source_height_ == target_height_) ? 0 : kDefaultAnimationDurationMs,
696 ui::Tween::EASE_OUT));
697 // Second part: fade-out the source_view.
698 if (source_view_->layer()) {
699 parts.push_back(ui::MultiAnimation::Part(
700 kDefaultAnimationDurationMs, ui::Tween::LINEAR));
701 } else {
702 parts.push_back(ui::MultiAnimation::Part());
703 }
704 // Third part: fade-in the target_view.
705 if (target_view_->layer()) {
706 parts.push_back(ui::MultiAnimation::Part(
707 kDefaultAnimationDurationMs, ui::Tween::LINEAR));
708 target_view_->layer()->SetOpacity(0);
709 target_view_->SetVisible(true);
710 } else {
711 parts.push_back(ui::MultiAnimation::Part());
712 }
713 settings_transition_animation_.reset(new ui::MultiAnimation(
714 parts, base::TimeDelta::FromMicroseconds(1000000 / kDefaultFrameRateHz)));
715 settings_transition_animation_->set_delegate(this);
716 settings_transition_animation_->set_continuous(false);
717 settings_transition_animation_->Start();
718}
719
Ben Murdoch2385ea32013-08-06 11:01:04 +0100720
Ben Murdocheb525c52013-07-10 11:40:50 +0100721void MessageCenterView::ClearAllNotifications() {
Ben Murdoch2385ea32013-08-06 11:01:04 +0100722 if (is_closing_)
723 return;
724
Ben Murdocheb525c52013-07-10 11:40:50 +0100725 scroller_->SetEnabled(false);
726 button_bar_->SetAllButtonsEnabled(false);
727 message_list_view_->ClearAllNotifications(scroller_->GetVisibleRect());
728}
729
730void MessageCenterView::OnAllNotificationsCleared() {
731 scroller_->SetEnabled(true);
732 button_bar_->SetAllButtonsEnabled(true);
733 message_center_->RemoveAllNotifications(true); // Action by user.
734}
735
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100736size_t MessageCenterView::NumMessageViewsForTest() const {
737 return message_list_view_->child_count();
738}
739
Ben Murdochbb1529c2013-08-08 10:24:53 +0100740void MessageCenterView::OnSettingsChanged() {
741 scroller_->InvalidateLayout();
742 PreferredSizeChanged();
743 Layout();
744}
745
746void MessageCenterView::SetIsClosing(bool is_closing) {
747 is_closing_ = is_closing;
748 if (is_closing)
749 message_center_->RemoveObserver(this);
750 else
751 message_center_->AddObserver(this);
752}
753
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100754void MessageCenterView::Layout() {
Ben Murdoch2385ea32013-08-06 11:01:04 +0100755 if (is_closing_)
756 return;
757
Ben Murdochbb1529c2013-08-08 10:24:53 +0100758 int button_height = button_bar_->GetHeightForWidth(width()) +
759 button_bar_->GetInsets().height();
Ben Murdocheb525c52013-07-10 11:40:50 +0100760 // Skip unnecessary re-layout of contents during the resize animation.
761 if (settings_transition_animation_ &&
762 settings_transition_animation_->is_animating() &&
763 settings_transition_animation_->current_part_index() == 0) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100764 if (!top_down_)
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100765 button_bar_->SetBounds(
766 0, height() - button_height, width(), button_height);
Ben Murdocheb525c52013-07-10 11:40:50 +0100767 return;
768 }
769
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100770 scroller_->SetBounds(0,
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100771 top_down_ ? button_height : 0,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100772 width(),
773 height() - button_height);
774 settings_view_->SetBounds(0,
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100775 top_down_ ? button_height : 0,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100776 width(),
777 height() - button_height);
Ben Murdocheb525c52013-07-10 11:40:50 +0100778
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100779 button_bar_->SetBounds(0,
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100780 top_down_ ? 0 : height() - button_height,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100781 width(),
782 button_height);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100783 if (GetWidget())
784 GetWidget()->GetRootView()->SchedulePaint();
785}
786
Ben Murdocheb525c52013-07-10 11:40:50 +0100787gfx::Size MessageCenterView::GetPreferredSize() {
788 if (settings_transition_animation_ &&
789 settings_transition_animation_->is_animating()) {
790 int content_width = std::max(source_view_->GetPreferredSize().width(),
791 target_view_->GetPreferredSize().width());
792 int width = std::max(content_width,
793 button_bar_->GetPreferredSize().width());
794 return gfx::Size(width, GetHeightForWidth(width));
795 }
796
797 int width = 0;
798 for (int i = 0; i < child_count(); ++i) {
799 views::View* child = child_at(0);
800 if (child->visible())
801 width = std::max(width, child->GetPreferredSize().width());
802 }
803 return gfx::Size(width, GetHeightForWidth(width));
804}
805
806int MessageCenterView::GetHeightForWidth(int width) {
807 if (settings_transition_animation_ &&
808 settings_transition_animation_->is_animating()) {
809 int content_height = target_height_;
810 if (settings_transition_animation_->current_part_index() == 0) {
811 content_height = settings_transition_animation_->CurrentValueBetween(
812 source_height_, target_height_);
813 }
814 return button_bar_->GetHeightForWidth(width) + content_height;
815 }
816
817 int content_height = 0;
818 if (scroller_->visible())
819 content_height += scroller_->GetHeightForWidth(width);
820 else
821 content_height += settings_view_->GetHeightForWidth(width);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100822 return button_bar_->GetHeightForWidth(width) +
823 button_bar_->GetInsets().height() + content_height;
Ben Murdocheb525c52013-07-10 11:40:50 +0100824}
825
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100826bool MessageCenterView::OnMouseWheel(const ui::MouseWheelEvent& event) {
827 // Do not rely on the default scroll event handler of ScrollView because
828 // the scroll happens only when the focus is on the ScrollView. The
829 // notification center will allow the scrolling even when the focus is on
830 // the buttons.
831 if (scroller_->bounds().Contains(event.location()))
832 return scroller_->OnMouseWheel(event);
833 return views::View::OnMouseWheel(event);
834}
835
836void MessageCenterView::OnMouseExited(const ui::MouseEvent& event) {
Ben Murdoch2385ea32013-08-06 11:01:04 +0100837 if (is_closing_)
838 return;
839
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100840 message_list_view_->ResetRepositionSession();
841 NotificationsChanged();
842}
843
844void MessageCenterView::OnNotificationAdded(const std::string& id) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100845 int index = 0;
846 const NotificationList::Notifications& notifications =
847 message_center_->GetNotifications();
848 for (NotificationList::Notifications::const_iterator iter =
849 notifications.begin(); iter != notifications.end();
850 ++iter, ++index) {
851 if ((*iter)->id() == id) {
852 AddNotificationAt(*(*iter), index);
853 break;
854 }
855 if (message_views_.size() >= kMaxVisibleMessageCenterNotifications)
856 break;
857 }
858 NotificationsChanged();
859}
860
861void MessageCenterView::OnNotificationRemoved(const std::string& id,
862 bool by_user) {
863 for (size_t i = 0; i < message_views_.size(); ++i) {
864 if (message_views_[i]->notification_id() == id) {
865 if (by_user) {
866 message_list_view_->SetRepositionTarget(message_views_[i]->bounds());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100867 // Moves the keyboard focus to the next notification if the removed
868 // notification is focused so that the user can dismiss notifications
869 // without re-focusing by tab key.
870 if (message_views_.size() > 1) {
871 views::View* focused_view = GetFocusManager()->GetFocusedView();
872 if (message_views_[i]->IsCloseButtonFocused() ||
873 focused_view == message_views_[i]) {
874 size_t next_index = i + 1;
875 if (next_index >= message_views_.size())
876 next_index = message_views_.size() - 2;
877 if (focused_view == message_views_[i])
878 message_views_[next_index]->RequestFocus();
879 else
880 message_views_[next_index]->RequestFocusOnCloseButton();
881 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100882 }
883 }
884 message_list_view_->RemoveNotificationAt(i);
885 message_views_.erase(message_views_.begin() + i);
886 NotificationsChanged();
887 break;
888 }
889 }
890}
891
892void MessageCenterView::OnNotificationUpdated(const std::string& id) {
893 const NotificationList::Notifications& notifications =
894 message_center_->GetNotifications();
895 size_t index = 0;
896 for (NotificationList::Notifications::const_iterator iter =
897 notifications.begin();
898 iter != notifications.end() && index < message_views_.size();
899 ++iter, ++index) {
900 DCHECK((*iter)->id() == message_views_[index]->notification_id());
901 if ((*iter)->id() == id) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100902 MessageView* view =
903 NotificationView::Create(*(*iter),
904 message_center_,
Ben Murdocheb525c52013-07-10 11:40:50 +0100905 tray_,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100906 true, // Create expanded.
907 false); // Not creating a top-level
908 // notification.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100909 view->set_scroller(scroller_);
910 message_list_view_->UpdateNotificationAt(view, index);
911 message_views_[index] = view;
912 NotificationsChanged();
913 break;
914 }
915 }
916}
917
Ben Murdocheb525c52013-07-10 11:40:50 +0100918void MessageCenterView::AnimationEnded(const ui::Animation* animation) {
919 DCHECK_EQ(animation, settings_transition_animation_.get());
920
921 source_view_->SetVisible(false);
922 target_view_->SetVisible(true);
923 if (source_view_->layer())
924 source_view_->layer()->SetOpacity(1.0);
925 if (target_view_->layer())
926 target_view_->layer()->SetOpacity(1.0);
927 settings_transition_animation_.reset();
928 PreferredSizeChanged();
929 Layout();
930}
931
932void MessageCenterView::AnimationProgressed(const ui::Animation* animation) {
933 DCHECK_EQ(animation, settings_transition_animation_.get());
934 PreferredSizeChanged();
935 if (settings_transition_animation_->current_part_index() == 1 &&
936 source_view_->layer()) {
937 source_view_->layer()->SetOpacity(
938 1.0 - settings_transition_animation_->GetCurrentValue());
939 SchedulePaint();
940 } else if (settings_transition_animation_->current_part_index() == 2 &&
941 target_view_->layer()) {
942 target_view_->layer()->SetOpacity(
943 settings_transition_animation_->GetCurrentValue());
944 SchedulePaint();
945 }
946}
947
948void MessageCenterView::AnimationCanceled(const ui::Animation* animation) {
949 DCHECK_EQ(animation, settings_transition_animation_.get());
950 AnimationEnded(animation);
951}
952
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100953void MessageCenterView::AddNotificationAt(const Notification& notification,
954 int index) {
955 // NotificationViews are expanded by default here until
956 // http://crbug.com/217902 is fixed. TODO(dharcourt): Fix.
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100957 MessageView* view =
958 NotificationView::Create(notification,
959 message_center_,
Ben Murdocheb525c52013-07-10 11:40:50 +0100960 tray_,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100961 true, // Create expanded.
962 false); // Not creating a top-level
963 // notification.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100964 view->set_scroller(scroller_);
965 message_views_.insert(message_views_.begin() + index, view);
966 message_list_view_->AddNotificationAt(view, index);
967 message_center_->DisplayedNotification(notification.id());
968}
969
970void MessageCenterView::NotificationsChanged() {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100971 bool no_message_views = message_views_.empty();
972
973 no_notifications_message_view_->SetVisible(no_message_views);
974 button_bar_->SetCloseAllButtonVisible(!no_message_views);
975 scroller_->set_focusable(!no_message_views);
976
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100977 scroller_->InvalidateLayout();
978 PreferredSizeChanged();
979 Layout();
980}
981
982void MessageCenterView::SetNotificationViewForTest(views::View* view) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100983 message_list_view_->AddNotificationAt(view, 0);
984}
985
986} // namespace message_center