Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Side by Side Diff: chrome/browser/notifications/balloon_collection_impl.cc

Issue 8503037: Fix the problem that notifications and panels overlap. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix per feedback Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
36 // The spacing between the balloon and the panel.
37 const int kVerticalSpacingBetweenBalloonAndPanel = 5;
38
30 } // namespace 39 } // namespace
31 40
32 BalloonCollectionImpl::BalloonCollectionImpl() 41 BalloonCollectionImpl::BalloonCollectionImpl()
33 #if USE_OFFSETS 42 #if USE_OFFSETS
34 : ALLOW_THIS_IN_INITIALIZER_LIST(reposition_factory_(this)), 43 : ALLOW_THIS_IN_INITIALIZER_LIST(reposition_factory_(this)),
35 added_as_message_loop_observer_(false) 44 added_as_message_loop_observer_(false)
36 #endif 45 #endif
37 { 46 {
47 registrar_.Add(this, chrome::NOTIFICATION_PANEL_ADDED,
48 content::NotificationService::AllSources());
49 registrar_.Add(this, chrome::NOTIFICATION_PANEL_REMOVED,
50 content::NotificationService::AllSources());
51 registrar_.Add(this, chrome::NOTIFICATION_PANEL_CHANGED_BOUNDS,
52 content::NotificationService::AllSources());
38 53
39 SetPositionPreference(BalloonCollection::DEFAULT_POSITION); 54 SetPositionPreference(BalloonCollection::DEFAULT_POSITION);
40 } 55 }
41 56
42 BalloonCollectionImpl::~BalloonCollectionImpl() { 57 BalloonCollectionImpl::~BalloonCollectionImpl() {
43 } 58 }
44 59
45 void BalloonCollectionImpl::Add(const Notification& notification, 60 void BalloonCollectionImpl::Add(const Notification& notification,
46 Profile* profile) { 61 Profile* profile) {
47 Balloon* new_balloon = MakeBalloon(notification, profile); 62 Balloon* new_balloon = MakeBalloon(notification, profile);
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
146 161
147 // This is used only for testing. 162 // This is used only for testing.
148 if (!on_collection_changed_callback_.is_null()) 163 if (!on_collection_changed_callback_.is_null())
149 on_collection_changed_callback_.Run(); 164 on_collection_changed_callback_.Run();
150 } 165 }
151 166
152 const BalloonCollection::Balloons& BalloonCollectionImpl::GetActiveBalloons() { 167 const BalloonCollection::Balloons& BalloonCollectionImpl::GetActiveBalloons() {
153 return base_.balloons(); 168 return base_.balloons();
154 } 169 }
155 170
171 void BalloonCollectionImpl::Observe(
172 int type,
173 const content::NotificationSource& source,
174 const content::NotificationDetails& details) {
175 gfx::Rect bounds;
176 switch (type) {
177 case chrome::NOTIFICATION_PANEL_CHANGED_BOUNDS:
178 bounds = content::Source<Panel>(source).ptr()->GetBounds();
179 // Fall through.
180 case chrome::NOTIFICATION_PANEL_ADDED:
181 case chrome::NOTIFICATION_PANEL_REMOVED:
182 if (layout_.ComputeOffsetToMoveAbovePanels(bounds))
183 PositionBalloons(true);
184 break;
185 default:
186 NOTREACHED();
187 break;
188 }
189 }
190
156 void BalloonCollectionImpl::PositionBalloonsInternal(bool reposition) { 191 void BalloonCollectionImpl::PositionBalloonsInternal(bool reposition) {
157 const Balloons& balloons = base_.balloons(); 192 const Balloons& balloons = base_.balloons();
158 193
159 layout_.RefreshSystemMetrics(); 194 layout_.RefreshSystemMetrics();
160 gfx::Point origin = layout_.GetLayoutOrigin(); 195 gfx::Point origin = layout_.GetLayoutOrigin();
161 for (Balloons::const_iterator it = balloons.begin(); 196 for (Balloons::const_iterator it = balloons.begin();
162 it != balloons.end(); 197 it != balloons.end();
163 ++it) { 198 ++it) {
164 gfx::Point upper_left = layout_.NextPosition((*it)->GetViewSize(), &origin); 199 gfx::Point upper_left = layout_.NextPosition((*it)->GetViewSize(), &origin);
165 (*it)->SetPosition(upper_left, reposition); 200 (*it)->SetPosition(upper_left, reposition);
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
224 reposition_factory_.GetWeakPtr()), 259 reposition_factory_.GetWeakPtr()),
225 kRepositionDelay); 260 kRepositionDelay);
226 } 261 }
227 } else { 262 } else {
228 // Mouse moved back into the region. Cancel the reposition. 263 // Mouse moved back into the region. Cancel the reposition.
229 reposition_factory_.InvalidateWeakPtrs(); 264 reposition_factory_.InvalidateWeakPtrs();
230 } 265 }
231 } 266 }
232 #endif 267 #endif
233 268
234 BalloonCollectionImpl::Layout::Layout() : placement_(INVALID) { 269 BalloonCollectionImpl::Layout::Layout()
270 : placement_(INVALID),
271 offset_to_move_above_panels_(0) {
235 RefreshSystemMetrics(); 272 RefreshSystemMetrics();
236 } 273 }
237 274
238 void BalloonCollectionImpl::Layout::GetMaxLinearSize(int* max_balloon_size, 275 void BalloonCollectionImpl::Layout::GetMaxLinearSize(int* max_balloon_size,
239 int* total_size) const { 276 int* total_size) const {
240 DCHECK(max_balloon_size && total_size); 277 DCHECK(max_balloon_size && total_size);
241 278
242 // All placement schemes are vertical, so we only care about height. 279 // All placement schemes are vertical, so we only care about height.
243 *total_size = work_area_.height(); 280 *total_size = work_area_.height();
244 *max_balloon_size = max_balloon_height(); 281 *max_balloon_size = max_balloon_height();
245 } 282 }
246 283
247 gfx::Point BalloonCollectionImpl::Layout::GetLayoutOrigin() const { 284 gfx::Point BalloonCollectionImpl::Layout::GetLayoutOrigin() const {
285 // For lower-left and lower-right positioning, we need to add an offset
286 // to ensure balloons to stay on top of panels to avoid overlapping.
248 int x = 0; 287 int x = 0;
249 int y = 0; 288 int y = 0;
250 switch (placement_) { 289 switch (placement_) {
251 case VERTICALLY_FROM_TOP_LEFT: 290 case VERTICALLY_FROM_TOP_LEFT: {
252 x = work_area_.x() + HorizontalEdgeMargin(); 291 x = work_area_.x() + HorizontalEdgeMargin();
253 y = work_area_.y() + VerticalEdgeMargin(); 292 y = work_area_.y() + VerticalEdgeMargin() + offset_to_move_above_panels_;
254 break; 293 break;
255 case VERTICALLY_FROM_TOP_RIGHT: 294 }
295 case VERTICALLY_FROM_TOP_RIGHT: {
256 x = work_area_.right() - HorizontalEdgeMargin(); 296 x = work_area_.right() - HorizontalEdgeMargin();
257 y = work_area_.y() + VerticalEdgeMargin(); 297 y = work_area_.y() + VerticalEdgeMargin() + offset_to_move_above_panels_;
258 break; 298 break;
299 }
259 case VERTICALLY_FROM_BOTTOM_LEFT: 300 case VERTICALLY_FROM_BOTTOM_LEFT:
260 x = work_area_.x() + HorizontalEdgeMargin(); 301 x = work_area_.x() + HorizontalEdgeMargin();
261 y = work_area_.bottom() - VerticalEdgeMargin(); 302 y = work_area_.bottom() - VerticalEdgeMargin() -
303 offset_to_move_above_panels_;
262 break; 304 break;
263 case VERTICALLY_FROM_BOTTOM_RIGHT: 305 case VERTICALLY_FROM_BOTTOM_RIGHT:
264 x = work_area_.right() - HorizontalEdgeMargin(); 306 x = work_area_.right() - HorizontalEdgeMargin();
265 y = work_area_.bottom() - VerticalEdgeMargin(); 307 y = work_area_.bottom() - VerticalEdgeMargin() -
308 offset_to_move_above_panels_;
266 break; 309 break;
267 default: 310 default:
268 NOTREACHED(); 311 NOTREACHED();
269 break; 312 break;
270 } 313 }
271 return gfx::Point(x, y); 314 return gfx::Point(x, y);
272 } 315 }
273 316
274 gfx::Point BalloonCollectionImpl::Layout::NextPosition( 317 gfx::Point BalloonCollectionImpl::Layout::NextPosition(
275 const gfx::Size& balloon_size, 318 const gfx::Size& balloon_size,
(...skipping 28 matching lines...) Expand all
304 y = position_iterator->y(); 347 y = position_iterator->y();
305 break; 348 break;
306 default: 349 default:
307 NOTREACHED(); 350 NOTREACHED();
308 break; 351 break;
309 } 352 }
310 return gfx::Point(x, y); 353 return gfx::Point(x, y);
311 } 354 }
312 355
313 gfx::Point BalloonCollectionImpl::Layout::OffScreenLocation() const { 356 gfx::Point BalloonCollectionImpl::Layout::OffScreenLocation() const {
314 int x = 0; 357 gfx::Point location = GetLayoutOrigin();
315 int y = 0;
316 switch (placement_) { 358 switch (placement_) {
317 case VERTICALLY_FROM_TOP_LEFT: 359 case VERTICALLY_FROM_TOP_LEFT:
318 x = work_area_.x() + HorizontalEdgeMargin(); 360 case VERTICALLY_FROM_BOTTOM_LEFT:
319 y = work_area_.y() + kBalloonMaxHeight + VerticalEdgeMargin(); 361 location.Offset(0, kBalloonMaxHeight);
320 break; 362 break;
321 case VERTICALLY_FROM_TOP_RIGHT: 363 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: 364 case VERTICALLY_FROM_BOTTOM_RIGHT:
330 x = work_area_.right() - kBalloonMaxWidth - HorizontalEdgeMargin(); 365 location.Offset(-kBalloonMaxWidth, kBalloonMaxHeight);
331 y = work_area_.bottom() + kBalloonMaxHeight + VerticalEdgeMargin();
332 break; 366 break;
333 default: 367 default:
334 NOTREACHED(); 368 NOTREACHED();
335 break; 369 break;
336 } 370 }
337 return gfx::Point(x, y); 371 return location;
338 } 372 }
339 373
340 bool BalloonCollectionImpl::Layout::RequiresOffsets() const { 374 bool BalloonCollectionImpl::Layout::RequiresOffsets() const {
341 // Layout schemes that grow up from the bottom require offsets; 375 // Layout schemes that grow up from the bottom require offsets;
342 // schemes that grow down do not require offsets. 376 // schemes that grow down do not require offsets.
343 bool offsets = (placement_ == VERTICALLY_FROM_BOTTOM_LEFT || 377 bool offsets = (placement_ == VERTICALLY_FROM_BOTTOM_LEFT ||
344 placement_ == VERTICALLY_FROM_BOTTOM_RIGHT); 378 placement_ == VERTICALLY_FROM_BOTTOM_RIGHT);
345 379
346 #if defined(OS_MACOSX) 380 #if defined(OS_MACOSX)
347 // These schemes are in screen-coordinates, and top and bottom 381 // These schemes are in screen-coordinates, and top and bottom
348 // are inverted on Mac. 382 // are inverted on Mac.
349 offsets = !offsets; 383 offsets = !offsets;
350 #endif 384 #endif
351 385
352 return offsets; 386 return offsets;
353 } 387 }
354 388
355 // static 389 // static
356 gfx::Size BalloonCollectionImpl::Layout::ConstrainToSizeLimits( 390 gfx::Size BalloonCollectionImpl::Layout::ConstrainToSizeLimits(
357 const gfx::Size& size) { 391 const gfx::Size& size) {
358 // restrict to the min & max sizes 392 // restrict to the min & max sizes
359 return gfx::Size( 393 return gfx::Size(
360 std::max(min_balloon_width(), 394 std::max(min_balloon_width(),
361 std::min(max_balloon_width(), size.width())), 395 std::min(max_balloon_width(), size.width())),
362 std::max(min_balloon_height(), 396 std::max(min_balloon_height(),
363 std::min(max_balloon_height(), size.height()))); 397 std::min(max_balloon_height(), size.height())));
364 } 398 }
365 399
400 bool BalloonCollectionImpl::Layout::ComputeOffsetToMoveAbovePanels(
401 const gfx::Rect& panel_bounds) {
402 const PanelManager::Panels& panels = PanelManager::GetInstance()->panels();
403 int offset_to_move_above_panels = 0;
404
405 // The offset is the maximum height of panels that could overlap with the
406 // balloons.
407 if (NeedToMoveAboveLeftSidePanels()) {
408 // If the affecting panel does not lie in the balloon area, no need to
409 // update the offset.
410 if (!panel_bounds.IsEmpty() &&
411 (panel_bounds.right() <= work_area_.x() ||
412 panel_bounds.x() >= work_area_.x() + max_balloon_width())) {
413 return false;
414 }
415
416 for (PanelManager::Panels::const_reverse_iterator iter = panels.rbegin();
417 iter != panels.rend(); ++iter) {
418 // No need to check panels beyond the area occupied by the balloons.
419 if ((*iter)->GetBounds().x() >= work_area_.x() + max_balloon_width())
jennb 2011/11/11 23:02:43 FYI, until your overflow changes are in, this will
420 break;
421
422 int current_height = (*iter)->GetBounds().height();
423 if (current_height > offset_to_move_above_panels)
424 offset_to_move_above_panels = current_height;
425 }
426 } else if (NeedToMoveAboveRightSidePanels()) {
427 // If the affecting panel does not lie in the balloon area, no need to
428 // update the offset.
429 if (!panel_bounds.IsEmpty() &&
430 (panel_bounds.right() <= work_area_.right() - max_balloon_width() ||
431 panel_bounds.x() >= work_area_.right())) {
432 return false;
433 }
434
435 for (PanelManager::Panels::const_iterator iter = panels.begin();
436 iter != panels.end(); ++iter) {
437 // No need to check panels beyond the area occupied by the balloons.
438 if ((*iter)->GetBounds().right() <=
439 work_area_.right() - max_balloon_width())
440 break;
441
442 int current_height = (*iter)->GetBounds().height();
443 if (current_height > offset_to_move_above_panels)
444 offset_to_move_above_panels = current_height;
445 }
446 }
447
448 // Ensure that we have some sort of margin between the 1st balloon and the
449 // panel beneath it even the vertical edge margin is 0 as on Mac.
450 if (offset_to_move_above_panels && !VerticalEdgeMargin())
451 offset_to_move_above_panels += kVerticalSpacingBetweenBalloonAndPanel;
452
453 // If no change is detected, return false to indicate that we do not need to
454 // reposition balloons.
455 if (offset_to_move_above_panels_ == offset_to_move_above_panels)
456 return false;
457
458 offset_to_move_above_panels_ = offset_to_move_above_panels;
459 return true;
460 }
461
366 bool BalloonCollectionImpl::Layout::RefreshSystemMetrics() { 462 bool BalloonCollectionImpl::Layout::RefreshSystemMetrics() {
367 bool changed = false; 463 bool changed = false;
368 464
369 #if defined(OS_MACOSX) 465 #if defined(OS_MACOSX)
370 gfx::Rect new_work_area = GetMacWorkArea(); 466 gfx::Rect new_work_area = GetMacWorkArea();
371 #else 467 #else
372 scoped_ptr<WindowSizer::MonitorInfoProvider> info_provider( 468 scoped_ptr<WindowSizer::MonitorInfoProvider> info_provider(
373 WindowSizer::CreateDefaultMonitorInfoProvider()); 469 WindowSizer::CreateDefaultMonitorInfoProvider());
374 gfx::Rect new_work_area = info_provider->GetPrimaryMonitorWorkArea(); 470 gfx::Rect new_work_area = info_provider->GetPrimaryMonitorWorkArea();
375 #endif 471 #endif
376 if (!work_area_.Equals(new_work_area)) { 472 if (!work_area_.Equals(new_work_area)) {
377 work_area_.SetRect(new_work_area.x(), new_work_area.y(), 473 work_area_.SetRect(new_work_area.x(), new_work_area.y(),
378 new_work_area.width(), new_work_area.height()); 474 new_work_area.width(), new_work_area.height());
379 changed = true; 475 changed = true;
380 } 476 }
381 477
382 return changed; 478 return changed;
383 } 479 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698