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