OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "chrome/browser/notifications/balloon_collection_impl.h" | 5 #include "chrome/browser/notifications/balloon_collection_impl.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
10 #include "chrome/browser/notifications/balloon.h" | 10 #include "chrome/browser/notifications/balloon.h" |
11 #include "chrome/browser/notifications/balloon_host.h" | 11 #include "chrome/browser/notifications/balloon_host.h" |
12 #include "chrome/browser/notifications/notification.h" | 12 #include "chrome/browser/notifications/notification.h" |
| 13 #include "chrome/browser/ui/browser.h" |
| 14 #include "chrome/browser/ui/panels/panel_manager.h" |
| 15 #include "chrome/browser/ui/panels/panel.h" |
13 #include "chrome/browser/ui/window_sizer.h" | 16 #include "chrome/browser/ui/window_sizer.h" |
| 17 #include "chrome/common/chrome_notification_types.h" |
| 18 #include "content/public/browser/notification_registrar.h" |
| 19 #include "content/public/browser/notification_service.h" |
14 #include "ui/gfx/rect.h" | 20 #include "ui/gfx/rect.h" |
15 #include "ui/gfx/size.h" | 21 #include "ui/gfx/size.h" |
16 | 22 |
17 namespace { | 23 namespace { |
18 | 24 |
19 // Portion of the screen allotted for notifications. When notification balloons | 25 // Portion of the screen allotted for notifications. When notification balloons |
20 // extend over this, no new notifications are shown until some are closed. | 26 // extend over this, no new notifications are shown until some are closed. |
21 const double kPercentBalloonFillFactor = 0.7; | 27 const double kPercentBalloonFillFactor = 0.7; |
22 | 28 |
23 // Allow at least this number of balloons on the screen. | 29 // Allow at least this number of balloons on the screen. |
24 const int kMinAllowedBalloonCount = 2; | 30 const int kMinAllowedBalloonCount = 2; |
25 | 31 |
26 // Delay from the mouse leaving the balloon collection before | 32 // Delay from the mouse leaving the balloon collection before |
27 // there is a relayout, in milliseconds. | 33 // there is a relayout, in milliseconds. |
28 const int kRepositionDelay = 300; | 34 const int kRepositionDelay = 300; |
29 | 35 |
30 } // namespace | 36 } // namespace |
31 | 37 |
32 BalloonCollectionImpl::BalloonCollectionImpl() | 38 BalloonCollectionImpl::BalloonCollectionImpl() |
33 #if USE_OFFSETS | 39 #if USE_OFFSETS |
34 : ALLOW_THIS_IN_INITIALIZER_LIST(reposition_factory_(this)), | 40 : ALLOW_THIS_IN_INITIALIZER_LIST(reposition_factory_(this)), |
35 added_as_message_loop_observer_(false) | 41 added_as_message_loop_observer_(false) |
36 #endif | 42 #endif |
37 { | 43 { |
| 44 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY, |
| 45 content::NotificationService::AllSources()); |
| 46 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED, |
| 47 content::NotificationService::AllSources()); |
| 48 registrar_.Add(this, chrome::NOTIFICATION_PANEL_CHANGED_BOUNDS, |
| 49 content::NotificationService::AllSources()); |
38 | 50 |
39 SetPositionPreference(BalloonCollection::DEFAULT_POSITION); | 51 SetPositionPreference(BalloonCollection::DEFAULT_POSITION); |
40 } | 52 } |
41 | 53 |
42 BalloonCollectionImpl::~BalloonCollectionImpl() { | 54 BalloonCollectionImpl::~BalloonCollectionImpl() { |
43 } | 55 } |
44 | 56 |
45 void BalloonCollectionImpl::Add(const Notification& notification, | 57 void BalloonCollectionImpl::Add(const Notification& notification, |
46 Profile* profile) { | 58 Profile* profile) { |
47 Balloon* new_balloon = MakeBalloon(notification, profile); | 59 Balloon* new_balloon = MakeBalloon(notification, profile); |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
146 | 158 |
147 // This is used only for testing. | 159 // This is used only for testing. |
148 if (!on_collection_changed_callback_.is_null()) | 160 if (!on_collection_changed_callback_.is_null()) |
149 on_collection_changed_callback_.Run(); | 161 on_collection_changed_callback_.Run(); |
150 } | 162 } |
151 | 163 |
152 const BalloonCollection::Balloons& BalloonCollectionImpl::GetActiveBalloons() { | 164 const BalloonCollection::Balloons& BalloonCollectionImpl::GetActiveBalloons() { |
153 return base_.balloons(); | 165 return base_.balloons(); |
154 } | 166 } |
155 | 167 |
| 168 void BalloonCollectionImpl::Observe( |
| 169 int type, |
| 170 const content::NotificationSource& source, |
| 171 const content::NotificationDetails& details) { |
| 172 switch (type) { |
| 173 case chrome::NOTIFICATION_BROWSER_WINDOW_READY: |
| 174 case chrome::NOTIFICATION_BROWSER_CLOSED: { |
| 175 Browser* browser = content::Source<Browser>(source).ptr(); |
| 176 if (browser->is_type_panel()) |
| 177 PositionBalloons(true); |
| 178 break; |
| 179 } |
| 180 case chrome::NOTIFICATION_PANEL_CHANGED_BOUNDS: |
| 181 PositionBalloons(true); |
| 182 break; |
| 183 default: |
| 184 NOTREACHED(); |
| 185 break; |
| 186 } |
| 187 } |
| 188 |
156 void BalloonCollectionImpl::PositionBalloonsInternal(bool reposition) { | 189 void BalloonCollectionImpl::PositionBalloonsInternal(bool reposition) { |
157 const Balloons& balloons = base_.balloons(); | 190 const Balloons& balloons = base_.balloons(); |
158 | 191 |
159 layout_.RefreshSystemMetrics(); | 192 layout_.RefreshSystemMetrics(); |
160 gfx::Point origin = layout_.GetLayoutOrigin(); | 193 gfx::Point origin = layout_.GetLayoutOrigin(); |
161 for (Balloons::const_iterator it = balloons.begin(); | 194 for (Balloons::const_iterator it = balloons.begin(); |
162 it != balloons.end(); | 195 it != balloons.end(); |
163 ++it) { | 196 ++it) { |
164 gfx::Point upper_left = layout_.NextPosition((*it)->GetViewSize(), &origin); | 197 gfx::Point upper_left = layout_.NextPosition((*it)->GetViewSize(), &origin); |
165 (*it)->SetPosition(upper_left, reposition); | 198 (*it)->SetPosition(upper_left, reposition); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
224 reposition_factory_.GetWeakPtr()), | 257 reposition_factory_.GetWeakPtr()), |
225 kRepositionDelay); | 258 kRepositionDelay); |
226 } | 259 } |
227 } else { | 260 } else { |
228 // Mouse moved back into the region. Cancel the reposition. | 261 // Mouse moved back into the region. Cancel the reposition. |
229 reposition_factory_.InvalidateWeakPtrs(); | 262 reposition_factory_.InvalidateWeakPtrs(); |
230 } | 263 } |
231 } | 264 } |
232 #endif | 265 #endif |
233 | 266 |
234 BalloonCollectionImpl::Layout::Layout() : placement_(INVALID) { | 267 BalloonCollectionImpl::Layout::Layout() |
| 268 : placement_(INVALID), |
| 269 bottom_left_offset_to_move_above_panels_(0), |
| 270 bottom_right_offset_to_move_above_panels_(0) { |
235 RefreshSystemMetrics(); | 271 RefreshSystemMetrics(); |
236 } | 272 } |
237 | 273 |
238 void BalloonCollectionImpl::Layout::GetMaxLinearSize(int* max_balloon_size, | 274 void BalloonCollectionImpl::Layout::GetMaxLinearSize(int* max_balloon_size, |
239 int* total_size) const { | 275 int* total_size) const { |
240 DCHECK(max_balloon_size && total_size); | 276 DCHECK(max_balloon_size && total_size); |
241 | 277 |
242 // All placement schemes are vertical, so we only care about height. | 278 // All placement schemes are vertical, so we only care about height. |
243 *total_size = work_area_.height(); | 279 *total_size = work_area_.height(); |
244 *max_balloon_size = max_balloon_height(); | 280 *max_balloon_size = max_balloon_height(); |
245 } | 281 } |
246 | 282 |
247 gfx::Point BalloonCollectionImpl::Layout::GetLayoutOrigin() const { | 283 gfx::Point BalloonCollectionImpl::Layout::GetLayoutOrigin() const { |
248 int x = 0; | 284 int x = 0; |
249 int y = 0; | 285 int y = 0; |
250 switch (placement_) { | 286 switch (placement_) { |
251 case VERTICALLY_FROM_TOP_LEFT: | 287 case VERTICALLY_FROM_TOP_LEFT: |
252 x = work_area_.x() + HorizontalEdgeMargin(); | 288 x = work_area_.x() + HorizontalEdgeMargin(); |
253 y = work_area_.y() + VerticalEdgeMargin(); | 289 y = work_area_.y() + VerticalEdgeMargin(); |
254 break; | 290 break; |
255 case VERTICALLY_FROM_TOP_RIGHT: | 291 case VERTICALLY_FROM_TOP_RIGHT: |
256 x = work_area_.right() - HorizontalEdgeMargin(); | 292 x = work_area_.right() - HorizontalEdgeMargin(); |
257 y = work_area_.y() + VerticalEdgeMargin(); | 293 y = work_area_.y() + VerticalEdgeMargin(); |
258 break; | 294 break; |
259 case VERTICALLY_FROM_BOTTOM_LEFT: | 295 case VERTICALLY_FROM_BOTTOM_LEFT: |
260 x = work_area_.x() + HorizontalEdgeMargin(); | 296 x = work_area_.x() + HorizontalEdgeMargin(); |
261 y = work_area_.bottom() - VerticalEdgeMargin(); | 297 // For this placement, balloon needs to stay on top of left-most panels |
| 298 // to avoid overlapping. |
| 299 y = work_area_.bottom() - VerticalEdgeMargin() - |
| 300 bottom_left_offset_to_move_above_panels_; |
262 break; | 301 break; |
263 case VERTICALLY_FROM_BOTTOM_RIGHT: | 302 case VERTICALLY_FROM_BOTTOM_RIGHT: |
264 x = work_area_.right() - HorizontalEdgeMargin(); | 303 x = work_area_.right() - HorizontalEdgeMargin(); |
265 y = work_area_.bottom() - VerticalEdgeMargin(); | 304 // For this placement, balloon needs to stay on top of right-most panels |
| 305 // to avoid overlapping. |
| 306 y = work_area_.bottom() - VerticalEdgeMargin() - |
| 307 bottom_right_offset_to_move_above_panels_; |
266 break; | 308 break; |
267 default: | 309 default: |
268 NOTREACHED(); | 310 NOTREACHED(); |
269 break; | 311 break; |
270 } | 312 } |
271 return gfx::Point(x, y); | 313 return gfx::Point(x, y); |
272 } | 314 } |
273 | 315 |
274 gfx::Point BalloonCollectionImpl::Layout::NextPosition( | 316 gfx::Point BalloonCollectionImpl::Layout::NextPosition( |
275 const gfx::Size& balloon_size, | 317 const gfx::Size& balloon_size, |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
356 gfx::Size BalloonCollectionImpl::Layout::ConstrainToSizeLimits( | 398 gfx::Size BalloonCollectionImpl::Layout::ConstrainToSizeLimits( |
357 const gfx::Size& size) { | 399 const gfx::Size& size) { |
358 // restrict to the min & max sizes | 400 // restrict to the min & max sizes |
359 return gfx::Size( | 401 return gfx::Size( |
360 std::max(min_balloon_width(), | 402 std::max(min_balloon_width(), |
361 std::min(max_balloon_width(), size.width())), | 403 std::min(max_balloon_width(), size.width())), |
362 std::max(min_balloon_height(), | 404 std::max(min_balloon_height(), |
363 std::min(max_balloon_height(), size.height()))); | 405 std::min(max_balloon_height(), size.height()))); |
364 } | 406 } |
365 | 407 |
| 408 void BalloonCollectionImpl::Layout::ComputeOffsetsToMoveAbovePanels() { |
| 409 const PanelManager::Panels& panels = PanelManager::GetInstance()->panels(); |
| 410 |
| 411 // For balloons that are aligned vertically from bottom-right corner, the |
| 412 // offset is the maximum height of the right-most panels that could overlap |
| 413 // with the balloons. |
| 414 bottom_right_offset_to_move_above_panels_ = 0; |
| 415 for (PanelManager::Panels::const_iterator iter = panels.begin(); |
| 416 iter != panels.end(); ++iter) { |
| 417 if ((*iter)->GetBounds().right() <= |
| 418 work_area_.right() - max_balloon_width()) |
| 419 break; |
| 420 int current_height = (*iter)->GetBounds().height(); |
| 421 if (current_height > bottom_right_offset_to_move_above_panels_) |
| 422 bottom_right_offset_to_move_above_panels_ = current_height; |
| 423 } |
| 424 |
| 425 // For balloons that are aligned vertically from bottom-left corner, the |
| 426 // offset is the maximum height of the left-most panels that could overlap |
| 427 // with the balloons. |
| 428 bottom_left_offset_to_move_above_panels_ = 0; |
| 429 for (PanelManager::Panels::const_reverse_iterator iter = panels.rbegin(); |
| 430 iter != panels.rend(); ++iter) { |
| 431 if ((*iter)->GetBounds().x() > work_area_.x() + max_balloon_width()) |
| 432 break; |
| 433 int current_height = (*iter)->GetBounds().height(); |
| 434 if (current_height > bottom_left_offset_to_move_above_panels_) |
| 435 bottom_left_offset_to_move_above_panels_ = current_height; |
| 436 } |
| 437 } |
| 438 |
366 bool BalloonCollectionImpl::Layout::RefreshSystemMetrics() { | 439 bool BalloonCollectionImpl::Layout::RefreshSystemMetrics() { |
367 bool changed = false; | 440 bool changed = false; |
368 | 441 |
369 #if defined(OS_MACOSX) | 442 #if defined(OS_MACOSX) |
370 gfx::Rect new_work_area = GetMacWorkArea(); | 443 gfx::Rect new_work_area = GetMacWorkArea(); |
371 #else | 444 #else |
372 scoped_ptr<WindowSizer::MonitorInfoProvider> info_provider( | 445 scoped_ptr<WindowSizer::MonitorInfoProvider> info_provider( |
373 WindowSizer::CreateDefaultMonitorInfoProvider()); | 446 WindowSizer::CreateDefaultMonitorInfoProvider()); |
374 gfx::Rect new_work_area = info_provider->GetPrimaryMonitorWorkArea(); | 447 gfx::Rect new_work_area = info_provider->GetPrimaryMonitorWorkArea(); |
375 #endif | 448 #endif |
376 if (!work_area_.Equals(new_work_area)) { | 449 if (!work_area_.Equals(new_work_area)) { |
377 work_area_.SetRect(new_work_area.x(), new_work_area.y(), | 450 work_area_.SetRect(new_work_area.x(), new_work_area.y(), |
378 new_work_area.width(), new_work_area.height()); | 451 new_work_area.width(), new_work_area.height()); |
379 changed = true; | 452 changed = true; |
380 } | 453 } |
381 | 454 |
| 455 ComputeOffsetsToMoveAbovePanels(); |
| 456 |
382 return changed; | 457 return changed; |
383 } | 458 } |
OLD | NEW |