Correct blacklist entry message
[chromium-blink-merge.git] / ui / message_center / views / toast_contents_view.cc
blob1ca3ed021957366a6baa9774b8c698568c75c8ab
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/base/accessibility/accessible_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.h"
19 #include "ui/message_center/message_center_style.h"
20 #include "ui/message_center/notification.h"
21 #include "ui/message_center/views/message_popup_collection.h"
22 #include "ui/message_center/views/message_view.h"
23 #include "ui/views/view.h"
24 #include "ui/views/widget/widget.h"
25 #include "ui/views/widget/widget_delegate.h"
27 namespace message_center {
28 namespace {
30 // The width of a toast before animated reveal and after closing.
31 const int kClosedToastWidth = 5;
33 // FadeIn/Out look a bit better if they are slightly longer then default slide.
34 const int kFadeInOutDuration = 200;
36 } // namespace.
38 // static
39 gfx::Size ToastContentsView::GetToastSizeForView(views::View* view) {
40 int width = kNotificationWidth + view->GetInsets().width();
41 return gfx::Size(width, view->GetHeightForWidth(width));
44 ToastContentsView::ToastContentsView(
45 const Notification* notification,
46 base::WeakPtr<MessagePopupCollection> collection,
47 MessageCenter* message_center)
48 : collection_(collection),
49 message_center_(message_center),
50 id_(notification->id()),
51 is_animating_bounds_(false),
52 is_closing_(false),
53 closing_animation_(NULL) {
54 DCHECK(collection_);
56 set_notify_enter_exit_on_child(true);
57 // Sets the transparent background. Then, when the message view is slid out,
58 // the whole toast seems to slide although the actual bound of the widget
59 // remains. This is hacky but easier to keep the consistency.
60 set_background(views::Background::CreateSolidBackground(0, 0, 0, 0));
62 fade_animation_.reset(new gfx::SlideAnimation(this));
63 fade_animation_->SetSlideDuration(kFadeInOutDuration);
66 // This is destroyed when the toast window closes.
67 ToastContentsView::~ToastContentsView() {
70 views::Widget* ToastContentsView::CreateWidget(gfx::NativeView parent) {
71 views::Widget::InitParams params(
72 views::Widget::InitParams::TYPE_POPUP);
73 params.keep_on_top = true;
74 if (parent)
75 params.parent = parent;
76 else
77 params.top_level = true;
78 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
79 params.delegate = this;
80 views::Widget* widget = new views::Widget();
81 widget->set_focus_on_creation(false);
82 widget->Init(params);
83 return widget;
86 void ToastContentsView::SetContents(MessageView* view) {
87 bool already_has_contents = child_count() > 0;
88 RemoveAllChildViews(true);
89 AddChildView(view);
90 preferred_size_ = GetToastSizeForView(view);
91 Layout();
92 // If it has the contents already, this invocation means an update of the
93 // popup toast, and the new contents should be read through a11y feature.
94 // The notification type should be ALERT, otherwise the accessibility message
95 // won't be read for this view which returns ROLE_WINDOW.
96 if (already_has_contents) {
97 const NotificationList::Notifications& notifications =
98 message_center_->GetVisibleNotifications();
99 for (NotificationList::Notifications::const_iterator iter =
100 notifications.begin(); iter != notifications.end(); ++iter) {
101 if ((*iter)->id() != id_)
102 continue;
104 const RichNotificationData& optional = (*iter)->rich_notification_data();
105 if (optional.should_make_spoken_feedback_for_popup_updates)
106 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, false);
107 break;
112 void ToastContentsView::RevealWithAnimation(gfx::Point origin) {
113 // Place/move the toast widgets. Currently it stacks the widgets from the
114 // right-bottom of the work area.
115 // TODO(mukai): allow to specify the placement policy from outside of this
116 // class. The policy should be specified from preference on Windows, or
117 // the launcher alignment on ChromeOS.
118 origin_ = gfx::Point(origin.x() - preferred_size_.width(),
119 origin.y() - preferred_size_.height());
121 gfx::Rect stable_bounds(origin_, preferred_size_);
123 SetBoundsInstantly(GetClosedToastBounds(stable_bounds));
124 StartFadeIn();
125 SetBoundsWithAnimation(stable_bounds);
128 void ToastContentsView::CloseWithAnimation(bool mark_as_shown) {
129 if (is_closing_)
130 return;
131 is_closing_ = true;
132 if (collection_)
133 collection_->RemoveToast(this);
134 if (mark_as_shown)
135 message_center_->MarkSinglePopupAsShown(id(), false);
136 StartFadeOut();
139 void ToastContentsView::SetBoundsInstantly(gfx::Rect new_bounds) {
140 if (new_bounds == bounds())
141 return;
143 origin_ = new_bounds.origin();
144 if (!GetWidget())
145 return;
146 GetWidget()->SetBounds(new_bounds);
149 void ToastContentsView::SetBoundsWithAnimation(gfx::Rect new_bounds) {
150 if (new_bounds == bounds())
151 return;
153 origin_ = new_bounds.origin();
154 if (!GetWidget())
155 return;
157 // This picks up the current bounds, so if there was a previous animation
158 // half-done, the next one will pick up from the current location.
159 // This is the only place that should query current location of the Widget
160 // on screen, the rest should refer to the bounds_.
161 animated_bounds_start_ = GetWidget()->GetWindowBoundsInScreen();
162 animated_bounds_end_ = new_bounds;
164 if (collection_)
165 collection_->IncrementDeferCounter();
167 if (bounds_animation_.get())
168 bounds_animation_->Stop();
170 bounds_animation_.reset(new gfx::SlideAnimation(this));
171 bounds_animation_->Show();
174 void ToastContentsView::StartFadeIn() {
175 // The decrement is done in OnBoundsAnimationEndedOrCancelled callback.
176 if (collection_)
177 collection_->IncrementDeferCounter();
178 fade_animation_->Stop();
180 GetWidget()->SetOpacity(0);
181 GetWidget()->Show();
182 fade_animation_->Reset(0);
183 fade_animation_->Show();
186 void ToastContentsView::StartFadeOut() {
187 // The decrement is done in OnBoundsAnimationEndedOrCancelled callback.
188 if (collection_)
189 collection_->IncrementDeferCounter();
190 fade_animation_->Stop();
192 closing_animation_ = (is_closing_ ? fade_animation_.get() : NULL);
193 fade_animation_->Reset(1);
194 fade_animation_->Hide();
197 void ToastContentsView::OnBoundsAnimationEndedOrCancelled(
198 const gfx::Animation* animation) {
199 if (is_closing_ && closing_animation_ == animation && GetWidget()) {
200 views::Widget* widget = GetWidget();
201 #if defined(USE_AURA)
202 // TODO(dewittj): This is a workaround to prevent a nasty bug where
203 // closing a transparent widget doesn't actually remove the window,
204 // causing entire areas of the screen to become unresponsive to clicks.
205 // See crbug.com/243469
206 widget->Hide();
207 # if defined(OS_WIN)
208 widget->SetOpacity(0xFF);
209 # endif
210 #endif
211 widget->Close();
214 // This cannot be called before GetWidget()->Close(). Decrementing defer count
215 // will invoke update, which may invoke another close animation with
216 // incrementing defer counter. Close() after such process will cause a
217 // mismatch between increment/decrement. See crbug.com/238477
218 if (collection_)
219 collection_->DecrementDeferCounter();
222 // gfx::AnimationDelegate
223 void ToastContentsView::AnimationProgressed(const gfx::Animation* animation) {
224 if (animation == bounds_animation_.get()) {
225 gfx::Rect current(animation->CurrentValueBetween(
226 animated_bounds_start_, animated_bounds_end_));
227 GetWidget()->SetBounds(current);
228 } else if (animation == fade_animation_.get()) {
229 unsigned char opacity =
230 static_cast<unsigned char>(fade_animation_->GetCurrentValue() * 255);
231 GetWidget()->SetOpacity(opacity);
235 void ToastContentsView::AnimationEnded(const gfx::Animation* animation) {
236 OnBoundsAnimationEndedOrCancelled(animation);
239 void ToastContentsView::AnimationCanceled(
240 const gfx::Animation* animation) {
241 OnBoundsAnimationEndedOrCancelled(animation);
244 // views::WidgetDelegate
245 views::View* ToastContentsView::GetContentsView() {
246 return this;
249 void ToastContentsView::WindowClosing() {
250 if (!is_closing_ && collection_)
251 collection_->RemoveToast(this);
254 bool ToastContentsView::CanActivate() const {
255 #if defined(OS_WIN) && defined(USE_AURA)
256 return true;
257 #else
258 return false;
259 #endif
262 void ToastContentsView::OnDisplayChanged() {
263 views::Widget* widget = GetWidget();
264 if (!widget)
265 return;
267 gfx::NativeView native_view = widget->GetNativeView();
268 if (!native_view || !collection_)
269 return;
271 collection_->OnDisplayBoundsChanged(gfx::Screen::GetScreenFor(
272 native_view)->GetDisplayNearestWindow(native_view));
275 void ToastContentsView::OnWorkAreaChanged() {
276 views::Widget* widget = GetWidget();
277 if (!widget)
278 return;
280 gfx::NativeView native_view = widget->GetNativeView();
281 if (!native_view || !collection_)
282 return;
284 collection_->OnDisplayBoundsChanged(gfx::Screen::GetScreenFor(
285 native_view)->GetDisplayNearestWindow(native_view));
288 // views::View
289 void ToastContentsView::OnMouseEntered(const ui::MouseEvent& event) {
290 if (collection_)
291 collection_->OnMouseEntered(this);
294 void ToastContentsView::OnMouseExited(const ui::MouseEvent& event) {
295 if (collection_)
296 collection_->OnMouseExited(this);
299 void ToastContentsView::Layout() {
300 if (child_count() > 0) {
301 child_at(0)->SetBounds(
302 0, 0, preferred_size_.width(), preferred_size_.height());
306 gfx::Size ToastContentsView::GetPreferredSize() {
307 return child_count() ? GetToastSizeForView(child_at(0)) : gfx::Size();
310 void ToastContentsView::GetAccessibleState(ui::AccessibleViewState* state) {
311 if (child_count() > 0)
312 child_at(0)->GetAccessibleState(state);
313 state->role = ui::AccessibilityTypes::ROLE_WINDOW;
316 gfx::Rect ToastContentsView::GetClosedToastBounds(gfx::Rect bounds) {
317 return gfx::Rect(bounds.x() + bounds.width() - kClosedToastWidth,
318 bounds.y(),
319 kClosedToastWidth,
320 bounds.height());
323 } // namespace message_center