Chromium Code Reviews| 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, | |
|
jennb
2011/11/10 19:17:13
Instead of these browser notifications, perhaps be
jianli
2011/11/11 00:38:30
Done.
| |
| 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 Browser* closed_browser = NULL; | |
| 173 switch (type) { | |
| 174 case chrome::NOTIFICATION_BROWSER_WINDOW_READY: { | |
| 175 Browser* browser = content::Source<Browser>(source).ptr(); | |
| 176 if (!browser->is_type_panel()) | |
| 177 return; | |
| 178 break; | |
| 179 } | |
| 180 case chrome::NOTIFICATION_BROWSER_CLOSED: { | |
| 181 Browser* browser = content::Source<Browser>(source).ptr(); | |
| 182 if (!browser->is_type_panel()) | |
| 183 return; | |
| 184 closed_browser = browser; | |
|
Dmitry Titov
2011/11/10 18:35:45
Lets add a notification when panel is removed from
jianli
2011/11/11 00:38:30
Done.
| |
| 185 break; | |
| 186 } | |
| 187 case chrome::NOTIFICATION_PANEL_CHANGED_BOUNDS: | |
|
jennb
2011/11/10 19:17:13
Should you check the panel bounds here and only do
jianli
2011/11/11 00:38:30
Done.
| |
| 188 break; | |
| 189 default: | |
| 190 NOTREACHED(); | |
| 191 return; | |
| 192 } | |
| 193 | |
| 194 if (layout_.ComputeOffsetsToMoveAbovePanels(closed_browser)) | |
| 195 PositionBalloons(true); | |
| 196 } | |
| 197 | |
| 156 void BalloonCollectionImpl::PositionBalloonsInternal(bool reposition) { | 198 void BalloonCollectionImpl::PositionBalloonsInternal(bool reposition) { |
| 157 const Balloons& balloons = base_.balloons(); | 199 const Balloons& balloons = base_.balloons(); |
| 158 | 200 |
| 159 layout_.RefreshSystemMetrics(); | 201 layout_.RefreshSystemMetrics(); |
| 160 gfx::Point origin = layout_.GetLayoutOrigin(); | 202 gfx::Point origin = layout_.GetLayoutOrigin(); |
| 161 for (Balloons::const_iterator it = balloons.begin(); | 203 for (Balloons::const_iterator it = balloons.begin(); |
| 162 it != balloons.end(); | 204 it != balloons.end(); |
| 163 ++it) { | 205 ++it) { |
| 164 gfx::Point upper_left = layout_.NextPosition((*it)->GetViewSize(), &origin); | 206 gfx::Point upper_left = layout_.NextPosition((*it)->GetViewSize(), &origin); |
| 165 (*it)->SetPosition(upper_left, reposition); | 207 (*it)->SetPosition(upper_left, reposition); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 224 reposition_factory_.GetWeakPtr()), | 266 reposition_factory_.GetWeakPtr()), |
| 225 kRepositionDelay); | 267 kRepositionDelay); |
| 226 } | 268 } |
| 227 } else { | 269 } else { |
| 228 // Mouse moved back into the region. Cancel the reposition. | 270 // Mouse moved back into the region. Cancel the reposition. |
| 229 reposition_factory_.InvalidateWeakPtrs(); | 271 reposition_factory_.InvalidateWeakPtrs(); |
| 230 } | 272 } |
| 231 } | 273 } |
| 232 #endif | 274 #endif |
| 233 | 275 |
| 234 BalloonCollectionImpl::Layout::Layout() : placement_(INVALID) { | 276 BalloonCollectionImpl::Layout::Layout() |
| 277 : placement_(INVALID), | |
| 278 bottom_left_offset_to_move_above_panels_(0), | |
| 279 bottom_right_offset_to_move_above_panels_(0) { | |
| 235 RefreshSystemMetrics(); | 280 RefreshSystemMetrics(); |
| 236 } | 281 } |
| 237 | 282 |
| 238 void BalloonCollectionImpl::Layout::GetMaxLinearSize(int* max_balloon_size, | 283 void BalloonCollectionImpl::Layout::GetMaxLinearSize(int* max_balloon_size, |
| 239 int* total_size) const { | 284 int* total_size) const { |
| 240 DCHECK(max_balloon_size && total_size); | 285 DCHECK(max_balloon_size && total_size); |
| 241 | 286 |
| 242 // All placement schemes are vertical, so we only care about height. | 287 // All placement schemes are vertical, so we only care about height. |
| 243 *total_size = work_area_.height(); | 288 *total_size = work_area_.height(); |
| 244 *max_balloon_size = max_balloon_height(); | 289 *max_balloon_size = max_balloon_height(); |
| 245 } | 290 } |
| 246 | 291 |
| 247 gfx::Point BalloonCollectionImpl::Layout::GetLayoutOrigin() const { | |
| 248 int x = 0; | |
| 249 int y = 0; | |
| 250 switch (placement_) { | |
| 251 case VERTICALLY_FROM_TOP_LEFT: | |
| 252 x = work_area_.x() + HorizontalEdgeMargin(); | |
| 253 y = work_area_.y() + VerticalEdgeMargin(); | |
| 254 break; | |
| 255 case VERTICALLY_FROM_TOP_RIGHT: | |
| 256 x = work_area_.right() - HorizontalEdgeMargin(); | |
| 257 y = work_area_.y() + VerticalEdgeMargin(); | |
| 258 break; | |
| 259 case VERTICALLY_FROM_BOTTOM_LEFT: | |
| 260 x = work_area_.x() + HorizontalEdgeMargin(); | |
| 261 y = work_area_.bottom() - VerticalEdgeMargin(); | |
| 262 break; | |
| 263 case VERTICALLY_FROM_BOTTOM_RIGHT: | |
| 264 x = work_area_.right() - HorizontalEdgeMargin(); | |
| 265 y = work_area_.bottom() - VerticalEdgeMargin(); | |
| 266 break; | |
| 267 default: | |
| 268 NOTREACHED(); | |
| 269 break; | |
| 270 } | |
| 271 return gfx::Point(x, y); | |
| 272 } | |
| 273 | |
| 274 gfx::Point BalloonCollectionImpl::Layout::NextPosition( | 292 gfx::Point BalloonCollectionImpl::Layout::NextPosition( |
| 275 const gfx::Size& balloon_size, | 293 const gfx::Size& balloon_size, |
| 276 gfx::Point* position_iterator) const { | 294 gfx::Point* position_iterator) const { |
| 277 DCHECK(position_iterator); | 295 DCHECK(position_iterator); |
| 278 | 296 |
| 279 int x = 0; | 297 int x = 0; |
| 280 int y = 0; | 298 int y = 0; |
| 281 switch (placement_) { | 299 switch (placement_) { |
| 282 case VERTICALLY_FROM_TOP_LEFT: | 300 case VERTICALLY_FROM_TOP_LEFT: |
| 283 x = position_iterator->x(); | 301 x = position_iterator->x(); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 304 y = position_iterator->y(); | 322 y = position_iterator->y(); |
| 305 break; | 323 break; |
| 306 default: | 324 default: |
| 307 NOTREACHED(); | 325 NOTREACHED(); |
| 308 break; | 326 break; |
| 309 } | 327 } |
| 310 return gfx::Point(x, y); | 328 return gfx::Point(x, y); |
| 311 } | 329 } |
| 312 | 330 |
| 313 gfx::Point BalloonCollectionImpl::Layout::OffScreenLocation() const { | 331 gfx::Point BalloonCollectionImpl::Layout::OffScreenLocation() const { |
| 314 int x = 0; | 332 gfx::Point location = GetLayoutOrigin(); |
| 315 int y = 0; | |
| 316 switch (placement_) { | 333 switch (placement_) { |
| 317 case VERTICALLY_FROM_TOP_LEFT: | 334 case VERTICALLY_FROM_TOP_LEFT: |
| 318 x = work_area_.x() + HorizontalEdgeMargin(); | 335 case VERTICALLY_FROM_BOTTOM_LEFT: |
| 319 y = work_area_.y() + kBalloonMaxHeight + VerticalEdgeMargin(); | 336 location.Offset(0, kBalloonMaxHeight); |
| 320 break; | 337 break; |
| 321 case VERTICALLY_FROM_TOP_RIGHT: | 338 case VERTICALLY_FROM_TOP_RIGHT: |
| 322 x = work_area_.right() - kBalloonMaxWidth - HorizontalEdgeMargin(); | |
| 323 y = work_area_.y() + kBalloonMaxHeight + VerticalEdgeMargin(); | |
| 324 break; | |
| 325 case VERTICALLY_FROM_BOTTOM_LEFT: | |
| 326 x = work_area_.x() + HorizontalEdgeMargin(); | |
| 327 y = work_area_.bottom() + kBalloonMaxHeight + VerticalEdgeMargin(); | |
| 328 break; | |
| 329 case VERTICALLY_FROM_BOTTOM_RIGHT: | 339 case VERTICALLY_FROM_BOTTOM_RIGHT: |
| 330 x = work_area_.right() - kBalloonMaxWidth - HorizontalEdgeMargin(); | 340 location.Offset(-kBalloonMaxWidth, kBalloonMaxHeight); |
| 331 y = work_area_.bottom() + kBalloonMaxHeight + VerticalEdgeMargin(); | |
| 332 break; | 341 break; |
| 333 default: | 342 default: |
| 334 NOTREACHED(); | 343 NOTREACHED(); |
| 335 break; | 344 break; |
| 336 } | 345 } |
| 337 return gfx::Point(x, y); | 346 return location; |
| 338 } | 347 } |
| 339 | 348 |
| 340 bool BalloonCollectionImpl::Layout::RequiresOffsets() const { | 349 bool BalloonCollectionImpl::Layout::RequiresOffsets() const { |
| 341 // Layout schemes that grow up from the bottom require offsets; | 350 // Layout schemes that grow up from the bottom require offsets; |
| 342 // schemes that grow down do not require offsets. | 351 // schemes that grow down do not require offsets. |
| 343 bool offsets = (placement_ == VERTICALLY_FROM_BOTTOM_LEFT || | 352 bool offsets = (placement_ == VERTICALLY_FROM_BOTTOM_LEFT || |
| 344 placement_ == VERTICALLY_FROM_BOTTOM_RIGHT); | 353 placement_ == VERTICALLY_FROM_BOTTOM_RIGHT); |
| 345 | 354 |
| 346 #if defined(OS_MACOSX) | 355 #if defined(OS_MACOSX) |
| 347 // These schemes are in screen-coordinates, and top and bottom | 356 // These schemes are in screen-coordinates, and top and bottom |
| 348 // are inverted on Mac. | 357 // are inverted on Mac. |
| 349 offsets = !offsets; | 358 offsets = !offsets; |
| 350 #endif | 359 #endif |
| 351 | 360 |
| 352 return offsets; | 361 return offsets; |
| 353 } | 362 } |
| 354 | 363 |
| 355 // static | 364 // static |
| 356 gfx::Size BalloonCollectionImpl::Layout::ConstrainToSizeLimits( | 365 gfx::Size BalloonCollectionImpl::Layout::ConstrainToSizeLimits( |
| 357 const gfx::Size& size) { | 366 const gfx::Size& size) { |
| 358 // restrict to the min & max sizes | 367 // restrict to the min & max sizes |
| 359 return gfx::Size( | 368 return gfx::Size( |
| 360 std::max(min_balloon_width(), | 369 std::max(min_balloon_width(), |
| 361 std::min(max_balloon_width(), size.width())), | 370 std::min(max_balloon_width(), size.width())), |
| 362 std::max(min_balloon_height(), | 371 std::max(min_balloon_height(), |
| 363 std::min(max_balloon_height(), size.height()))); | 372 std::min(max_balloon_height(), size.height()))); |
| 364 } | 373 } |
| 365 | 374 |
| 375 bool BalloonCollectionImpl::Layout::ComputeOffsetsToMoveAbovePanels( | |
| 376 Browser* closed_browser) { | |
| 377 const PanelManager::Panels& panels = PanelManager::GetInstance()->panels(); | |
| 378 | |
| 379 // For balloons that are aligned vertically from bottom-right corner, the | |
| 380 // offset is the maximum height of the right-most panels that could overlap | |
| 381 // with the balloons. | |
| 382 int bottom_right_offset_to_move_above_panels = 0; | |
| 383 for (PanelManager::Panels::const_iterator iter = panels.begin(); | |
| 384 iter != panels.end(); ++iter) { | |
| 385 // No need to check panels beyond the area occupied by the balloons. | |
| 386 if ((*iter)->GetBounds().right() <= | |
| 387 work_area_.right() - max_balloon_width()) | |
| 388 break; | |
| 389 | |
| 390 // Skip the panel that is currently being closed. We might not remove it | |
| 391 // from the panel manager yet. | |
| 392 if ((*iter)->browser() == closed_browser) | |
| 393 continue; | |
| 394 | |
| 395 int current_height = (*iter)->GetBounds().height(); | |
| 396 if (current_height > bottom_right_offset_to_move_above_panels) | |
| 397 bottom_right_offset_to_move_above_panels = current_height; | |
| 398 } | |
| 399 | |
| 400 // For balloons that are aligned vertically from bottom-left corner, the | |
| 401 // offset is the maximum height of the left-most panels that could overlap | |
| 402 // with the balloons. | |
| 403 int bottom_left_offset_to_move_above_panels = 0; | |
| 404 for (PanelManager::Panels::const_reverse_iterator iter = panels.rbegin(); | |
| 405 iter != panels.rend(); ++iter) { | |
| 406 // No need to check panels beyond the area occupied by the balloons. | |
| 407 if ((*iter)->GetBounds().x() > work_area_.x() + max_balloon_width()) | |
| 408 break; | |
| 409 | |
| 410 // Skip the panel that is currently being closed. We might not remove it | |
| 411 // from the panel manager yet. | |
| 412 if ((*iter)->browser() == closed_browser) | |
| 413 continue; | |
| 414 | |
| 415 int current_height = (*iter)->GetBounds().height(); | |
| 416 if (current_height > bottom_left_offset_to_move_above_panels) | |
| 417 bottom_left_offset_to_move_above_panels = current_height; | |
| 418 } | |
| 419 | |
| 420 // If no change is detected, return false to indicate that we do not need to | |
| 421 // reposition balloons. | |
| 422 if (bottom_right_offset_to_move_above_panels_ == | |
| 423 bottom_right_offset_to_move_above_panels && | |
| 424 bottom_left_offset_to_move_above_panels_ == | |
| 425 bottom_left_offset_to_move_above_panels) { | |
| 426 return false; | |
| 427 } | |
| 428 | |
| 429 bottom_right_offset_to_move_above_panels_ = | |
| 430 bottom_right_offset_to_move_above_panels; | |
| 431 bottom_left_offset_to_move_above_panels_ = | |
| 432 bottom_left_offset_to_move_above_panels; | |
| 433 return true; | |
| 434 } | |
| 435 | |
| 366 bool BalloonCollectionImpl::Layout::RefreshSystemMetrics() { | 436 bool BalloonCollectionImpl::Layout::RefreshSystemMetrics() { |
| 367 bool changed = false; | 437 bool changed = false; |
| 368 | 438 |
| 369 #if defined(OS_MACOSX) | 439 #if defined(OS_MACOSX) |
| 370 gfx::Rect new_work_area = GetMacWorkArea(); | 440 gfx::Rect new_work_area = GetMacWorkArea(); |
| 371 #else | 441 #else |
| 372 scoped_ptr<WindowSizer::MonitorInfoProvider> info_provider( | 442 scoped_ptr<WindowSizer::MonitorInfoProvider> info_provider( |
| 373 WindowSizer::CreateDefaultMonitorInfoProvider()); | 443 WindowSizer::CreateDefaultMonitorInfoProvider()); |
| 374 gfx::Rect new_work_area = info_provider->GetPrimaryMonitorWorkArea(); | 444 gfx::Rect new_work_area = info_provider->GetPrimaryMonitorWorkArea(); |
| 375 #endif | 445 #endif |
| 376 if (!work_area_.Equals(new_work_area)) { | 446 if (!work_area_.Equals(new_work_area)) { |
| 377 work_area_.SetRect(new_work_area.x(), new_work_area.y(), | 447 work_area_.SetRect(new_work_area.x(), new_work_area.y(), |
| 378 new_work_area.width(), new_work_area.height()); | 448 new_work_area.width(), new_work_area.height()); |
| 379 changed = true; | 449 changed = true; |
| 380 } | 450 } |
| 381 | 451 |
| 382 return changed; | 452 return changed; |
| 383 } | 453 } |
| OLD | NEW |