message_popup_collection.h revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#ifndef UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_COLLECTION_H_
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#define UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_COLLECTION_H_
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <list>
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <map>
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/compiler_specific.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/gtest_prod_util.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/memory/weak_ptr.h"
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/timer/timer.h"
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "ui/gfx/display.h"
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "ui/gfx/display_observer.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/native_widget_types.h"
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/rect.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/message_center_export.h"
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/message_center/message_center_observer.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/widget/widget_observer.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace base {
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class RunLoop;
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace views {
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class Widget;
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace ash {
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)FORWARD_DECLARE_TEST(WebNotificationTrayTest, ManyPopupNotifications);
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace message_center {
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace test {
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class MessagePopupCollectionTest;
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class MessagePopupCollectionWidgetsTest;
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class MessageCenter;
42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochclass MessageCenterTray;
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class ToastContentsView;
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Container for popup toasts. Because each toast is a frameless window rather
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// than a view in a bubble, now the container just manages all of those toasts.
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This is similar to chrome/browser/notifications/balloon_collection, but the
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// contents of each toast are for the message center and layout strategy would
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// be slightly different.
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class MESSAGE_CENTER_EXPORT MessagePopupCollection
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : public MessageCenterObserver,
52eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      public gfx::DisplayObserver,
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      public base::SupportsWeakPtr<MessagePopupCollection> {
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // |parent| specifies the parent widget of the toast windows. The default
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // parent will be used for NULL.
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  MessagePopupCollection(gfx::NativeView parent,
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                         MessageCenter* message_center,
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                         MessageCenterTray* tray);
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual ~MessagePopupCollection();
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Called by ToastContentsView when its window is closed.
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void RemoveToast(ToastContentsView* toast);
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Since these events are really coming from individual toast widgets,
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // it helps to be able to keep track of the sender.
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void OnMouseEntered(ToastContentsView* toast_entered);
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void OnMouseExited(ToastContentsView* toast_exited);
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Invoked by toasts when they start/finish their animations.
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // While "defer counter" is greater then zero, the popup collection does
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // not perform updates. It is used to wait for various animations and user
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // actions like serial closing of the toasts, when the remaining toasts "flow
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // under the mouse".
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void IncrementDeferCounter();
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void DecrementDeferCounter();
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Runs the next step in update/animate sequence, if the defer counter is not
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // zero. Otherwise, simply waits when it becomes zero.
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void DoUpdateIfPossible();
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
82ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Updates |work_area_| and rearranges the notification toasts if necessary.
83ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // This is separated from methods from OnDisplayBoundsChanged(), since
84ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // sometimes a work area has to be specified directly. One example is shelf's
85ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // auto-hide change. When the shelf in ChromeOS is temporarily shown from auto
86ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // hide status, it doesn't change the display's work area but the actual work
87ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // area for toasts should be resized.
88ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  void SetWorkArea(const gfx::Rect& work_area);
89ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Overridden from gfx::DislayObserver:
91eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE;
92eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE;
93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE;
94eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FRIEND_TEST_ALL_PREFIXES(ash::WebNotificationTrayTest,
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           ManyPopupNotifications);
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  friend class test::MessagePopupCollectionTest;
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  friend class test::MessagePopupCollectionWidgetsTest;
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  typedef std::list<ToastContentsView*> Toasts;
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void CloseAllWidgets();
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
104eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Returns the x-origin for the given toast bounds in the current work area.
105eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int GetToastOriginX(const gfx::Rect& toast_bounds);
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Iterates toasts and starts closing the expired ones.
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void CloseToasts();
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Creates new widgets for new toast notifications, and updates |toasts_| and
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // |widgets_| correctly.
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void UpdateWidgets();
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Repositions all of the widgets based on the current work area.
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void RepositionWidgets();
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Repositions widgets to the top edge of the notification toast that was
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // just removed, so that the user can click close button without mouse moves.
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // See crbug.com/224089
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void RepositionWidgetsWithTarget();
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Overridden from MessageCenterObserver:
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual void OnNotificationAdded(const std::string& notification_id) OVERRIDE;
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual void OnNotificationRemoved(const std::string& notification_id,
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                     bool by_user) OVERRIDE;
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual void OnNotificationUpdated(
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      const std::string& notification_id) OVERRIDE;
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
129868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  ToastContentsView* FindToast(const std::string& notification_id);
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // While the toasts are animated, avoid updating the collection, to reduce
132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // user confusion. Instead, update the collection when all animations are
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // done. This method is run when defer counter is zero, may initiate next
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // update/animation step.
135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void PerformDeferredTasks();
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void OnDeferTimerExpired();
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // "ForTest" methods.
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  views::Widget* GetWidgetForTest(const std::string& id);
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void RunLoopForTest();
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Rect GetToastRectAt(size_t index);
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::NativeView parent_;
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  MessageCenter* message_center_;
145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  MessageCenterTray* tray_;
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Toasts toasts_;
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Rect work_area_;
148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int64 display_id_;
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int defer_counter_;
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // This is only used to compare with incoming events, do not assume that
153c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // the toast will be valid if this pointer is non-NULL.
154c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ToastContentsView* latest_toast_entered_;
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Denotes a mode when user is clicking the Close button of toasts in a
157c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // sequence, w/o moving the mouse. We reposition the toasts so the next one
158c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // happens to be right under the mouse, and the user can just dispose of
159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // multipel toasts by clicking. The mode ends when defer_timer_ expires.
160c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool user_is_closing_toasts_by_clicking_;
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_ptr<base::OneShotTimer<MessagePopupCollection> > defer_timer_;
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The top edge to align the position of the next toast during 'close by
163c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // clicking" mode.
164c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Only to be used when user_is_closing_toasts_by_clicking_ is true.
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int target_top_edge_;
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
167c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Weak, only exists temporarily in tests.
168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_ptr<base::RunLoop> run_loop_for_test_;
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(MessagePopupCollection);
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace message_center
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_COLLECTION_H_
176