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

Unified Diff: ui/message_center/views/message_popup_collection.cc

Issue 14139014: Reposition toasts to align the closed one. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix Created 7 years, 8 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 side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « ui/message_center/views/message_popup_collection.h ('k') | ui/message_center/views/message_popup_collection_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698