OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/message_center/message_popup_bubble.h" | 5 #include "ui/message_center/message_popup_bubble.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/stl_util.h" |
8 #include "ui/message_center/message_view.h" | 9 #include "ui/message_center/message_view.h" |
9 #include "ui/message_center/notification_view.h" | 10 #include "ui/message_center/notification_view.h" |
10 #include "ui/notifications/notification_types.h" | 11 #include "ui/notifications/notification_types.h" |
11 #include "ui/views/bubble/tray_bubble_view.h" | 12 #include "ui/views/bubble/tray_bubble_view.h" |
12 #include "ui/views/layout/box_layout.h" | 13 #include "ui/views/layout/box_layout.h" |
13 #include "ui/views/view.h" | 14 #include "ui/views/view.h" |
14 #include "ui/views/widget/widget.h" | 15 #include "ui/views/widget/widget.h" |
15 | 16 |
16 namespace message_center { | 17 namespace message_center { |
17 namespace { | 18 namespace { |
18 | 19 |
19 const int kAutocloseHighPriorityDelaySeconds = 25; | 20 const int kAutocloseHighPriorityDelaySeconds = 25; |
20 const int kAutocloseDefaultDelaySeconds = 8; | 21 const int kAutocloseDefaultDelaySeconds = 8; |
21 | 22 |
22 std::vector<const NotificationList::Notification*> GetNewNotifications( | |
23 const NotificationList::Notifications& old_list, | |
24 const NotificationList::Notifications& new_list) { | |
25 std::set<std::string> existing_ids; | |
26 std::vector<const NotificationList::Notification*> result; | |
27 for (NotificationList::Notifications::const_iterator iter = old_list.begin(); | |
28 iter != old_list.end(); ++iter) { | |
29 existing_ids.insert(iter->id); | |
30 } | |
31 for (NotificationList::Notifications::const_iterator iter = new_list.begin(); | |
32 iter != new_list.end(); ++iter) { | |
33 if (existing_ids.find(iter->id) == existing_ids.end()) | |
34 result.push_back(&(*iter)); | |
35 } | |
36 return result; | |
37 } | |
38 | |
39 } // namespace | 23 } // namespace |
40 | 24 |
41 // Popup notifications contents. | 25 // Popup notifications contents. |
42 class PopupBubbleContentsView : public views::View { | 26 class PopupBubbleContentsView : public views::View { |
43 public: | 27 public: |
44 explicit PopupBubbleContentsView(NotificationList::Delegate* list_delegate) | 28 explicit PopupBubbleContentsView(NotificationList::Delegate* list_delegate); |
45 : list_delegate_(list_delegate) { | |
46 SetLayoutManager( | |
47 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); | |
48 | 29 |
49 content_ = new views::View; | 30 void Update(const NotificationList::Notifications& popup_notifications); |
50 content_->SetLayoutManager( | |
51 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); | |
52 AddChildView(content_); | |
53 | |
54 if (get_use_acceleration_when_possible()) { | |
55 content_->SetPaintToLayer(true); | |
56 content_->SetFillsBoundsOpaquely(false); | |
57 content_->layer()->SetMasksToBounds(true); | |
58 } | |
59 } | |
60 | |
61 void Update(const NotificationList::Notifications& popup_notifications) { | |
62 content_->RemoveAllChildViews(true); | |
63 for (NotificationList::Notifications::const_iterator iter = | |
64 popup_notifications.begin(); | |
65 iter != popup_notifications.end(); ++iter) { | |
66 MessageView* view = | |
67 NotificationView::ViewForNotification(*iter, list_delegate_); | |
68 view->SetUpView(); | |
69 content_->AddChildView(view); | |
70 } | |
71 content_->SizeToPreferredSize(); | |
72 content_->InvalidateLayout(); | |
73 Layout(); | |
74 if (GetWidget()) | |
75 GetWidget()->GetRootView()->SchedulePaint(); | |
76 } | |
77 | 31 |
78 size_t NumMessageViews() const { | 32 size_t NumMessageViews() const { |
79 return content_->child_count(); | 33 return content_->child_count(); |
80 } | 34 } |
81 | 35 |
82 private: | 36 private: |
83 NotificationList::Delegate* list_delegate_; | 37 NotificationList::Delegate* list_delegate_; |
84 views::View* content_; | 38 views::View* content_; |
85 | 39 |
86 DISALLOW_COPY_AND_ASSIGN(PopupBubbleContentsView); | 40 DISALLOW_COPY_AND_ASSIGN(PopupBubbleContentsView); |
87 }; | 41 }; |
88 | 42 |
| 43 PopupBubbleContentsView::PopupBubbleContentsView( |
| 44 NotificationList::Delegate* list_delegate) |
| 45 : list_delegate_(list_delegate) { |
| 46 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); |
| 47 |
| 48 content_ = new views::View; |
| 49 content_->SetLayoutManager( |
| 50 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); |
| 51 AddChildView(content_); |
| 52 |
| 53 if (get_use_acceleration_when_possible()) { |
| 54 content_->SetPaintToLayer(true); |
| 55 content_->SetFillsBoundsOpaquely(false); |
| 56 content_->layer()->SetMasksToBounds(true); |
| 57 } |
| 58 } |
| 59 |
| 60 void PopupBubbleContentsView::Update( |
| 61 const NotificationList::Notifications& popup_notifications) { |
| 62 content_->RemoveAllChildViews(true); |
| 63 for (NotificationList::Notifications::const_iterator iter = |
| 64 popup_notifications.begin(); |
| 65 iter != popup_notifications.end(); ++iter) { |
| 66 MessageView* view = |
| 67 NotificationView::ViewForNotification(*iter, list_delegate_); |
| 68 view->SetUpView(); |
| 69 content_->AddChildView(view); |
| 70 } |
| 71 content_->SizeToPreferredSize(); |
| 72 content_->InvalidateLayout(); |
| 73 Layout(); |
| 74 if (GetWidget()) |
| 75 GetWidget()->GetRootView()->SchedulePaint(); |
| 76 } |
| 77 |
| 78 // The timer to call OnAutoClose for |notification|. |
| 79 class MessagePopupBubble::AutocloseTimer { |
| 80 public: |
| 81 AutocloseTimer(const NotificationList::Notification& notification, |
| 82 MessagePopupBubble* bubble); |
| 83 |
| 84 void Start(); |
| 85 |
| 86 void Suspend(); |
| 87 |
| 88 private: |
| 89 const std::string id_; |
| 90 base::TimeDelta delay_; |
| 91 base::Time start_time_; |
| 92 MessagePopupBubble* bubble_; |
| 93 base::OneShotTimer<MessagePopupBubble> timer_; |
| 94 |
| 95 DISALLOW_COPY_AND_ASSIGN(AutocloseTimer); |
| 96 }; |
| 97 |
| 98 MessagePopupBubble::AutocloseTimer::AutocloseTimer( |
| 99 const NotificationList::Notification& notification, |
| 100 MessagePopupBubble* bubble) |
| 101 : id_(notification.id), |
| 102 bubble_(bubble) { |
| 103 int seconds = kAutocloseDefaultDelaySeconds; |
| 104 if (notification.priority > ui::notifications::DEFAULT_PRIORITY) |
| 105 seconds = kAutocloseHighPriorityDelaySeconds; |
| 106 delay_ = base::TimeDelta::FromSeconds(seconds); |
| 107 } |
| 108 |
| 109 void MessagePopupBubble::AutocloseTimer::Start() { |
| 110 start_time_ = base::Time::Now(); |
| 111 timer_.Start(FROM_HERE, |
| 112 delay_, |
| 113 base::Bind(&MessagePopupBubble::OnAutoClose, |
| 114 base::Unretained(bubble_), id_)); |
| 115 } |
| 116 |
| 117 void MessagePopupBubble::AutocloseTimer::Suspend() { |
| 118 base::TimeDelta passed = base::Time::Now() - start_time_; |
| 119 delay_ = std::max(base::TimeDelta(), delay_ - passed); |
| 120 timer_.Reset(); |
| 121 } |
| 122 |
89 // MessagePopupBubble | 123 // MessagePopupBubble |
90 MessagePopupBubble::MessagePopupBubble(NotificationList::Delegate* delegate) | 124 MessagePopupBubble::MessagePopupBubble(NotificationList::Delegate* delegate) |
91 : MessageBubbleBase(delegate), | 125 : MessageBubbleBase(delegate), |
92 contents_view_(NULL), | 126 contents_view_(NULL) { |
93 should_run_default_timer_(false), | |
94 should_run_high_timer_(false) { | |
95 } | 127 } |
96 | 128 |
97 MessagePopupBubble::~MessagePopupBubble() { | 129 MessagePopupBubble::~MessagePopupBubble() { |
| 130 STLDeleteContainerPairSecondPointers(autoclose_timers_.begin(), |
| 131 autoclose_timers_.end()); |
98 } | 132 } |
99 | 133 |
100 views::TrayBubbleView::InitParams MessagePopupBubble::GetInitParams( | 134 views::TrayBubbleView::InitParams MessagePopupBubble::GetInitParams( |
101 views::TrayBubbleView::AnchorAlignment anchor_alignment) { | 135 views::TrayBubbleView::AnchorAlignment anchor_alignment) { |
102 views::TrayBubbleView::InitParams init_params = | 136 views::TrayBubbleView::InitParams init_params = |
103 GetDefaultInitParams(anchor_alignment); | 137 GetDefaultInitParams(anchor_alignment); |
104 init_params.arrow_color = kBackgroundColor; | 138 init_params.arrow_color = kBackgroundColor; |
105 init_params.close_on_deactivate = false; | 139 init_params.close_on_deactivate = false; |
106 return init_params; | 140 return init_params; |
107 } | 141 } |
108 | 142 |
109 void MessagePopupBubble::InitializeContents( | 143 void MessagePopupBubble::InitializeContents( |
110 views::TrayBubbleView* new_bubble_view) { | 144 views::TrayBubbleView* new_bubble_view) { |
111 set_bubble_view(new_bubble_view); | 145 set_bubble_view(new_bubble_view); |
112 contents_view_ = new PopupBubbleContentsView(list_delegate()); | 146 contents_view_ = new PopupBubbleContentsView(list_delegate()); |
113 bubble_view()->AddChildView(contents_view_); | 147 bubble_view()->AddChildView(contents_view_); |
114 UpdateBubbleView(); | 148 UpdateBubbleView(); |
115 } | 149 } |
116 | 150 |
117 void MessagePopupBubble::OnBubbleViewDestroyed() { | 151 void MessagePopupBubble::OnBubbleViewDestroyed() { |
118 contents_view_ = NULL; | 152 contents_view_ = NULL; |
119 } | 153 } |
120 | 154 |
121 void MessagePopupBubble::UpdateBubbleView() { | 155 void MessagePopupBubble::UpdateBubbleView() { |
122 NotificationList::Notifications new_notifications; | 156 NotificationList::Notifications popups; |
123 list_delegate()->GetNotificationList()->GetPopupNotifications( | 157 list_delegate()->GetNotificationList()->GetPopupNotifications(&popups); |
124 &new_notifications); | 158 |
125 if (new_notifications.size() == 0) { | 159 if (popups.size() == 0) { |
126 if (bubble_view()) | 160 if (bubble_view()) |
127 bubble_view()->delegate()->HideBubble(bubble_view()); // deletes |this| | 161 bubble_view()->delegate()->HideBubble(bubble_view()); // deletes |this| |
128 return; | 162 return; |
129 } | 163 } |
130 // Only reset the timer when the number of visible notifications changes. | 164 |
131 std::vector<const NotificationList::Notification*> added = | 165 contents_view_->Update(popups); |
132 GetNewNotifications(popup_notifications_, new_notifications); | |
133 bool run_default = false; | |
134 bool run_high = false; | |
135 for (std::vector<const NotificationList::Notification*>::const_iterator iter = | |
136 added.begin(); iter != added.end(); ++iter) { | |
137 if ((*iter)->priority == ui::notifications::DEFAULT_PRIORITY) | |
138 run_default = true; | |
139 else if ((*iter)->priority >= ui::notifications::HIGH_PRIORITY) | |
140 run_high = true; | |
141 } | |
142 // Currently MAX priority is same as HIGH priority. | |
143 if (run_high) | |
144 StartAutoCloseTimer(ui::notifications::MAX_PRIORITY); | |
145 if (run_default) | |
146 StartAutoCloseTimer(ui::notifications::DEFAULT_PRIORITY); | |
147 should_run_high_timer_ = run_high; | |
148 should_run_default_timer_ = run_default; | |
149 contents_view_->Update(new_notifications); | |
150 popup_notifications_.swap(new_notifications); | |
151 bubble_view()->Show(); | 166 bubble_view()->Show(); |
152 bubble_view()->UpdateBubble(); | 167 bubble_view()->UpdateBubble(); |
| 168 |
| 169 std::set<std::string> old_popup_ids; |
| 170 old_popup_ids.swap(popup_ids_); |
| 171 |
| 172 // Start autoclose timers. |
| 173 for (NotificationList::Notifications::const_iterator iter = popups.begin(); |
| 174 iter != popups.end(); ++iter) { |
| 175 std::map<std::string, AutocloseTimer*>::const_iterator timer_iter = |
| 176 autoclose_timers_.find(iter->id); |
| 177 if (timer_iter == autoclose_timers_.end()) { |
| 178 AutocloseTimer *timer = new AutocloseTimer(*iter, this); |
| 179 autoclose_timers_[iter->id] = timer; |
| 180 timer->Start(); |
| 181 } |
| 182 popup_ids_.insert(iter->id); |
| 183 old_popup_ids.erase(iter->id); |
| 184 } |
| 185 |
| 186 // Stops timers whose notifications are gone. |
| 187 for (std::set<std::string>::const_iterator iter = old_popup_ids.begin(); |
| 188 iter != old_popup_ids.end(); ++iter) { |
| 189 DeleteTimer(*iter); |
| 190 } |
153 } | 191 } |
154 | 192 |
155 void MessagePopupBubble::OnMouseEnteredView() { | 193 void MessagePopupBubble::OnMouseEnteredView() { |
156 StopAutoCloseTimer(); | 194 for (std::map<std::string, AutocloseTimer*>::iterator iter = |
| 195 autoclose_timers_.begin(); iter != autoclose_timers_.end(); ++iter) { |
| 196 iter->second->Suspend(); |
| 197 } |
157 } | 198 } |
158 | 199 |
159 void MessagePopupBubble::OnMouseExitedView() { | 200 void MessagePopupBubble::OnMouseExitedView() { |
160 if (should_run_high_timer_) | 201 for (std::map<std::string, AutocloseTimer*>::iterator iter = |
161 StartAutoCloseTimer(ui::notifications::HIGH_PRIORITY); | 202 autoclose_timers_.begin(); iter != autoclose_timers_.end(); ++iter) { |
162 if (should_run_default_timer_) | 203 iter->second->Start(); |
163 StartAutoCloseTimer(ui::notifications::DEFAULT_PRIORITY); | 204 } |
164 } | 205 } |
165 | 206 |
166 void MessagePopupBubble::StartAutoCloseTimer(int priority) { | 207 void MessagePopupBubble::OnAutoClose(const std::string& id) { |
167 base::TimeDelta seconds; | 208 DeleteTimer(id); |
168 base::OneShotTimer<MessagePopupBubble>* timer = NULL; | 209 list_delegate()->GetNotificationList()->MarkSinglePopupAsShown(id, false); |
169 if (priority == ui::notifications::MAX_PRIORITY) { | |
170 seconds = base::TimeDelta::FromSeconds(kAutocloseHighPriorityDelaySeconds); | |
171 timer = &autoclose_high_; | |
172 } else { | |
173 seconds = base::TimeDelta::FromSeconds(kAutocloseDefaultDelaySeconds); | |
174 timer = &autoclose_default_; | |
175 } | |
176 | |
177 timer->Start(FROM_HERE, | |
178 seconds, | |
179 base::Bind(&MessagePopupBubble::OnAutoClose, | |
180 base::Unretained(this), priority)); | |
181 } | |
182 | |
183 void MessagePopupBubble::StopAutoCloseTimer() { | |
184 autoclose_high_.Stop(); | |
185 autoclose_default_.Stop(); | |
186 } | |
187 | |
188 void MessagePopupBubble::OnAutoClose(int priority) { | |
189 list_delegate()->GetNotificationList()->MarkPopupsAsShown(priority); | |
190 if (priority == ui::notifications::MAX_PRIORITY) | |
191 list_delegate()->GetNotificationList()->MarkPopupsAsShown( | |
192 ui::notifications::HIGH_PRIORITY); | |
193 | |
194 if (priority >= ui::notifications::MAX_PRIORITY) | |
195 should_run_high_timer_ = false; | |
196 else | |
197 should_run_default_timer_ = false; | |
198 UpdateBubbleView(); | 210 UpdateBubbleView(); |
199 } | 211 } |
200 | 212 |
| 213 void MessagePopupBubble::DeleteTimer(const std::string& id) { |
| 214 std::map<std::string, AutocloseTimer*>::iterator iter = |
| 215 autoclose_timers_.find(id); |
| 216 if (iter != autoclose_timers_.end()) { |
| 217 scoped_ptr<AutocloseTimer> release_timer(iter->second); |
| 218 autoclose_timers_.erase(iter); |
| 219 } |
| 220 } |
| 221 |
201 size_t MessagePopupBubble::NumMessageViewsForTest() const { | 222 size_t MessagePopupBubble::NumMessageViewsForTest() const { |
202 return contents_view_->NumMessageViews(); | 223 return contents_view_->NumMessageViews(); |
203 } | 224 } |
204 | 225 |
205 } // namespace message_center | 226 } // namespace message_center |
OLD | NEW |