cc: Add more eviction categories to picture layer impl.
[chromium-blink-merge.git] / ui / message_center / views / toast_contents_view.cc
blob365519a920d0bbfd873f6fb33cf16490b1310f4e
1 // 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.
5 #include "ui/message_center/views/toast_contents_view.h"
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/time/time.h"
12 #include "base/timer/timer.h"
13 #include "ui/accessibility/ax_view_state.h"
14 #include "ui/gfx/animation/animation_delegate.h"
15 #include "ui/gfx/animation/slide_animation.h"
16 #include "ui/gfx/display.h"
17 #include "ui/gfx/screen.h"
18 #include "ui/message_center/message_center_style.h"
19 #include "ui/message_center/notification.h"
20 #include "ui/message_center/views/message_popup_collection.h"
21 #include "ui/message_center/views/message_view.h"
22 #include "ui/views/background.h"
23 #include "ui/views/view.h"
24 #include "ui/views/widget/widget.h"
25 #include "ui/views/widget/widget_delegate.h"
27 #if defined(OS_WIN)
28 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
29 #endif
31 using gfx::Screen;
33 namespace message_center {
34 namespace {
36 // The width of a toast before animated reveal and after closing.
37 const int kClosedToastWidth = 5;
39 // FadeIn/Out look a bit better if they are slightly longer then default slide.
40 const int kFadeInOutDuration = 200;
42 } // namespace.
44 // static
45 gfx::Size ToastContentsView::GetToastSizeForView(const views::View* view) {
46 int width = kNotificationWidth + view->GetInsets().width();
47 return gfx::Size(width, view->GetHeightForWidth(width));
50 ToastContentsView::ToastContentsView(
51 const std::string& notification_id,
52 base::WeakPtr<MessagePopupCollection> collection)
53 : collection_(collection),
54 id_(notification_id),
55 is_animating_bounds_(false),
56 is_closing_(false),
57 closing_animation_(NULL) {
58 set_notify_enter_exit_on_child(true);
59 // Sets the transparent background. Then, when the message view is slid out,
60 // the whole toast seems to slide although the actual bound of the widget
61 // remains. This is hacky but easier to keep the consistency.
62 set_background(views::Background::CreateSolidBackground(0, 0, 0, 0));
64 fade_animation_.reset(new gfx::SlideAnimation(this));
65 fade_animation_->SetSlideDuration(kFadeInOutDuration);
67 CreateWidget(collection->parent());
70 // This is destroyed when the toast window closes.
71 ToastContentsView::~ToastContentsView() {
72 if (collection_)
73 collection_->ForgetToast(this);
76 void ToastContentsView::SetContents(MessageView* view,
77 bool a11y_feedback_for_updates) {
78 bool already_has_contents = child_count() > 0;
79 RemoveAllChildViews(true);
80 AddChildView(view);
81 preferred_size_ = GetToastSizeForView(view);
82 Layout();
84 // If it has the contents already, this invocation means an update of the
85 // popup toast, and the new contents should be read through a11y feature.
86 // The notification type should be ALERT, otherwise the accessibility message
87 // won't be read for this view which returns ROLE_WINDOW.
88 if (already_has_contents && a11y_feedback_for_updates)
89 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, false);
92 void ToastContentsView::UpdateContents(const Notification& notification,
93 bool a11y_feedback_for_updates) {
94 DCHECK_GT(child_count(), 0);
95 MessageView* message_view = static_cast<MessageView*>(child_at(0));
96 message_view->UpdateWithNotification(notification);
97 if (a11y_feedback_for_updates)
98 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, false);
101 void ToastContentsView::RevealWithAnimation(gfx::Point origin) {
102 // Place/move the toast widgets. Currently it stacks the widgets from the
103 // right-bottom of the work area.
104 // TODO(mukai): allow to specify the placement policy from outside of this
105 // class. The policy should be specified from preference on Windows, or
106 // the launcher alignment on ChromeOS.
107 origin_ = gfx::Point(origin.x() - preferred_size_.width(),
108 origin.y() - preferred_size_.height());
110 gfx::Rect stable_bounds(origin_, preferred_size_);
112 SetBoundsInstantly(GetClosedToastBounds(stable_bounds));
113 StartFadeIn();
114 SetBoundsWithAnimation(stable_bounds);
117 void ToastContentsView::CloseWithAnimation() {
118 if (is_closing_)
119 return;
120 is_closing_ = true;
121 StartFadeOut();
124 void ToastContentsView::SetBoundsInstantly(gfx::Rect new_bounds) {
125 if (new_bounds == bounds())
126 return;
128 origin_ = new_bounds.origin();
129 if (!GetWidget())
130 return;
131 GetWidget()->SetBounds(new_bounds);
134 void ToastContentsView::SetBoundsWithAnimation(gfx::Rect new_bounds) {
135 if (new_bounds == bounds())
136 return;
138 origin_ = new_bounds.origin();
139 if (!GetWidget())
140 return;
142 // This picks up the current bounds, so if there was a previous animation
143 // half-done, the next one will pick up from the current location.
144 // This is the only place that should query current location of the Widget
145 // on screen, the rest should refer to the bounds_.
146 animated_bounds_start_ = GetWidget()->GetWindowBoundsInScreen();
147 animated_bounds_end_ = new_bounds;
149 if (collection_)
150 collection_->IncrementDeferCounter();
152 if (bounds_animation_.get())
153 bounds_animation_->Stop();
155 bounds_animation_.reset(new gfx::SlideAnimation(this));
156 bounds_animation_->Show();
159 void ToastContentsView::StartFadeIn() {
160 // The decrement is done in OnBoundsAnimationEndedOrCancelled callback.
161 if (collection_)
162 collection_->IncrementDeferCounter();
163 fade_animation_->Stop();
165 GetWidget()->SetOpacity(0);
166 GetWidget()->ShowInactive();
167 fade_animation_->Reset(0);
168 fade_animation_->Show();
171 void ToastContentsView::StartFadeOut() {
172 // The decrement is done in OnBoundsAnimationEndedOrCancelled callback.
173 if (collection_)
174 collection_->IncrementDeferCounter();
175 fade_animation_->Stop();
177 closing_animation_ = (is_closing_ ? fade_animation_.get() : NULL);
178 fade_animation_->Reset(1);
179 fade_animation_->Hide();
182 void ToastContentsView::OnBoundsAnimationEndedOrCancelled(
183 const gfx::Animation* animation) {
184 if (is_closing_ && closing_animation_ == animation && GetWidget()) {
185 views::Widget* widget = GetWidget();
187 // TODO(dewittj): This is a workaround to prevent a nasty bug where
188 // closing a transparent widget doesn't actually remove the window,
189 // causing entire areas of the screen to become unresponsive to clicks.
190 // See crbug.com/243469
191 widget->Hide();
192 #if defined(OS_WIN)
193 widget->SetOpacity(0xFF);
194 #endif
196 widget->Close();
199 // This cannot be called before GetWidget()->Close(). Decrementing defer count
200 // will invoke update, which may invoke another close animation with
201 // incrementing defer counter. Close() after such process will cause a
202 // mismatch between increment/decrement. See crbug.com/238477
203 if (collection_)
204 collection_->DecrementDeferCounter();
207 // gfx::AnimationDelegate
208 void ToastContentsView::AnimationProgressed(const gfx::Animation* animation) {
209 if (animation == bounds_animation_.get()) {
210 gfx::Rect current(animation->CurrentValueBetween(
211 animated_bounds_start_, animated_bounds_end_));
212 GetWidget()->SetBounds(current);
213 } else if (animation == fade_animation_.get()) {
214 unsigned char opacity =
215 static_cast<unsigned char>(fade_animation_->GetCurrentValue() * 255);
216 GetWidget()->SetOpacity(opacity);
220 void ToastContentsView::AnimationEnded(const gfx::Animation* animation) {
221 OnBoundsAnimationEndedOrCancelled(animation);
224 void ToastContentsView::AnimationCanceled(
225 const gfx::Animation* animation) {
226 OnBoundsAnimationEndedOrCancelled(animation);
229 // views::WidgetDelegate
230 views::View* ToastContentsView::GetContentsView() {
231 return this;
234 void ToastContentsView::WindowClosing() {
235 if (!is_closing_ && collection_.get())
236 collection_->ForgetToast(this);
239 void ToastContentsView::OnDisplayChanged() {
240 views::Widget* widget = GetWidget();
241 if (!widget)
242 return;
244 gfx::NativeView native_view = widget->GetNativeView();
245 if (!native_view || !collection_.get())
246 return;
248 collection_->OnDisplayMetricsChanged(
249 Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view));
252 void ToastContentsView::OnWorkAreaChanged() {
253 views::Widget* widget = GetWidget();
254 if (!widget)
255 return;
257 gfx::NativeView native_view = widget->GetNativeView();
258 if (!native_view || !collection_.get())
259 return;
261 collection_->OnDisplayMetricsChanged(
262 Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view));
265 // views::View
266 void ToastContentsView::OnMouseEntered(const ui::MouseEvent& event) {
267 if (collection_)
268 collection_->OnMouseEntered(this);
271 void ToastContentsView::OnMouseExited(const ui::MouseEvent& event) {
272 if (collection_)
273 collection_->OnMouseExited(this);
276 void ToastContentsView::Layout() {
277 if (child_count() > 0) {
278 child_at(0)->SetBounds(
279 0, 0, preferred_size_.width(), preferred_size_.height());
283 gfx::Size ToastContentsView::GetPreferredSize() const {
284 return child_count() ? GetToastSizeForView(child_at(0)) : gfx::Size();
287 void ToastContentsView::GetAccessibleState(ui::AXViewState* state) {
288 if (child_count() > 0)
289 child_at(0)->GetAccessibleState(state);
290 state->role = ui::AX_ROLE_WINDOW;
293 void ToastContentsView::ClickOnNotification(
294 const std::string& notification_id) {
295 if (collection_)
296 collection_->ClickOnNotification(notification_id);
299 void ToastContentsView::RemoveNotification(
300 const std::string& notification_id,
301 bool by_user) {
302 if (collection_)
303 collection_->RemoveNotification(notification_id, by_user);
306 scoped_ptr<ui::MenuModel> ToastContentsView::CreateMenuModel(
307 const NotifierId& notifier_id,
308 const base::string16& display_source) {
309 // Should not reach, the context menu should be handled in
310 // MessagePopupCollection.
311 NOTREACHED();
312 return scoped_ptr<ui::MenuModel>();
315 bool ToastContentsView::HasClickedListener(
316 const std::string& notification_id) {
317 if (!collection_)
318 return false;
319 return collection_->HasClickedListener(notification_id);
322 void ToastContentsView::ClickOnNotificationButton(
323 const std::string& notification_id,
324 int button_index) {
325 if (collection_)
326 collection_->ClickOnNotificationButton(notification_id, button_index);
329 void ToastContentsView::CreateWidget(gfx::NativeView parent) {
330 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
331 params.keep_on_top = true;
332 if (parent)
333 params.parent = parent;
334 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
335 params.delegate = this;
336 views::Widget* widget = new views::Widget();
337 widget->set_focus_on_creation(false);
339 #if defined(OS_WIN)
340 // We want to ensure that this toast always goes to the native desktop,
341 // not the Ash desktop (since there is already another toast contents view
342 // there.
343 if (!params.parent)
344 params.native_widget = new views::DesktopNativeWidgetAura(widget);
345 #endif
347 widget->Init(params);
350 gfx::Rect ToastContentsView::GetClosedToastBounds(gfx::Rect bounds) {
351 return gfx::Rect(bounds.x() + bounds.width() - kClosedToastWidth,
352 bounds.y(),
353 kClosedToastWidth,
354 bounds.height());
357 } // namespace message_center