Chromium Code Reviews| Index: ui/message_center/cocoa/popup_collection.mm | 
| diff --git a/ui/message_center/cocoa/popup_collection.mm b/ui/message_center/cocoa/popup_collection.mm | 
| index e77c3abc24af3662a615be7fdab3c0d04ffcca3d..cf286f6cc60abc32be138ddd18daa5567c7e6fbd 100644 | 
| --- a/ui/message_center/cocoa/popup_collection.mm | 
| +++ b/ui/message_center/cocoa/popup_collection.mm | 
| @@ -4,7 +4,8 @@ | 
| #import "ui/message_center/cocoa/popup_collection.h" | 
| -#include "ui/message_center/cocoa/popup_controller.h" | 
| +#import "ui/message_center/cocoa/notification_controller.h" | 
| +#import "ui/message_center/cocoa/popup_controller.h" | 
| #include "ui/message_center/message_center.h" | 
| #include "ui/message_center/message_center_constants.h" | 
| #include "ui/message_center/message_center_observer.h" | 
| @@ -17,12 +18,20 @@ | 
| // Returns YES if the notification was actually displayed. | 
| - (BOOL)addNotification:(const message_center::Notification*)notification; | 
| +// Updates the contents of the notification with the given ID. | 
| +- (void)updateNotification:(const std::string&)notificationID; | 
| + | 
| // Removes a popup from the screen and lays out new notifications that can | 
| // now potentially fit on the screen. | 
| - (void)removeNotification:(const std::string&)notificationID; | 
| // Closes all the popups. | 
| - (void)removeAllNotifications; | 
| + | 
| +// Positions popup notifications for the |index|th notification in |popups_|. | 
| +// This will put the proper amount of space between popup |index - 1| and | 
| +// |index| and then any subsequent notifications. | 
| +- (void)layoutNotificationsStartingAt:(NSUInteger)index; | 
| @end | 
| namespace { | 
| @@ -58,7 +67,7 @@ class PopupCollectionObserver : public message_center::MessageCenterObserver { | 
| virtual void OnNotificationUpdated( | 
| const std::string& notification_id) OVERRIDE { | 
| - // TODO(rsesek): Refactor MCNotificationController to support updating. | 
| + [popup_collection_ updateNotification:notification_id]; | 
| } | 
| private: | 
| @@ -130,52 +139,65 @@ class PopupCollectionObserver : public message_center::MessageCenterObserver { | 
| return NO; | 
| } | 
| -- (void)removeNotification:(const std::string&)notificationID { | 
| - // The set of notification IDs that are currently displayed. | 
| - std::set<std::string> onScreen; | 
| +- (void)updateNotification:(const std::string&)notificationID { | 
| + // Find the controller for this notification ID. | 
| + NSUInteger index = [popups_ indexOfObjectPassingTest: | 
| + ^BOOL(id popup, NSUInteger i, BOOL* stop) { | 
| + return [popup notificationID] == notificationID; | 
| + }]; | 
| + // The notification may not be on screen. | 
| + if (index == NSNotFound) | 
| + return; | 
| - // Find the popup for the given ID. | 
| - NSUInteger index = 0; | 
| - MCPopupController* popup = nil; | 
| - for (MCPopupController* candidate in popups_.get()) { | 
| - if ([candidate notificationID] == notificationID) { | 
| - popup = candidate; | 
| - } else { | 
| - onScreen.insert([candidate notificationID]); | 
| + // Find the corresponding model object for the ID. This may be a replaced | 
| + // notification, so the controller's current model object may be stale. | 
| + const message_center::Notification* notification = NULL; | 
| + const auto& modelPopups = messageCenter_->GetPopupNotifications(); | 
| + 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
 
 | 
| + if ((*it)->id() == notificationID) { | 
| + notification = *it; | 
| + break; | 
| } | 
| + } | 
| + if (!notification) { | 
| + 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.
 
 | 
| + return; | 
| + } | 
| + | 
| + MCPopupController* popup = [popups_ objectAtIndex:index]; | 
| - // If the popup has not yet been found, increase the index. | 
| - if (!popup) | 
| - ++index; | 
| + CGFloat oldHeight = | 
| + NSHeight([[[popup notificationController] view] frame]); | 
| + CGFloat newHeight = NSHeight( | 
| + [[popup notificationController] updateNotification:notification]); | 
| + | 
| + // The notification has changed height. This requires updating the popup | 
| + // window and any popups that come below it. | 
| + if (oldHeight != newHeight) { | 
| + NSRect popupFrame = [[popup window] frame]; | 
| + popupFrame.size.height += newHeight - oldHeight; | 
| + [[popup window] setFrame:popupFrame display:YES]; | 
| + | 
| + // Start re-layout of notifications at this index, so that it readjusts | 
| + // the Y origin of this notification. | 
| + [self layoutNotificationsStartingAt:index]; | 
| } | 
| +} | 
| - if (!popup) | 
| +- (void)removeNotification:(const std::string&)notificationID { | 
| + NSUInteger index = [popups_ indexOfObjectPassingTest: | 
| + ^BOOL(id popup, NSUInteger index, BOOL* stop) { | 
| + return [popup notificationID] == notificationID; | 
| + }]; | 
| + // The notification may not be on screen. | 
| + if (index == NSNotFound) | 
| return; | 
| - // Calculate its height and then close it. | 
| - CGFloat delta = NSHeight([[popup window] frame]) + | 
| - message_center::kMarginBetweenItems; | 
| - [popup close]; | 
| + [[popups_ objectAtIndex:index] close]; | 
| [popups_ removeObjectAtIndex:index]; | 
| - // Shift all the popups up. | 
| - for ( ; index < [popups_ count]; ++index) { | 
| - NSWindow* window = [[popups_ objectAtIndex:index] window]; | 
| - NSPoint origin = [window frame].origin; | 
| - origin.y += delta; | 
| - [window setFrameOrigin:origin]; | 
| - } | 
| - | 
| - // Display any new popups that can now fit on screen. | 
| - const auto& allPopups = messageCenter_->GetPopupNotifications(); | 
| - for (auto it = allPopups.begin(); it != allPopups.end(); ++it) { | 
| - if (onScreen.find((*it)->id()) == onScreen.end()) { | 
| - // If there's no room left on screen to display notifications, stop | 
| - // trying. | 
| - if (![self addNotification:*it]) | 
| - break; | 
| - } | 
| - } | 
| + if (index < [popups_ count]) | 
| + [self layoutNotificationsStartingAt:index]; | 
| } | 
| - (void)removeAllNotifications { | 
| @@ -183,4 +205,65 @@ class PopupCollectionObserver : public message_center::MessageCenterObserver { | 
| [popups_ removeAllObjects]; | 
| } | 
| +- (void)layoutNotificationsStartingAt:(NSUInteger)index { | 
| + 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
 
 | 
| + | 
| + NSRect screenFrame = [self screenFrame]; | 
| + std::set<std::string> onScreen; | 
| + | 
| + // Calcluate the bottom edge of the |index - 1|th popup. | 
| + CGFloat maxY = 0; | 
| + if (index == 0) | 
| + maxY = NSMaxY(screenFrame); | 
| + else | 
| + maxY = NSMinY([[[popups_ objectAtIndex:index - 1] window] frame]); | 
| + | 
| + // Collect the IDs of popups that are currently on screen. | 
| + for (NSUInteger i = 0; i < index; ++i) | 
| + onScreen.insert([[popups_ objectAtIndex:i] notificationID]); | 
| + | 
| + // Iterate between [index, count) notifications and reposition each. If one | 
| + // does not fit on screen, close it and any other on-screen popups that come | 
| + // after it. | 
| + NSUInteger removeAt = NSNotFound; | 
| + for (NSUInteger i = index; i < [popups_ count]; ++i) { | 
| + MCPopupController* popup = [popups_ objectAtIndex:i]; | 
| + NSRect frame = [[popup window] frame]; | 
| + frame.origin.y = maxY - message_center::kMarginBetweenItems - | 
| + NSHeight(frame); | 
| + | 
| + // If this popup does not fit on screen, stop repositioning and close this | 
| + // and subsequent popups. | 
| + if (NSMinY(frame) < NSMinY(screenFrame)) { | 
| + removeAt = i; | 
| + break; | 
| + } | 
| + | 
| + [[popup window] setFrame:frame display:YES]; | 
| + onScreen.insert([popup notificationID]); | 
| + | 
| + // Set the new maximum Y to be the bottom of this notification. | 
| + maxY = NSMinY(frame); | 
| + } | 
| + | 
| + if (removeAt != NSNotFound) { | 
| + // Remove any popups that are on screen but no longer fit. | 
| + while ([popups_ count] >= removeAt) { | 
| + [[popups_ lastObject] close]; | 
| + [popups_ removeLastObject]; | 
| + } | 
| + } else { | 
| + // Display any new popups that can now fit on screen. | 
| + const auto& allPopups = messageCenter_->GetPopupNotifications(); | 
| + for (auto it = allPopups.begin(); it != allPopups.end(); ++it) { | 
| + if (onScreen.find((*it)->id()) == onScreen.end()) { | 
| + // If there's no room left on screen to display notifications, stop | 
| + // trying. | 
| + if (![self addNotification:*it]) | 
| + break; | 
| + } | 
| + } | 
| + } | 
| +} | 
| + | 
| @end |