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

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

Issue 2915003: position the balloons after closing in a way that will keep the next one's cl... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 5 months 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) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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/logging.h" 7 #include "base/logging.h"
8 #include "base/stl_util-inl.h" 8 #include "base/stl_util-inl.h"
9 #include "chrome/browser/notifications/balloon.h" 9 #include "chrome/browser/notifications/balloon.h"
10 #include "chrome/browser/notifications/balloon_host.h" 10 #include "chrome/browser/notifications/balloon_host.h"
11 #include "chrome/browser/notifications/notification.h" 11 #include "chrome/browser/notifications/notification.h"
12 #include "chrome/browser/window_sizer.h" 12 #include "chrome/browser/window_sizer.h"
13 #include "gfx/rect.h" 13 #include "gfx/rect.h"
14 #include "gfx/size.h" 14 #include "gfx/size.h"
15 15
16 namespace { 16 namespace {
17 17
18 // Portion of the screen allotted for notifications. When notification balloons 18 // Portion of the screen allotted for notifications. When notification balloons
19 // extend over this, no new notifications are shown until some are closed. 19 // extend over this, no new notifications are shown until some are closed.
20 const double kPercentBalloonFillFactor = 0.7; 20 const double kPercentBalloonFillFactor = 0.7;
21 21
22 // Allow at least this number of balloons on the screen. 22 // Allow at least this number of balloons on the screen.
23 const int kMinAllowedBalloonCount = 2; 23 const int kMinAllowedBalloonCount = 2;
24 24
25 // Delay from the mouse leaving the balloon collection before
26 // there is a relayout, in milliseconds.
27 const int kRepositionDelay = 300;
28
25 } // namespace 29 } // namespace
26 30
27 // static 31 // static
28 // Note that on MacOS, since the coordinate system is inverted vertically from 32 // Note that on MacOS, since the coordinate system is inverted vertically from
29 // the others, this actually produces notifications coming from the TOP right, 33 // the others, this actually produces notifications coming from the TOP right,
30 // which is what is desired. 34 // which is what is desired.
31 BalloonCollectionImpl::Layout::Placement 35 BalloonCollectionImpl::Layout::Placement
32 BalloonCollectionImpl::Layout::placement_ = 36 BalloonCollectionImpl::Layout::placement_ =
33 Layout::VERTICALLY_FROM_BOTTOM_RIGHT; 37 Layout::VERTICALLY_FROM_BOTTOM_RIGHT;
34 38
35 BalloonCollectionImpl::BalloonCollectionImpl() { 39 BalloonCollectionImpl::BalloonCollectionImpl()
40 #if USE_OFFSETS
41 : ALLOW_THIS_IN_INITIALIZER_LIST(reposition_factory_(this)),
42 added_as_message_loop_observer_(false)
43 #endif
44 {
36 } 45 }
37 46
38 BalloonCollectionImpl::~BalloonCollectionImpl() { 47 BalloonCollectionImpl::~BalloonCollectionImpl() {
39 STLDeleteElements(&balloons_); 48 STLDeleteElements(&balloons_);
40 } 49 }
41 50
42 void BalloonCollectionImpl::Add(const Notification& notification, 51 void BalloonCollectionImpl::Add(const Notification& notification,
43 Profile* profile) { 52 Profile* profile) {
44 Balloon* new_balloon = MakeBalloon(notification, profile); 53 Balloon* new_balloon = MakeBalloon(notification, profile);
45 // The +1 on width is necessary because width is fixed on notifications, 54 // The +1 on width is necessary because width is fixed on notifications,
46 // so since we always have the max size, we would always hit the scrollbar 55 // so since we always have the max size, we would always hit the scrollbar
47 // condition. We are only interested in comparing height to maximum. 56 // condition. We are only interested in comparing height to maximum.
48 new_balloon->set_min_scrollbar_size(gfx::Size(1 + layout_.max_balloon_width(), 57 new_balloon->set_min_scrollbar_size(gfx::Size(1 + layout_.max_balloon_width(),
49 layout_.max_balloon_height())); 58 layout_.max_balloon_height()));
50 new_balloon->SetPosition(layout_.OffScreenLocation(), false); 59 new_balloon->SetPosition(layout_.OffScreenLocation(), false);
51 new_balloon->Show(); 60 new_balloon->Show();
61 #if USE_OFFSETS
62 if (balloons_.size() > 0)
63 new_balloon->set_offset(balloons_[balloons_.size() - 1]->offset());
64 #endif
52 65
53 balloons_.push_back(new_balloon); 66 balloons_.push_back(new_balloon);
54 PositionBalloons(false); 67 PositionBalloons(false);
55 68
56 // There may be no listener in a unit test. 69 // There may be no listener in a unit test.
57 if (space_change_listener_) 70 if (space_change_listener_)
58 space_change_listener_->OnBalloonSpaceChanged(); 71 space_change_listener_->OnBalloonSpaceChanged();
59 } 72 }
60 73
61 bool BalloonCollectionImpl::Remove(const Notification& notification) { 74 bool BalloonCollectionImpl::Remove(const Notification& notification) {
(...skipping 30 matching lines...) Expand all
92 } 105 }
93 106
94 void BalloonCollectionImpl::DisplayChanged() { 107 void BalloonCollectionImpl::DisplayChanged() {
95 layout_.RefreshSystemMetrics(); 108 layout_.RefreshSystemMetrics();
96 PositionBalloons(true); 109 PositionBalloons(true);
97 } 110 }
98 111
99 void BalloonCollectionImpl::OnBalloonClosed(Balloon* source) { 112 void BalloonCollectionImpl::OnBalloonClosed(Balloon* source) {
100 // We want to free the balloon when finished. 113 // We want to free the balloon when finished.
101 scoped_ptr<Balloon> closed(source); 114 scoped_ptr<Balloon> closed(source);
102 for (Balloons::iterator it = balloons_.begin(); it != balloons_.end(); ++it) { 115 Balloons::iterator it = balloons_.begin();
116
117 #if USE_OFFSETS
118 gfx::Point offset;
119 bool apply_offset = false;
120 while (it != balloons_.end()) {
121 if (*it == source) {
122 it = balloons_.erase(it);
123 if (it != balloons_.end()) {
124 apply_offset = true;
125 offset.set_y((source)->offset().y() - (*it)->offset().y() +
126 (*it)->content_size().height() - source->content_size().height());
127 }
128 } else {
129 if (apply_offset)
130 (*it)->add_offset(offset);
131 ++it;
132 }
133 }
134 // Start listening for UI events so we cancel the offset when the mouse
135 // leaves the balloon area.
136 if (apply_offset)
137 AddMessageLoopObserver();
138 #else
139 for (; it != balloons_.end(); ++it) {
103 if (*it == source) { 140 if (*it == source) {
104 balloons_.erase(it); 141 balloons_.erase(it);
105 break; 142 break;
106 } 143 }
107 } 144 }
145 #endif
146
108 PositionBalloons(true); 147 PositionBalloons(true);
109 148
110 // There may be no listener in a unit test. 149 // There may be no listener in a unit test.
111 if (space_change_listener_) 150 if (space_change_listener_)
112 space_change_listener_->OnBalloonSpaceChanged(); 151 space_change_listener_->OnBalloonSpaceChanged();
113 } 152 }
114 153
115 void BalloonCollectionImpl::PositionBalloons(bool reposition) { 154 void BalloonCollectionImpl::PositionBalloons(bool reposition) {
116 gfx::Point origin = layout_.GetLayoutOrigin(); 155 gfx::Point origin = layout_.GetLayoutOrigin();
117 for (Balloons::iterator it = balloons_.begin(); it != balloons_.end(); ++it) { 156 for (Balloons::iterator it = balloons_.begin(); it != balloons_.end(); ++it) {
118 gfx::Point upper_left = layout_.NextPosition( 157 gfx::Point upper_left = layout_.NextPosition((*it)->GetViewSize(), &origin);
119 Layout::ConstrainToSizeLimits((*it)->GetViewSize()), &origin);
120 (*it)->SetPosition(upper_left, reposition); 158 (*it)->SetPosition(upper_left, reposition);
121 } 159 }
122 } 160 }
123 161
162 #if USE_OFFSETS
163 void BalloonCollectionImpl::AddMessageLoopObserver() {
164 if (!added_as_message_loop_observer_) {
165 MessageLoopForUI::current()->AddObserver(this);
166 added_as_message_loop_observer_ = true;
167 }
168 }
169
170 void BalloonCollectionImpl::RemoveMessageLoopObserver() {
171 if (added_as_message_loop_observer_) {
172 MessageLoopForUI::current()->RemoveObserver(this);
173 added_as_message_loop_observer_ = false;
174 }
175 }
176
177 void BalloonCollectionImpl::CancelOffsets() {
178 reposition_factory_.RevokeAll();
179
180 // Unhook from listening to all UI events.
181 RemoveMessageLoopObserver();
182
183 for (Balloons::iterator it = balloons_.begin(); it != balloons_.end(); ++it)
184 (*it)->set_offset(gfx::Point(0, 0));
185
186 PositionBalloons(true);
187 }
188
189 void BalloonCollectionImpl::HandleMouseMoveEvent() {
190 if (!IsCursorInBalloonCollection()) {
191 // Mouse has left the region. Schedule a reposition after
192 // a short delay.
193 if (reposition_factory_.empty()) {
194 MessageLoop::current()->PostDelayedTask(
195 FROM_HERE,
196 reposition_factory_.NewRunnableMethod(
197 &BalloonCollectionImpl::CancelOffsets),
198 kRepositionDelay);
199 }
200 } else {
201 // Mouse moved back into the region. Cancel the reposition.
202 reposition_factory_.RevokeAll();
203 }
204 }
205 #endif
206
124 BalloonCollectionImpl::Layout::Layout() { 207 BalloonCollectionImpl::Layout::Layout() {
125 RefreshSystemMetrics(); 208 RefreshSystemMetrics();
126 } 209 }
127 210
128 void BalloonCollectionImpl::Layout::GetMaxLinearSize(int* max_balloon_size, 211 void BalloonCollectionImpl::Layout::GetMaxLinearSize(int* max_balloon_size,
129 int* total_size) const { 212 int* total_size) const {
130 DCHECK(max_balloon_size && total_size); 213 DCHECK(max_balloon_size && total_size);
131 214
132 switch (placement_) { 215 switch (placement_) {
133 case VERTICALLY_FROM_TOP_RIGHT: 216 case VERTICALLY_FROM_TOP_RIGHT:
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 gfx::Rect new_work_area = info_provider->GetPrimaryMonitorWorkArea(); 311 gfx::Rect new_work_area = info_provider->GetPrimaryMonitorWorkArea();
229 #endif 312 #endif
230 if (!work_area_.Equals(new_work_area)) { 313 if (!work_area_.Equals(new_work_area)) {
231 work_area_.SetRect(new_work_area.x(), new_work_area.y(), 314 work_area_.SetRect(new_work_area.x(), new_work_area.y(),
232 new_work_area.width(), new_work_area.height()); 315 new_work_area.width(), new_work_area.height());
233 changed = true; 316 changed = true;
234 } 317 }
235 318
236 return changed; 319 return changed;
237 } 320 }
OLDNEW
« no previous file with comments | « chrome/browser/notifications/balloon.h ('k') | chrome/browser/notifications/balloon_collection_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698