Index: ui/message_center/views/message_popup_collection.cc |
diff --git a/ui/message_center/views/message_popup_collection.cc b/ui/message_center/views/message_popup_collection.cc |
index 4f13879f5400ca0cf0b77f09c09d6b86c2e36a50..d1a3918f60664a8c03a8b9e2e692f68cac0f6abf 100644 |
--- a/ui/message_center/views/message_popup_collection.cc |
+++ b/ui/message_center/views/message_popup_collection.cc |
@@ -26,8 +26,11 @@ namespace message_center { |
class ToastContentsView : public views::WidgetDelegateView { |
public: |
ToastContentsView(const Notification* notification, |
- base::WeakPtr<MessagePopupCollection> collection) |
- : collection_(collection) { |
+ base::WeakPtr<MessagePopupCollection> collection, |
+ MessageCenter* message_center) |
+ : id_(notification->id()), |
+ collection_(collection), |
+ message_center_(message_center) { |
DCHECK(collection_); |
set_notify_enter_exit_on_child(true); |
@@ -43,7 +46,7 @@ class ToastContentsView : public views::WidgetDelegateView { |
// Creates the timer only when it does the timeout (i.e. not never-timeout). |
if (!notification->never_timeout()) |
- timer_.reset(new base::OneShotTimer<views::Widget>); |
+ timer_.reset(new base::OneShotTimer<MessageCenter>); |
} |
views::Widget* CreateWidget(gfx::NativeView parent) { |
@@ -98,8 +101,8 @@ class ToastContentsView : public views::WidgetDelegateView { |
start_time_ = base::Time::Now(); |
timer_->Start(FROM_HERE, |
delay_, |
- base::Bind(&views::Widget::Close, |
- base::Unretained(GetWidget()))); |
+ base::Bind(&MessageCenter::RemoveNotification, |
+ base::Unretained(message_center_), id_, false)); |
dharcourt
2013/04/12 09:05:38
Won't this remove the notification from the notifi
Jun Mukai
2013/04/12 21:20:54
Ugha, you're right. I'm doing a bad thing.
reverti
|
} |
// Overridden from views::WidgetDelegate: |
@@ -145,10 +148,12 @@ class ToastContentsView : public views::WidgetDelegateView { |
} |
private: |
+ std::string id_; |
base::TimeDelta delay_; |
base::Time start_time_; |
- scoped_ptr<base::OneShotTimer<views::Widget> > timer_; |
+ scoped_ptr<base::OneShotTimer<MessageCenter> > timer_; |
base::WeakPtr<MessagePopupCollection> collection_; |
+ MessageCenter* message_center_; |
DISALLOW_COPY_AND_ASSIGN(ToastContentsView); |
}; |
@@ -158,14 +163,17 @@ MessagePopupCollection::MessagePopupCollection(gfx::NativeView parent, |
: parent_(parent), |
message_center_(message_center) { |
DCHECK(message_center_); |
- UpdatePopups(); |
+ |
+ UpdateWidgets(); |
+ message_center_->AddObserver(this); |
} |
MessagePopupCollection::~MessagePopupCollection() { |
+ message_center_->RemoveObserver(this); |
CloseAllWidgets(); |
} |
-void MessagePopupCollection::UpdatePopups() { |
+void MessagePopupCollection::UpdateWidgets() { |
NotificationList::PopupNotifications popups = |
message_center_->GetPopupNotifications(); |
@@ -174,26 +182,11 @@ void MessagePopupCollection::UpdatePopups() { |
return; |
} |
- gfx::Rect work_area; |
- if (!parent_) { |
- // On Win+Aura, we don't have a parent since the popups currently show up |
- // on the Windows desktop, not in the Aura/Ash desktop. This code will |
- // display the popups on the primary display. |
- gfx::Screen* screen = gfx::Screen::GetNativeScreen(); |
- work_area = screen->GetPrimaryDisplay().work_area(); |
- } else { |
- gfx::Screen* screen = gfx::Screen::GetScreenFor(parent_); |
- work_area = screen->GetDisplayNearestWindow(parent_).work_area(); |
- } |
- |
- std::set<std::string> old_toast_ids; |
- for (ToastContainer::iterator iter = toasts_.begin(); iter != toasts_.end(); |
- ++iter) { |
- old_toast_ids.insert(iter->first); |
- } |
- |
- int bottom = work_area.bottom() - kMarginBetweenItems; |
- int left = work_area.right() - kNotificationWidth - kMarginBetweenItems; |
+ gfx::Point base_position = GetWorkAreaBottomRight(); |
+ int bottom = widgets_.empty() ? |
+ base_position.y() : widgets_.back()->GetWindowBoundsInScreen().y(); |
+ bottom -= kMarginBetweenItems; |
+ int left = base_position.x() - kNotificationWidth - kMarginBetweenItems; |
// Iterate in the reverse order to keep the oldest toasts on screen. Newer |
// items may be ignored if there are no room to place them. |
for (NotificationList::PopupNotifications::const_reverse_iterator iter = |
@@ -206,48 +199,30 @@ void MessagePopupCollection::UpdatePopups() { |
break; |
} |
- ToastContainer::iterator toast_iter = toasts_.find((*iter)->id()); |
- views::Widget* widget = NULL; |
- if (toast_iter != toasts_.end()) { |
- widget = toast_iter->second->GetWidget(); |
- old_toast_ids.erase((*iter)->id()); |
- // Need to replace the contents because |view| can be updated, like |
- // image loads. |
- toast_iter->second->SetContents(view); |
- } else { |
- ToastContentsView* toast = new ToastContentsView(*iter, AsWeakPtr()); |
- widget = toast->CreateWidget(parent_); |
- toast->SetContents(view); |
- widget->AddObserver(this); |
- toast->StartTimer(); |
- toasts_[(*iter)->id()] = toast; |
+ if (toasts_.find((*iter)->id()) != toasts_.end()) { |
+ delete view; |
+ continue; |
} |
+ ToastContentsView* toast = new ToastContentsView( |
+ *iter, AsWeakPtr(), message_center_); |
+ views::Widget* widget = toast->CreateWidget(parent_); |
+ toast->SetContents(view); |
+ toast->StartTimer(); |
+ toasts_[(*iter)->id()] = toast; |
+ widgets_.push_back(widget); |
+ |
// Place/move the toast widgets. Currently it stacks the widgets from the |
// right-bottom of the work area. |
// TODO(mukai): allow to specify the placement policy from outside of this |
// class. The policy should be specified from preference on Windows, or |
// the launcher alignment on ChromeOS. |
- if (widget) { |
- gfx::Rect bounds(widget->GetWindowBoundsInScreen()); |
- bounds.set_origin(gfx::Point(left, bottom - bounds.height())); |
- widget->SetBounds(bounds); |
- if (!widget->IsVisible()) |
- widget->Show(); |
- } |
- |
+ gfx::Rect bounds(widget->GetWindowBoundsInScreen()); |
+ bounds.set_origin(gfx::Point(left, bottom - bounds.height())); |
+ widget->SetBounds(bounds); |
+ widget->Show(); |
bottom -= view_height + kMarginBetweenItems; |
} |
- |
- for (std::set<std::string>::const_iterator iter = old_toast_ids.begin(); |
- iter != old_toast_ids.end(); ++iter) { |
- ToastContainer::iterator toast_iter = toasts_.find(*iter); |
- DCHECK(toast_iter != toasts_.end()); |
- views::Widget* widget = toast_iter->second->GetWidget(); |
- widget->RemoveObserver(this); |
- widget->Close(); |
- toasts_.erase(toast_iter); |
- } |
} |
void MessagePopupCollection::OnMouseEntered() { |
@@ -262,6 +237,9 @@ void MessagePopupCollection::OnMouseExited() { |
iter != toasts_.end(); ++iter) { |
iter->second->RestartTimer(); |
} |
+ RepositionWidgets(); |
+ // Reposition could create extra space which allows additional widgets. |
+ UpdateWidgets(); |
} |
void MessagePopupCollection::CloseAllWidgets() { |
@@ -269,23 +247,139 @@ void MessagePopupCollection::CloseAllWidgets() { |
iter != toasts_.end(); ++iter) { |
iter->second->SuspendTimer(); |
views::Widget* widget = iter->second->GetWidget(); |
- widget->RemoveObserver(this); |
widget->Close(); |
} |
toasts_.clear(); |
+ widgets_.clear(); |
} |
-void MessagePopupCollection::OnWidgetDestroying(views::Widget* widget) { |
- widget->RemoveObserver(this); |
- for (ToastContainer::iterator iter = toasts_.begin(); |
- iter != toasts_.end(); ++iter) { |
- if (iter->second->GetWidget() == widget) { |
- message_center_->MarkSinglePopupAsShown(iter->first, false); |
- toasts_.erase(iter); |
- break; |
+gfx::Point MessagePopupCollection::GetWorkAreaBottomRight() { |
+ if (!work_area_.IsEmpty()) |
+ return work_area_.bottom_right(); |
+ |
+ if (!parent_) { |
+ // On Win+Aura, we don't have a parent since the popups currently show up |
+ // on the Windows desktop, not in the Aura/Ash desktop. This code will |
+ // display the popups on the primary display. |
+ gfx::Screen* screen = gfx::Screen::GetNativeScreen(); |
+ work_area_ = screen->GetPrimaryDisplay().work_area(); |
+ } else { |
+ gfx::Screen* screen = gfx::Screen::GetScreenFor(parent_); |
+ work_area_ = screen->GetDisplayNearestWindow(parent_).work_area(); |
+ } |
+ |
+ return work_area_.bottom_right(); |
+} |
+ |
+void MessagePopupCollection::RepositionWidgets() { |
+ int bottom = GetWorkAreaBottomRight().y() - kMarginBetweenItems; |
+ for (std::list<views::Widget*>::iterator iter = widgets_.begin(); |
+ iter != widgets_.end(); ++iter) { |
+ gfx::Rect bounds((*iter)->GetWindowBoundsInScreen()); |
+ bounds.set_y(bottom - bounds.height()); |
+ (*iter)->SetBounds(bounds); |
+ bottom -= bounds.height() + kMarginBetweenItems; |
+ } |
+} |
+ |
+void MessagePopupCollection::RepositionWidgetsWithTarget( |
+ const gfx::Rect& target_bounds) { |
+ if (widgets_.empty()) |
+ return; |
+ |
+ if (widgets_.back()->GetWindowBoundsInScreen().y() > target_bounds.y()) { |
+ // No widgets are above, thus slides up the widgets. |
+ int slide_length = |
+ widgets_.back()->GetWindowBoundsInScreen().y() - target_bounds.y(); |
+ for (std::list<views::Widget*>::iterator iter = widgets_.begin(); |
+ iter != widgets_.end(); ++iter) { |
+ gfx::Rect bounds((*iter)->GetWindowBoundsInScreen()); |
+ bounds.set_y(bounds.y() - slide_length); |
+ (*iter)->SetBounds(bounds); |
+ } |
+ } else { |
+ std::list<views::Widget*>::reverse_iterator iter = widgets_.rbegin(); |
+ for (; iter != widgets_.rend(); ++iter) { |
+ if ((*iter)->GetWindowBoundsInScreen().y() > target_bounds.y()) |
+ break; |
+ } |
+ --iter; |
+ int slide_length = |
+ target_bounds.y() - (*iter)->GetWindowBoundsInScreen().y(); |
+ for (; ; --iter) { |
+ gfx::Rect bounds((*iter)->GetWindowBoundsInScreen()); |
+ bounds.set_y(bounds.y() + slide_length); |
+ (*iter)->SetBounds(bounds); |
+ |
+ if (iter == widgets_.rbegin()) |
+ break; |
} |
} |
- UpdatePopups(); |
+} |
+ |
+void MessagePopupCollection::OnNotificationAdded( |
+ const std::string& notification_id) { |
+ UpdateWidgets(); |
+} |
+ |
+void MessagePopupCollection::OnNotificationRemoved( |
+ const std::string& notification_id, |
+ bool by_user) { |
+ ToastContainer::iterator iter = toasts_.find(notification_id); |
+ if (iter == toasts_.end()) |
+ return; |
+ |
+ views::Widget* widget = iter->second->GetWidget(); |
+ gfx::Rect removed_bounds = widget->GetWindowBoundsInScreen(); |
+ widget->Close(); |
+ widgets_.erase(std::find(widgets_.begin(), widgets_.end(), widget)); |
+ toasts_.erase(iter); |
+ bool widgets_went_empty = widgets_.empty(); |
+ if (by_user) |
+ RepositionWidgetsWithTarget(removed_bounds); |
+ else |
+ RepositionWidgets(); |
+ |
+ // A notification removal may create extra space which allows appearing |
+ // other notifications. |
+ UpdateWidgets(); |
+ |
+ // Also, if the removed notification is the last one but removing that enables |
+ // other notifications appearing, the newly created widgets also have to be |
+ // repositioned. |
+ if (by_user && widgets_went_empty) |
+ RepositionWidgetsWithTarget(removed_bounds); |
+} |
+ |
+void MessagePopupCollection::OnNotificationUpdated( |
+ const std::string& notification_id) { |
+ ToastContainer::iterator toast_iter = toasts_.find(notification_id); |
+ if (toast_iter == toasts_.end()) |
+ return; |
+ |
+ NotificationList::Notifications notifications = |
+ message_center_->GetNotifications(); |
+ bool updated = false; |
+ for (NotificationList::Notifications::iterator iter = |
+ notifications.begin(); iter != notifications.end(); ++iter) { |
+ if ((*iter)->id() != notification_id) |
+ continue; |
+ |
+ MessageView* view = NotificationView::Create( |
+ *(*iter), message_center_, true); |
+ toast_iter->second->SetContents(view); |
+ updated = true; |
+ } |
+ |
+ if (updated) { |
+ RepositionWidgets(); |
+ // Reposition could create extra space which allows additional widgets. |
+ UpdateWidgets(); |
+ } |
+} |
+ |
+void MessagePopupCollection::SetWorkAreaForTest(const gfx::Rect& work_area) { |
+ work_area_ = work_area; |
} |
} // namespace message_center |