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 #ifndef UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_COLLECTION_H_
6 #define UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_COLLECTION_H_
11 #include "base/compiler_specific.h"
12 #include "base/gtest_prod_util.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/timer/timer.h"
15 #include "ui/gfx/display.h"
16 #include "ui/gfx/display_observer.h"
17 #include "ui/gfx/native_widget_types.h"
18 #include "ui/gfx/rect.h"
19 #include "ui/message_center/message_center_export.h"
20 #include "ui/message_center/message_center_observer.h"
21 #include "ui/message_center/views/message_center_controller.h"
22 #include "ui/message_center/views/toast_contents_view.h"
23 #include "ui/views/widget/widget_observer.h"
34 class WebNotificationTrayTest
;
35 FORWARD_DECLARE_TEST(WebNotificationTrayTest
, ManyPopupNotifications
);
38 namespace message_center
{
40 class MessagePopupCollectionTest
;
44 class MessageCenterTray
;
45 class MessageViewContextMenuController
;
48 POPUP_ALIGNMENT_TOP
= 1 << 0,
49 POPUP_ALIGNMENT_LEFT
= 1 << 1,
50 POPUP_ALIGNMENT_BOTTOM
= 1 << 2,
51 POPUP_ALIGNMENT_RIGHT
= 1 << 3,
54 // Container for popup toasts. Because each toast is a frameless window rather
55 // than a view in a bubble, now the container just manages all of those toasts.
56 // This is similar to chrome/browser/notifications/balloon_collection, but the
57 // contents of each toast are for the message center and layout strategy would
58 // be slightly different.
59 class MESSAGE_CENTER_EXPORT MessagePopupCollection
60 : public MessageCenterController
,
61 public MessageCenterObserver
,
62 public gfx::DisplayObserver
{
64 // |parent| specifies the parent widget of the toast windows. The default
65 // parent will be used for NULL. Usually each icon is spacing against its
66 // predecessor. If |first_item_has_no_margin| is set however the first item
67 // does not space against the tray.
68 MessagePopupCollection(gfx::NativeView parent
,
69 MessageCenter
* message_center
,
70 MessageCenterTray
* tray
,
71 bool first_item_has_no_margin
);
72 virtual ~MessagePopupCollection();
74 // Overridden from MessageCenterController:
75 virtual void ClickOnNotification(const std::string
& notification_id
) OVERRIDE
;
76 virtual void RemoveNotification(const std::string
& notification_id
,
77 bool by_user
) OVERRIDE
;
78 virtual scoped_ptr
<ui::MenuModel
> CreateMenuModel(
79 const NotifierId
& notifier_id
,
80 const base::string16
& display_source
) OVERRIDE
;
81 virtual bool HasClickedListener(const std::string
& notification_id
) OVERRIDE
;
82 virtual void ClickOnNotificationButton(const std::string
& notification_id
,
83 int button_index
) OVERRIDE
;
84 virtual void ExpandNotification(const std::string
& notification_id
) OVERRIDE
;
86 void MarkAllPopupsShown();
88 // Since these events are really coming from individual toast widgets,
89 // it helps to be able to keep track of the sender.
90 void OnMouseEntered(ToastContentsView
* toast_entered
);
91 void OnMouseExited(ToastContentsView
* toast_exited
);
93 // Invoked by toasts when they start/finish their animations.
94 // While "defer counter" is greater then zero, the popup collection does
95 // not perform updates. It is used to wait for various animations and user
96 // actions like serial closing of the toasts, when the remaining toasts "flow
98 void IncrementDeferCounter();
99 void DecrementDeferCounter();
101 // Runs the next step in update/animate sequence, if the defer counter is not
102 // zero. Otherwise, simply waits when it becomes zero.
103 void DoUpdateIfPossible();
105 // Removes the toast from our internal list of toasts; this is called when the
106 // toast is irrevocably closed (such as within RemoveToast).
107 void ForgetToast(ToastContentsView
* toast
);
109 // Updates |work_area_| and re-calculates the alignment of notification toasts
110 // rearranging them if necessary.
111 // This is separated from methods from OnDisplayBoundsChanged(), since
112 // sometimes the display info has to be specified directly. One example is
113 // shelf's auto-hide change. When the shelf in ChromeOS is temporarily shown
114 // from auto hide status, it doesn't change the display's work area but the
115 // actual work area for toasts should be resized.
116 void SetDisplayInfo(const gfx::Rect
& work_area
,
117 const gfx::Rect
& screen_bounds
);
119 // Overridden from gfx::DislayObserver:
120 virtual void OnDisplayBoundsChanged(const gfx::Display
& display
) OVERRIDE
;
121 virtual void OnDisplayAdded(const gfx::Display
& new_display
) OVERRIDE
;
122 virtual void OnDisplayRemoved(const gfx::Display
& old_display
) OVERRIDE
;
124 // Used by ToastContentsView to locate itself.
125 gfx::NativeView
parent() const { return parent_
; }
128 FRIEND_TEST_ALL_PREFIXES(ash::WebNotificationTrayTest
,
129 ManyPopupNotifications
);
130 friend class test::MessagePopupCollectionTest
;
131 friend class ash::WebNotificationTrayTest
;
132 typedef std::list
<ToastContentsView
*> Toasts
;
134 // Iterates toasts and starts closing them.
135 std::set
<std::string
> CloseAllWidgets();
137 // Called by ToastContentsView when its window is closed.
138 void RemoveToast(ToastContentsView
* toast
, bool mark_as_shown
);
140 // Returns the x-origin for the given toast bounds in the current work area.
141 int GetToastOriginX(const gfx::Rect
& toast_bounds
) const;
143 // Creates new widgets for new toast notifications, and updates |toasts_| and
144 // |widgets_| correctly.
145 void UpdateWidgets();
147 // Repositions all of the widgets based on the current work area.
148 void RepositionWidgets();
150 // Repositions widgets to the top edge of the notification toast that was
151 // just removed, so that the user can click close button without mouse moves.
152 // See crbug.com/224089
153 void RepositionWidgetsWithTarget();
155 void ComputePopupAlignment(gfx::Rect work_area
, gfx::Rect screen_bounds
);
157 // The base line is an (imaginary) line that would touch the bottom of the
158 // next created notification if bottom-aligned or its top if top-aligned.
159 int GetBaseLine(ToastContentsView
* last_toast
) const;
161 // Overridden from MessageCenterObserver:
162 virtual void OnNotificationAdded(const std::string
& notification_id
) OVERRIDE
;
163 virtual void OnNotificationRemoved(const std::string
& notification_id
,
164 bool by_user
) OVERRIDE
;
165 virtual void OnNotificationUpdated(
166 const std::string
& notification_id
) OVERRIDE
;
168 ToastContentsView
* FindToast(const std::string
& notification_id
) const;
170 // While the toasts are animated, avoid updating the collection, to reduce
171 // user confusion. Instead, update the collection when all animations are
172 // done. This method is run when defer counter is zero, may initiate next
173 // update/animation step.
174 void OnDeferTimerExpired();
176 // "ForTest" methods.
177 views::Widget
* GetWidgetForTest(const std::string
& id
) const;
178 void CreateRunLoopForTest();
180 gfx::Rect
GetToastRectAt(size_t index
) const;
182 gfx::NativeView parent_
;
183 MessageCenter
* message_center_
;
184 MessageCenterTray
* tray_
;
186 gfx::Rect work_area_
;
189 // Specifies which corner of the screen popups should show up. This should
190 // ideally be the same corner the notification area (systray) is at.
191 PopupAlignment alignment_
;
195 // This is only used to compare with incoming events, do not assume that
196 // the toast will be valid if this pointer is non-NULL.
197 ToastContentsView
* latest_toast_entered_
;
199 // Denotes a mode when user is clicking the Close button of toasts in a
200 // sequence, w/o moving the mouse. We reposition the toasts so the next one
201 // happens to be right under the mouse, and the user can just dispose of
202 // multipel toasts by clicking. The mode ends when defer_timer_ expires.
203 bool user_is_closing_toasts_by_clicking_
;
204 scoped_ptr
<base::OneShotTimer
<MessagePopupCollection
> > defer_timer_
;
205 // The top edge to align the position of the next toast during 'close by
207 // Only to be used when user_is_closing_toasts_by_clicking_ is true.
208 int target_top_edge_
;
210 // Weak, only exists temporarily in tests.
211 scoped_ptr
<base::RunLoop
> run_loop_for_test_
;
213 // True if the first item should not have spacing against the tray.
214 bool first_item_has_no_margin_
;
216 scoped_ptr
<MessageViewContextMenuController
> context_menu_controller_
;
218 // Gives out weak pointers to toast contents views which have an unrelated
219 // lifetime. Must remain the last member variable.
220 base::WeakPtrFactory
<MessagePopupCollection
> weak_factory_
;
222 DISALLOW_COPY_AND_ASSIGN(MessagePopupCollection
);
225 } // namespace message_center
227 #endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_COLLECTION_H_