Revert of Build the Clang plugin on Windows. (patchset #6 id:100001 of https://codere...
[chromium-blink-merge.git] / ui / message_center / views / toast_contents_view.cc
blobbd98d032dcdd0a4b626667ba971e6aaa5aff3f05
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 "ui/accessibility/ax_view_state.h"
13 #include "ui/gfx/animation/animation_delegate.h"
14 #include "ui/gfx/animation/slide_animation.h"
15 #include "ui/gfx/display.h"
16 #include "ui/gfx/screen.h"
17 #include "ui/message_center/message_center_style.h"
18 #include "ui/message_center/notification.h"
19 #include "ui/message_center/views/message_popup_collection.h"
20 #include "ui/message_center/views/message_view.h"
21 #include "ui/views/background.h"
22 #include "ui/views/view.h"
23 #include "ui/views/widget/widget.h"
24 #include "ui/views/widget/widget_delegate.h"
26 #if defined(OS_WIN)
27 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
28 #endif
30 using gfx::Screen;
32 namespace message_center {
33 namespace {
35 // The width of a toast before animated reveal and after closing.
36 const int kClosedToastWidth = 5;
38 // FadeIn/Out look a bit better if they are slightly longer then default slide.
39 const int kFadeInOutDuration = 200;
41 } // namespace.
43 // static
44 gfx::Size ToastContentsView::GetToastSizeForView(const views::View* view) {
45 int width = kNotificationWidth + view->GetInsets().width();
46 return gfx::Size(width, view->GetHeightForWidth(width));
49 ToastContentsView::ToastContentsView(
50 const std::string& notification_id,
51 base::WeakPtr<MessagePopupCollection> collection)
52 : collection_(collection),
53 id_(notification_id),
54 is_closing_(false),
55 closing_animation_(NULL) {
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);
65 CreateWidget(collection->parent());
68 // This is destroyed when the toast window closes.
69 ToastContentsView::~ToastContentsView() {
70 if (collection_)
71 collection_->ForgetToast(this);
74 void ToastContentsView::SetContents(MessageView* view,
75 bool a11y_feedback_for_updates) {
76 bool already_has_contents = child_count() > 0;
77 RemoveAllChildViews(true);
78 AddChildView(view);
79 preferred_size_ = GetToastSizeForView(view);
80 Layout();
82 // If it has the contents already, this invocation means an update of the
83 // popup toast, and the new contents should be read through a11y feature.
84 // The notification type should be ALERT, otherwise the accessibility message
85 // won't be read for this view which returns ROLE_WINDOW.
86 if (already_has_contents && a11y_feedback_for_updates)
87 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, false);
90 void ToastContentsView::UpdateContents(const Notification& notification,
91 bool a11y_feedback_for_updates) {
92 DCHECK_GT(child_count(), 0);
93 MessageView* message_view = static_cast<MessageView*>(child_at(0));
94 message_view->UpdateWithNotification(notification);
95 if (a11y_feedback_for_updates)
96 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, false);
99 void ToastContentsView::RevealWithAnimation(gfx::Point origin) {
100 // Place/move the toast widgets. Currently it stacks the widgets from the
101 // right-bottom of the work area.
102 // TODO(mukai): allow to specify the placement policy from outside of this
103 // class. The policy should be specified from preference on Windows, or
104 // the launcher alignment on ChromeOS.
105 origin_ = gfx::Point(origin.x() - preferred_size_.width(),
106 origin.y() - preferred_size_.height());
108 gfx::Rect stable_bounds(origin_, preferred_size_);
110 SetBoundsInstantly(GetClosedToastBounds(stable_bounds));
111 StartFadeIn();
112 SetBoundsWithAnimation(stable_bounds);
115 void ToastContentsView::CloseWithAnimation() {
116 if (is_closing_)
117 return;
118 is_closing_ = true;
119 StartFadeOut();
122 void ToastContentsView::SetBoundsInstantly(gfx::Rect new_bounds) {
123 if (new_bounds == bounds())
124 return;
126 origin_ = new_bounds.origin();
127 if (!GetWidget())
128 return;
129 GetWidget()->SetBounds(new_bounds);
132 void ToastContentsView::SetBoundsWithAnimation(gfx::Rect new_bounds) {
133 if (new_bounds == bounds())
134 return;
136 origin_ = new_bounds.origin();
137 if (!GetWidget())
138 return;
140 // This picks up the current bounds, so if there was a previous animation
141 // half-done, the next one will pick up from the current location.
142 // This is the only place that should query current location of the Widget
143 // on screen, the rest should refer to the bounds_.
144 animated_bounds_start_ = GetWidget()->GetWindowBoundsInScreen();
145 animated_bounds_end_ = new_bounds;
147 if (collection_)
148 collection_->IncrementDeferCounter();
150 if (bounds_animation_.get())
151 bounds_animation_->Stop();
153 bounds_animation_.reset(new gfx::SlideAnimation(this));
154 bounds_animation_->Show();
157 void ToastContentsView::StartFadeIn() {
158 // The decrement is done in OnBoundsAnimationEndedOrCancelled callback.
159 if (collection_)
160 collection_->IncrementDeferCounter();
161 fade_animation_->Stop();
163 GetWidget()->SetOpacity(0);
164 GetWidget()->ShowInactive();
165 fade_animation_->Reset(0);
166 fade_animation_->Show();
169 void ToastContentsView::StartFadeOut() {
170 // The decrement is done in OnBoundsAnimationEndedOrCancelled callback.
171 if (collection_)
172 collection_->IncrementDeferCounter();
173 fade_animation_->Stop();
175 closing_animation_ = (is_closing_ ? fade_animation_.get() : NULL);
176 fade_animation_->Reset(1);
177 fade_animation_->Hide();
180 void ToastContentsView::OnBoundsAnimationEndedOrCancelled(
181 const gfx::Animation* animation) {
182 if (is_closing_ && closing_animation_ == animation && GetWidget()) {
183 views::Widget* widget = GetWidget();
185 // TODO(dewittj): This is a workaround to prevent a nasty bug where
186 // closing a transparent widget doesn't actually remove the window,
187 // causing entire areas of the screen to become unresponsive to clicks.
188 // See crbug.com/243469
189 widget->Hide();
190 #if defined(OS_WIN)
191 widget->SetOpacity(0xFF);
192 #endif
194 widget->Close();
197 // This cannot be called before GetWidget()->Close(). Decrementing defer count
198 // will invoke update, which may invoke another close animation with
199 // incrementing defer counter. Close() after such process will cause a
200 // mismatch between increment/decrement. See crbug.com/238477
201 if (collection_)
202 collection_->DecrementDeferCounter();
205 // gfx::AnimationDelegate
206 void ToastContentsView::AnimationProgressed(const gfx::Animation* animation) {
207 if (animation == bounds_animation_.get()) {
208 gfx::Rect current(animation->CurrentValueBetween(
209 animated_bounds_start_, animated_bounds_end_));
210 GetWidget()->SetBounds(current);
211 } else if (animation == fade_animation_.get()) {
212 unsigned char opacity =
213 static_cast<unsigned char>(fade_animation_->GetCurrentValue() * 255);
214 GetWidget()->SetOpacity(opacity);
218 void ToastContentsView::AnimationEnded(const gfx::Animation* animation) {
219 OnBoundsAnimationEndedOrCancelled(animation);
222 void ToastContentsView::AnimationCanceled(
223 const gfx::Animation* animation) {
224 OnBoundsAnimationEndedOrCancelled(animation);
227 // views::WidgetDelegate
228 views::View* ToastContentsView::GetContentsView() {
229 return this;
232 void ToastContentsView::WindowClosing() {
233 if (!is_closing_ && collection_.get())
234 collection_->ForgetToast(this);
237 void ToastContentsView::OnDisplayChanged() {
238 views::Widget* widget = GetWidget();
239 if (!widget)
240 return;
242 gfx::NativeView native_view = widget->GetNativeView();
243 if (!native_view || !collection_.get())
244 return;
246 collection_->OnDisplayMetricsChanged(
247 Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view));
250 void ToastContentsView::OnWorkAreaChanged() {
251 views::Widget* widget = GetWidget();
252 if (!widget)
253 return;
255 gfx::NativeView native_view = widget->GetNativeView();
256 if (!native_view || !collection_.get())
257 return;
259 collection_->OnDisplayMetricsChanged(
260 Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view));
263 // views::View
264 void ToastContentsView::OnMouseEntered(const ui::MouseEvent& event) {
265 if (collection_)
266 collection_->OnMouseEntered(this);
269 void ToastContentsView::OnMouseExited(const ui::MouseEvent& event) {
270 if (collection_)
271 collection_->OnMouseExited(this);
274 void ToastContentsView::Layout() {
275 if (child_count() > 0) {
276 child_at(0)->SetBounds(
277 0, 0, preferred_size_.width(), preferred_size_.height());
281 gfx::Size ToastContentsView::GetPreferredSize() const {
282 return child_count() ? GetToastSizeForView(child_at(0)) : gfx::Size();
285 void ToastContentsView::GetAccessibleState(ui::AXViewState* state) {
286 if (child_count() > 0)
287 child_at(0)->GetAccessibleState(state);
288 state->role = ui::AX_ROLE_WINDOW;
291 void ToastContentsView::ClickOnNotification(
292 const std::string& notification_id) {
293 if (collection_)
294 collection_->ClickOnNotification(notification_id);
297 void ToastContentsView::RemoveNotification(
298 const std::string& notification_id,
299 bool by_user) {
300 if (collection_)
301 collection_->RemoveNotification(notification_id, by_user);
304 scoped_ptr<ui::MenuModel> ToastContentsView::CreateMenuModel(
305 const NotifierId& notifier_id,
306 const base::string16& display_source) {
307 // Should not reach, the context menu should be handled in
308 // MessagePopupCollection.
309 NOTREACHED();
310 return nullptr;
313 bool ToastContentsView::HasClickedListener(
314 const std::string& notification_id) {
315 if (!collection_)
316 return false;
317 return collection_->HasClickedListener(notification_id);
320 void ToastContentsView::ClickOnNotificationButton(
321 const std::string& notification_id,
322 int button_index) {
323 if (collection_)
324 collection_->ClickOnNotificationButton(notification_id, button_index);
327 void ToastContentsView::CreateWidget(gfx::NativeView parent) {
328 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
329 params.keep_on_top = true;
330 if (parent)
331 params.parent = parent;
332 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
333 params.delegate = this;
334 views::Widget* widget = new views::Widget();
335 widget->set_focus_on_creation(false);
337 #if defined(OS_WIN)
338 // We want to ensure that this toast always goes to the native desktop,
339 // not the Ash desktop (since there is already another toast contents view
340 // there.
341 if (!params.parent)
342 params.native_widget = new views::DesktopNativeWidgetAura(widget);
343 #endif
345 widget->Init(params);
348 gfx::Rect ToastContentsView::GetClosedToastBounds(gfx::Rect bounds) {
349 return gfx::Rect(bounds.x() + bounds.width() - kClosedToastWidth,
350 bounds.y(),
351 kClosedToastWidth,
352 bounds.height());
355 } // namespace message_center