Home | History | Annotate | Download | only in views
      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/display.h"
     16 #include "ui/gfx/display_observer.h"
     17 #include "ui/gfx/native_widget_types.h"
     18 #include "ui/gfx/rect.h"
     19 #include "ui/message_center/message_center_export.h"
     20 #include "ui/message_center/message_center_observer.h"
     21 #include "ui/message_center/views/message_center_controller.h"
     22 #include "ui/message_center/views/toast_contents_view.h"
     23 #include "ui/views/widget/widget_observer.h"
     24 
     25 namespace base {
     26 class RunLoop;
     27 }
     28 
     29 namespace views {
     30 class Widget;
     31 }
     32 
     33 namespace ash {
     34 class WebNotificationTrayTest;
     35 FORWARD_DECLARE_TEST(WebNotificationTrayTest, ManyPopupNotifications);
     36 }
     37 
     38 namespace gfx {
     39 class Screen;
     40 }
     41 
     42 namespace message_center {
     43 namespace test {
     44 class MessagePopupCollectionTest;
     45 }
     46 
     47 class MessageCenter;
     48 class MessageCenterTray;
     49 class MessageViewContextMenuController;
     50 
     51 enum PopupAlignment {
     52   POPUP_ALIGNMENT_TOP = 1 << 0,
     53   POPUP_ALIGNMENT_LEFT = 1 << 1,
     54   POPUP_ALIGNMENT_BOTTOM = 1 << 2,
     55   POPUP_ALIGNMENT_RIGHT = 1 << 3,
     56 };
     57 
     58 // Container for popup toasts. Because each toast is a frameless window rather
     59 // than a view in a bubble, now the container just manages all of those toasts.
     60 // This is similar to chrome/browser/notifications/balloon_collection, but the
     61 // contents of each toast are for the message center and layout strategy would
     62 // be slightly different.
     63 class MESSAGE_CENTER_EXPORT MessagePopupCollection
     64     : public MessageCenterController,
     65       public MessageCenterObserver,
     66       public gfx::DisplayObserver {
     67  public:
     68   // |parent| specifies the parent widget of the toast windows. The default
     69   // parent will be used for NULL. Usually each icon is spacing against its
     70   // predecessor. If |first_item_has_no_margin| is set however the first item
     71   // does not space against the tray.
     72   MessagePopupCollection(gfx::NativeView parent,
     73                          MessageCenter* message_center,
     74                          MessageCenterTray* tray,
     75                          bool first_item_has_no_margin);
     76   virtual ~MessagePopupCollection();
     77 
     78   // Overridden from MessageCenterController:
     79   virtual void ClickOnNotification(const std::string& notification_id) OVERRIDE;
     80   virtual void RemoveNotification(const std::string& notification_id,
     81                                   bool by_user) OVERRIDE;
     82   virtual scoped_ptr<ui::MenuModel> CreateMenuModel(
     83       const NotifierId& notifier_id,
     84       const base::string16& display_source) OVERRIDE;
     85   virtual bool HasClickedListener(const std::string& notification_id) OVERRIDE;
     86   virtual void ClickOnNotificationButton(const std::string& notification_id,
     87                                          int button_index) OVERRIDE;
     88 
     89   void MarkAllPopupsShown();
     90 
     91   // Since these events are really coming from individual toast widgets,
     92   // it helps to be able to keep track of the sender.
     93   void OnMouseEntered(ToastContentsView* toast_entered);
     94   void OnMouseExited(ToastContentsView* toast_exited);
     95 
     96   // Invoked by toasts when they start/finish their animations.
     97   // While "defer counter" is greater then zero, the popup collection does
     98   // not perform updates. It is used to wait for various animations and user
     99   // actions like serial closing of the toasts, when the remaining toasts "flow
    100   // under the mouse".
    101   void IncrementDeferCounter();
    102   void DecrementDeferCounter();
    103 
    104   // Runs the next step in update/animate sequence, if the defer counter is not
    105   // zero. Otherwise, simply waits when it becomes zero.
    106   void DoUpdateIfPossible();
    107 
    108   // Removes the toast from our internal list of toasts; this is called when the
    109   // toast is irrevocably closed (such as within RemoveToast).
    110   void ForgetToast(ToastContentsView* toast);
    111 
    112   // Updates |work_area_| and re-calculates the alignment of notification toasts
    113   // rearranging them if necessary.
    114   // This is separated from methods from OnDisplayMetricsChanged(), since
    115   // sometimes the display info has to be specified directly. One example is
    116   // shelf's auto-hide change. When the shelf in ChromeOS is temporarily shown
    117   // from auto hide status, it doesn't change the display's work area but the
    118   // actual work area for toasts should be resized.
    119   void SetDisplayInfo(const gfx::Rect& work_area,
    120                       const gfx::Rect& screen_bounds);
    121 
    122   // Overridden from gfx::DislayObserver:
    123   virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE;
    124   virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE;
    125   virtual void OnDisplayMetricsChanged(const gfx::Display& display,
    126                                        uint32_t metrics) OVERRIDE;
    127 
    128   // Used by ToastContentsView to locate itself.
    129   gfx::NativeView parent() const { return parent_; }
    130 
    131  private:
    132   FRIEND_TEST_ALL_PREFIXES(ash::WebNotificationTrayTest,
    133                            ManyPopupNotifications);
    134   friend class test::MessagePopupCollectionTest;
    135   friend class ash::WebNotificationTrayTest;
    136   typedef std::list<ToastContentsView*> Toasts;
    137 
    138   // Iterates toasts and starts closing them.
    139   std::set<std::string> CloseAllWidgets();
    140 
    141   // Called by ToastContentsView when its window is closed.
    142   void RemoveToast(ToastContentsView* toast, bool mark_as_shown);
    143 
    144   // Returns the x-origin for the given toast bounds in the current work area.
    145   int GetToastOriginX(const gfx::Rect& toast_bounds) const;
    146 
    147   // Creates new widgets for new toast notifications, and updates |toasts_| and
    148   // |widgets_| correctly.
    149   void UpdateWidgets();
    150 
    151   // Repositions all of the widgets based on the current work area.
    152   void RepositionWidgets();
    153 
    154   // Repositions widgets to the top edge of the notification toast that was
    155   // just removed, so that the user can click close button without mouse moves.
    156   // See crbug.com/224089
    157   void RepositionWidgetsWithTarget();
    158 
    159   void ComputePopupAlignment(gfx::Rect work_area, gfx::Rect screen_bounds);
    160 
    161   // The base line is an (imaginary) line that would touch the bottom of the
    162   // next created notification if bottom-aligned or its top if top-aligned.
    163   int GetBaseLine(ToastContentsView* last_toast) const;
    164 
    165   // Overridden from MessageCenterObserver:
    166   virtual void OnNotificationAdded(const std::string& notification_id) OVERRIDE;
    167   virtual void OnNotificationRemoved(const std::string& notification_id,
    168                                      bool by_user) OVERRIDE;
    169   virtual void OnNotificationUpdated(
    170       const std::string& notification_id) OVERRIDE;
    171 
    172   ToastContentsView* FindToast(const std::string& notification_id) const;
    173 
    174   // While the toasts are animated, avoid updating the collection, to reduce
    175   // user confusion. Instead, update the collection when all animations are
    176   // done. This method is run when defer counter is zero, may initiate next
    177   // update/animation step.
    178   void OnDeferTimerExpired();
    179 
    180   // "ForTest" methods.
    181   views::Widget* GetWidgetForTest(const std::string& id) const;
    182   void CreateRunLoopForTest();
    183   void WaitForTest();
    184   gfx::Rect GetToastRectAt(size_t index) const;
    185 
    186   gfx::NativeView parent_;
    187   MessageCenter* message_center_;
    188   MessageCenterTray* tray_;
    189   Toasts toasts_;
    190   gfx::Rect work_area_;
    191   int64 display_id_;
    192   gfx::Screen* screen_;
    193 
    194   // Specifies which corner of the screen popups should show up. This should
    195   // ideally be the same corner the notification area (systray) is at.
    196   PopupAlignment alignment_;
    197 
    198   int defer_counter_;
    199 
    200   // This is only used to compare with incoming events, do not assume that
    201   // the toast will be valid if this pointer is non-NULL.
    202   ToastContentsView* latest_toast_entered_;
    203 
    204   // Denotes a mode when user is clicking the Close button of toasts in a
    205   // sequence, w/o moving the mouse. We reposition the toasts so the next one
    206   // happens to be right under the mouse, and the user can just dispose of
    207   // multipel toasts by clicking. The mode ends when defer_timer_ expires.
    208   bool user_is_closing_toasts_by_clicking_;
    209   scoped_ptr<base::OneShotTimer<MessagePopupCollection> > defer_timer_;
    210   // The top edge to align the position of the next toast during 'close by
    211   // clicking" mode.
    212   // Only to be used when user_is_closing_toasts_by_clicking_ is true.
    213   int target_top_edge_;
    214 
    215   // Weak, only exists temporarily in tests.
    216   scoped_ptr<base::RunLoop> run_loop_for_test_;
    217 
    218   // True if the first item should not have spacing against the tray.
    219   bool first_item_has_no_margin_;
    220 
    221   scoped_ptr<MessageViewContextMenuController> context_menu_controller_;
    222 
    223   // Gives out weak pointers to toast contents views which have an unrelated
    224   // lifetime.  Must remain the last member variable.
    225   base::WeakPtrFactory<MessagePopupCollection> weak_factory_;
    226 
    227   DISALLOW_COPY_AND_ASSIGN(MessagePopupCollection);
    228 };
    229 
    230 }  // namespace message_center
    231 
    232 #endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_POPUP_COLLECTION_H_
    233