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 3 // found in the LICENSE file. 4 5 #ifndef UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_COLLECTION_H_ 6 #define UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_COLLECTION_H_ 7 8 #include <list> 9 #include <map> 10 11 #include "base/compiler_specific.h" 12 #include "base/gtest_prod_util.h" 13 #include "base/memory/weak_ptr.h" 14 #include "base/timer/timer.h" 15 #include "ui/gfx/native_widget_types.h" 16 #include "ui/gfx/rect.h" 17 #include "ui/message_center/message_center_export.h" 18 #include "ui/message_center/message_center_observer.h" 19 #include "ui/message_center/views/message_center_controller.h" 20 #include "ui/message_center/views/toast_contents_view.h" 21 #include "ui/views/widget/widget_observer.h" 22 23 namespace base { 24 class RunLoop; 25 } 26 27 namespace views { 28 class Widget; 29 } 30 31 namespace gfx { 32 class Display; 33 class Screen; 34 } 35 36 namespace message_center { 37 namespace test { 38 class MessagePopupCollectionTest; 39 } 40 41 class MessageCenter; 42 class MessageCenterTray; 43 class MessageViewContextMenuController; 44 class PopupAlignmentDelegate; 45 46 // Container for popup toasts. Because each toast is a frameless window rather 47 // than a view in a bubble, now the container just manages all of those toasts. 48 // This is similar to chrome/browser/notifications/balloon_collection, but the 49 // contents of each toast are for the message center and layout strategy would 50 // be slightly different. 51 class MESSAGE_CENTER_EXPORT MessagePopupCollection 52 : public MessageCenterController, 53 public MessageCenterObserver { 54 public: 55 // |parent| specifies the parent widget of the toast windows. The default 56 // parent will be used for NULL. Usually each icon is spacing against its 57 // predecessor. 58 MessagePopupCollection(gfx::NativeView parent, 59 MessageCenter* message_center, 60 MessageCenterTray* tray, 61 PopupAlignmentDelegate* alignment_delegate); 62 virtual ~MessagePopupCollection(); 63 64 // Overridden from MessageCenterController: 65 virtual void ClickOnNotification(const std::string& notification_id) OVERRIDE; 66 virtual void RemoveNotification(const std::string& notification_id, 67 bool by_user) OVERRIDE; 68 virtual scoped_ptr<ui::MenuModel> CreateMenuModel( 69 const NotifierId& notifier_id, 70 const base::string16& display_source) OVERRIDE; 71 virtual bool HasClickedListener(const std::string& notification_id) OVERRIDE; 72 virtual void ClickOnNotificationButton(const std::string& notification_id, 73 int button_index) OVERRIDE; 74 75 void MarkAllPopupsShown(); 76 77 // Since these events are really coming from individual toast widgets, 78 // it helps to be able to keep track of the sender. 79 void OnMouseEntered(ToastContentsView* toast_entered); 80 void OnMouseExited(ToastContentsView* toast_exited); 81 82 // Invoked by toasts when they start/finish their animations. 83 // While "defer counter" is greater then zero, the popup collection does 84 // not perform updates. It is used to wait for various animations and user 85 // actions like serial closing of the toasts, when the remaining toasts "flow 86 // under the mouse". 87 void IncrementDeferCounter(); 88 void DecrementDeferCounter(); 89 90 // Runs the next step in update/animate sequence, if the defer counter is not 91 // zero. Otherwise, simply waits when it becomes zero. 92 void DoUpdateIfPossible(); 93 94 // Removes the toast from our internal list of toasts; this is called when the 95 // toast is irrevocably closed (such as within RemoveToast). 96 void ForgetToast(ToastContentsView* toast); 97 98 // Called when the display bounds has been changed. Used in Windows only. 99 void OnDisplayMetricsChanged(const gfx::Display& display); 100 101 // Used by ToastContentsView to locate itself. 102 gfx::NativeView parent() const { return parent_; } 103 104 private: 105 friend class test::MessagePopupCollectionTest; 106 typedef std::list<ToastContentsView*> Toasts; 107 108 // Iterates toasts and starts closing them. 109 std::set<std::string> CloseAllWidgets(); 110 111 // Called by ToastContentsView when its window is closed. 112 void RemoveToast(ToastContentsView* toast, bool mark_as_shown); 113 114 // Creates new widgets for new toast notifications, and updates |toasts_| and 115 // |widgets_| correctly. 116 void UpdateWidgets(); 117 118 // Repositions all of the widgets based on the current work area. 119 void RepositionWidgets(); 120 121 // Repositions widgets to the top edge of the notification toast that was 122 // just removed, so that the user can click close button without mouse moves. 123 // See crbug.com/224089 124 void RepositionWidgetsWithTarget(); 125 126 // The base line is an (imaginary) line that would touch the bottom of the 127 // next created notification if bottom-aligned or its top if top-aligned. 128 int GetBaseLine(ToastContentsView* last_toast) const; 129 130 // Overridden from MessageCenterObserver: 131 virtual void OnNotificationAdded(const std::string& notification_id) OVERRIDE; 132 virtual void OnNotificationRemoved(const std::string& notification_id, 133 bool by_user) OVERRIDE; 134 virtual void OnNotificationUpdated( 135 const std::string& notification_id) OVERRIDE; 136 137 ToastContentsView* FindToast(const std::string& notification_id) const; 138 139 // While the toasts are animated, avoid updating the collection, to reduce 140 // user confusion. Instead, update the collection when all animations are 141 // done. This method is run when defer counter is zero, may initiate next 142 // update/animation step. 143 void OnDeferTimerExpired(); 144 145 // "ForTest" methods. 146 views::Widget* GetWidgetForTest(const std::string& id) const; 147 void CreateRunLoopForTest(); 148 void WaitForTest(); 149 gfx::Rect GetToastRectAt(size_t index) const; 150 151 gfx::NativeView parent_; 152 MessageCenter* message_center_; 153 MessageCenterTray* tray_; 154 Toasts toasts_; 155 156 PopupAlignmentDelegate* alignment_delegate_; 157 158 int defer_counter_; 159 160 // This is only used to compare with incoming events, do not assume that 161 // the toast will be valid if this pointer is non-NULL. 162 ToastContentsView* latest_toast_entered_; 163 164 // Denotes a mode when user is clicking the Close button of toasts in a 165 // sequence, w/o moving the mouse. We reposition the toasts so the next one 166 // happens to be right under the mouse, and the user can just dispose of 167 // multipel toasts by clicking. The mode ends when defer_timer_ expires. 168 bool user_is_closing_toasts_by_clicking_; 169 scoped_ptr<base::OneShotTimer<MessagePopupCollection> > defer_timer_; 170 // The top edge to align the position of the next toast during 'close by 171 // clicking" mode. 172 // Only to be used when user_is_closing_toasts_by_clicking_ is true. 173 int target_top_edge_; 174 175 // Weak, only exists temporarily in tests. 176 scoped_ptr<base::RunLoop> run_loop_for_test_; 177 178 scoped_ptr<MessageViewContextMenuController> context_menu_controller_; 179 180 // Gives out weak pointers to toast contents views which have an unrelated 181 // lifetime. Must remain the last member variable. 182 base::WeakPtrFactory<MessagePopupCollection> weak_factory_; 183 184 DISALLOW_COPY_AND_ASSIGN(MessagePopupCollection); 185 }; 186 187 } // namespace message_center 188 189 #endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_COLLECTION_H_ 190