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..60547e0b95c0b448b14df1caeb1322dfe5135ff2 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,64 @@ 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) { |
+ if ((*it)->id() == notificationID) { |
+ notification = *it; |
+ break; |
} |
+ } |
+ DCHECK(notification); |
+ if (!notification) |
+ 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 +204,63 @@ class PopupCollectionObserver : public message_center::MessageCenterObserver { |
[popups_ removeAllObjects]; |
} |
+- (void)layoutNotificationsStartingAt:(NSUInteger)index { |
+ 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 |