Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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 #import "ui/message_center/cocoa/popup_collection.h" | 5 #import "ui/message_center/cocoa/popup_collection.h" |
| 6 | 6 |
| 7 #include "ui/message_center/cocoa/popup_controller.h" | 7 #import "ui/message_center/cocoa/notification_controller.h" |
| 8 #import "ui/message_center/cocoa/popup_controller.h" | |
| 8 #include "ui/message_center/message_center.h" | 9 #include "ui/message_center/message_center.h" |
| 9 #include "ui/message_center/message_center_constants.h" | 10 #include "ui/message_center/message_center_constants.h" |
| 10 #include "ui/message_center/message_center_observer.h" | 11 #include "ui/message_center/message_center_observer.h" |
| 11 | 12 |
| 12 @interface MCPopupCollection (Private) | 13 @interface MCPopupCollection (Private) |
| 13 // Returns the primary screen's visible frame rectangle. | 14 // Returns the primary screen's visible frame rectangle. |
| 14 - (NSRect)screenFrame; | 15 - (NSRect)screenFrame; |
| 15 | 16 |
| 16 // Shows a popup, if there is room on-screen, for the given notification. | 17 // Shows a popup, if there is room on-screen, for the given notification. |
| 17 // Returns YES if the notification was actually displayed. | 18 // Returns YES if the notification was actually displayed. |
| 18 - (BOOL)addNotification:(const message_center::Notification*)notification; | 19 - (BOOL)addNotification:(const message_center::Notification*)notification; |
| 19 | 20 |
| 21 // Updates the contents of the notification with the given ID. | |
| 22 - (void)updateNotification:(const std::string&)notificationID; | |
| 23 | |
| 20 // Removes a popup from the screen and lays out new notifications that can | 24 // Removes a popup from the screen and lays out new notifications that can |
| 21 // now potentially fit on the screen. | 25 // now potentially fit on the screen. |
| 22 - (void)removeNotification:(const std::string&)notificationID; | 26 - (void)removeNotification:(const std::string&)notificationID; |
| 23 | 27 |
| 24 // Closes all the popups. | 28 // Closes all the popups. |
| 25 - (void)removeAllNotifications; | 29 - (void)removeAllNotifications; |
| 30 | |
| 31 // Positions popup notifications for the |index|th notification in |popups_|. | |
| 32 // This will put the proper amount of space between popup |index - 1| and | |
| 33 // |index| and then any subsequent notifications. | |
| 34 - (void)layoutNotificationsStartingAt:(NSUInteger)index; | |
| 26 @end | 35 @end |
| 27 | 36 |
| 28 namespace { | 37 namespace { |
| 29 | 38 |
| 30 class PopupCollectionObserver : public message_center::MessageCenterObserver { | 39 class PopupCollectionObserver : public message_center::MessageCenterObserver { |
| 31 public: | 40 public: |
| 32 PopupCollectionObserver(message_center::MessageCenter* message_center, | 41 PopupCollectionObserver(message_center::MessageCenter* message_center, |
| 33 MCPopupCollection* popup_collection) | 42 MCPopupCollection* popup_collection) |
| 34 : message_center_(message_center), | 43 : message_center_(message_center), |
| 35 popup_collection_(popup_collection) { | 44 popup_collection_(popup_collection) { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 51 } | 60 } |
| 52 } | 61 } |
| 53 | 62 |
| 54 virtual void OnNotificationRemoved(const std::string& notification_id, | 63 virtual void OnNotificationRemoved(const std::string& notification_id, |
| 55 bool user_id) OVERRIDE { | 64 bool user_id) OVERRIDE { |
| 56 [popup_collection_ removeNotification:notification_id]; | 65 [popup_collection_ removeNotification:notification_id]; |
| 57 } | 66 } |
| 58 | 67 |
| 59 virtual void OnNotificationUpdated( | 68 virtual void OnNotificationUpdated( |
| 60 const std::string& notification_id) OVERRIDE { | 69 const std::string& notification_id) OVERRIDE { |
| 61 // TODO(rsesek): Refactor MCNotificationController to support updating. | 70 [popup_collection_ updateNotification:notification_id]; |
| 62 } | 71 } |
| 63 | 72 |
| 64 private: | 73 private: |
| 65 message_center::MessageCenter* message_center_; // Weak, global. | 74 message_center::MessageCenter* message_center_; // Weak, global. |
| 66 | 75 |
| 67 MCPopupCollection* popup_collection_; // Weak, owns this. | 76 MCPopupCollection* popup_collection_; // Weak, owns this. |
| 68 }; | 77 }; |
| 69 | 78 |
| 70 } // namespace | 79 } // namespace |
| 71 | 80 |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 123 if (y > NSMinY(screenFrame)) { | 132 if (y > NSMinY(screenFrame)) { |
| 124 [[popup window] setFrameOrigin:NSMakePoint(x, y)]; | 133 [[popup window] setFrameOrigin:NSMakePoint(x, y)]; |
| 125 [popup showWindow:self]; | 134 [popup showWindow:self]; |
| 126 [popups_ addObject:popup]; // Transfer ownership. | 135 [popups_ addObject:popup]; // Transfer ownership. |
| 127 return YES; | 136 return YES; |
| 128 } | 137 } |
| 129 | 138 |
| 130 return NO; | 139 return NO; |
| 131 } | 140 } |
| 132 | 141 |
| 133 - (void)removeNotification:(const std::string&)notificationID { | 142 - (void)updateNotification:(const std::string&)notificationID { |
| 134 // The set of notification IDs that are currently displayed. | 143 // Find the controller for this notification ID. |
| 135 std::set<std::string> onScreen; | 144 NSUInteger index = [popups_ indexOfObjectPassingTest: |
| 145 ^BOOL(id popup, NSUInteger i, BOOL* stop) { | |
| 146 return [popup notificationID] == notificationID; | |
| 147 }]; | |
| 148 // The notification may not be on screen. | |
| 149 if (index == NSNotFound) | |
| 150 return; | |
| 136 | 151 |
| 137 // Find the popup for the given ID. | 152 // Find the corresponding model object for the ID. This may be a replaced |
| 138 NSUInteger index = 0; | 153 // notification, so the controller's current model object may be stale. |
| 139 MCPopupController* popup = nil; | 154 const message_center::Notification* notification = NULL; |
| 140 for (MCPopupController* candidate in popups_.get()) { | 155 const auto& modelPopups = messageCenter_->GetPopupNotifications(); |
| 141 if ([candidate notificationID] == notificationID) { | 156 for (auto it = modelPopups.begin(); it != modelPopups.end(); ++it) { |
|
sail
2013/05/03 21:42:32
nit, this seems like something we'd want to do on
Robert Sesek
2013/05/03 21:59:18
Justin: WDYT? NotificationList has this method but
dewittj
2013/05/03 22:31:37
sounds OK to me
| |
| 142 popup = candidate; | 157 if ((*it)->id() == notificationID) { |
| 143 } else { | 158 notification = *it; |
| 144 onScreen.insert([candidate notificationID]); | 159 break; |
| 145 } | 160 } |
| 146 | 161 } |
| 147 // If the popup has not yet been found, increase the index. | 162 if (!notification) { |
| 148 if (!popup) | 163 NOTREACHED(); |
|
sail
2013/05/03 21:42:32
I don't think this is a good use of NOTREACHED().
Robert Sesek
2013/05/03 21:59:18
Done.
| |
| 149 ++index; | 164 return; |
| 150 } | 165 } |
| 151 | 166 |
| 152 if (!popup) | 167 MCPopupController* popup = [popups_ objectAtIndex:index]; |
| 153 return; | |
| 154 | 168 |
| 155 // Calculate its height and then close it. | 169 CGFloat oldHeight = |
| 156 CGFloat delta = NSHeight([[popup window] frame]) + | 170 NSHeight([[[popup notificationController] view] frame]); |
| 157 message_center::kMarginBetweenItems; | 171 CGFloat newHeight = NSHeight( |
| 158 [popup close]; | 172 [[popup notificationController] updateNotification:notification]); |
| 159 [popups_ removeObjectAtIndex:index]; | |
| 160 | 173 |
| 161 // Shift all the popups up. | 174 // The notification has changed height. This requires updating the popup |
| 162 for ( ; index < [popups_ count]; ++index) { | 175 // window and any popups that come below it. |
| 163 NSWindow* window = [[popups_ objectAtIndex:index] window]; | 176 if (oldHeight != newHeight) { |
| 164 NSPoint origin = [window frame].origin; | 177 NSRect popupFrame = [[popup window] frame]; |
| 165 origin.y += delta; | 178 popupFrame.size.height += newHeight - oldHeight; |
| 166 [window setFrameOrigin:origin]; | 179 [[popup window] setFrame:popupFrame display:YES]; |
| 167 } | |
| 168 | 180 |
| 169 // Display any new popups that can now fit on screen. | 181 // Start re-layout of notifications at this index, so that it readjusts |
| 170 const auto& allPopups = messageCenter_->GetPopupNotifications(); | 182 // the Y origin of this notification. |
| 171 for (auto it = allPopups.begin(); it != allPopups.end(); ++it) { | 183 [self layoutNotificationsStartingAt:index]; |
| 172 if (onScreen.find((*it)->id()) == onScreen.end()) { | |
| 173 // If there's no room left on screen to display notifications, stop | |
| 174 // trying. | |
| 175 if (![self addNotification:*it]) | |
| 176 break; | |
| 177 } | |
| 178 } | 184 } |
| 179 } | 185 } |
| 180 | 186 |
| 187 - (void)removeNotification:(const std::string&)notificationID { | |
| 188 NSUInteger index = [popups_ indexOfObjectPassingTest: | |
| 189 ^BOOL(id popup, NSUInteger index, BOOL* stop) { | |
| 190 return [popup notificationID] == notificationID; | |
| 191 }]; | |
| 192 // The notification may not be on screen. | |
| 193 if (index == NSNotFound) | |
| 194 return; | |
| 195 | |
| 196 [[popups_ objectAtIndex:index] close]; | |
| 197 [popups_ removeObjectAtIndex:index]; | |
| 198 | |
| 199 if (index < [popups_ count]) | |
| 200 [self layoutNotificationsStartingAt:index]; | |
| 201 } | |
| 202 | |
| 181 - (void)removeAllNotifications { | 203 - (void)removeAllNotifications { |
| 182 [popups_ makeObjectsPerformSelector:@selector(close)]; | 204 [popups_ makeObjectsPerformSelector:@selector(close)]; |
| 183 [popups_ removeAllObjects]; | 205 [popups_ removeAllObjects]; |
| 184 } | 206 } |
| 185 | 207 |
| 208 - (void)layoutNotificationsStartingAt:(NSUInteger)index { | |
| 209 DCHECK_GE(index, 0u); | |
|
sail
2013/05/03 21:42:32
since this is unsigned, won't this always be true?
Robert Sesek
2013/05/03 21:59:18
Yes, |index| wasn't always unsigned in a previous
| |
| 210 | |
| 211 NSRect screenFrame = [self screenFrame]; | |
| 212 std::set<std::string> onScreen; | |
| 213 | |
| 214 // Calcluate the bottom edge of the |index - 1|th popup. | |
| 215 CGFloat maxY = 0; | |
| 216 if (index == 0) | |
| 217 maxY = NSMaxY(screenFrame); | |
| 218 else | |
| 219 maxY = NSMinY([[[popups_ objectAtIndex:index - 1] window] frame]); | |
| 220 | |
| 221 // Collect the IDs of popups that are currently on screen. | |
| 222 for (NSUInteger i = 0; i < index; ++i) | |
| 223 onScreen.insert([[popups_ objectAtIndex:i] notificationID]); | |
| 224 | |
| 225 // Iterate between [index, count) notifications and reposition each. If one | |
| 226 // does not fit on screen, close it and any other on-screen popups that come | |
| 227 // after it. | |
| 228 NSUInteger removeAt = NSNotFound; | |
| 229 for (NSUInteger i = index; i < [popups_ count]; ++i) { | |
| 230 MCPopupController* popup = [popups_ objectAtIndex:i]; | |
| 231 NSRect frame = [[popup window] frame]; | |
| 232 frame.origin.y = maxY - message_center::kMarginBetweenItems - | |
| 233 NSHeight(frame); | |
| 234 | |
| 235 // If this popup does not fit on screen, stop repositioning and close this | |
| 236 // and subsequent popups. | |
| 237 if (NSMinY(frame) < NSMinY(screenFrame)) { | |
| 238 removeAt = i; | |
| 239 break; | |
| 240 } | |
| 241 | |
| 242 [[popup window] setFrame:frame display:YES]; | |
| 243 onScreen.insert([popup notificationID]); | |
| 244 | |
| 245 // Set the new maximum Y to be the bottom of this notification. | |
| 246 maxY = NSMinY(frame); | |
| 247 } | |
| 248 | |
| 249 if (removeAt != NSNotFound) { | |
| 250 // Remove any popups that are on screen but no longer fit. | |
| 251 while ([popups_ count] >= removeAt) { | |
| 252 [[popups_ lastObject] close]; | |
| 253 [popups_ removeLastObject]; | |
| 254 } | |
| 255 } else { | |
| 256 // Display any new popups that can now fit on screen. | |
| 257 const auto& allPopups = messageCenter_->GetPopupNotifications(); | |
| 258 for (auto it = allPopups.begin(); it != allPopups.end(); ++it) { | |
| 259 if (onScreen.find((*it)->id()) == onScreen.end()) { | |
| 260 // If there's no room left on screen to display notifications, stop | |
| 261 // trying. | |
| 262 if (![self addNotification:*it]) | |
| 263 break; | |
| 264 } | |
| 265 } | |
| 266 } | |
| 267 } | |
| 268 | |
| 186 @end | 269 @end |
| OLD | NEW |