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

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 for MacOSX 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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698