summaryrefslogtreecommitdiffstats
path: root/chrome/browser/notifications/balloon_collection_impl.h
blob: 4a006fb135605cb25e76c368af58aff87ff8b9e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Handles the visible notification (or balloons).

#ifndef CHROME_BROWSER_NOTIFICATIONS_BALLOON_COLLECTION_IMPL_H_
#define CHROME_BROWSER_NOTIFICATIONS_BALLOON_COLLECTION_IMPL_H_

#include <deque>

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "chrome/browser/notifications/balloon_collection.h"
#include "chrome/browser/notifications/balloon_collection_base.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"

// Mac balloons grow from the top down and have close buttons on top, so
// offsetting is not necessary for easy multiple-closing.  Other platforms grow
// from the bottom up and have close buttons on top, so it is necessary.
#if defined(OS_MACOSX)
#define USE_OFFSETS 0
#else
#define USE_OFFSETS 1
#endif

// A balloon collection represents a set of notification balloons being
// shown on the screen.  It positions new notifications according to
// a layout, and monitors for balloons being closed, which it reports
// up to its parent, the notification UI manager.
class BalloonCollectionImpl : public BalloonCollection,
                              public content::NotificationObserver
#if USE_OFFSETS
                            , public base::MessageLoopForUI::Observer
#endif
{
 public:
  BalloonCollectionImpl();
  virtual ~BalloonCollectionImpl();

  // BalloonCollection interface.
  virtual void Add(const Notification& notification,
                   Profile* profile) OVERRIDE;
  virtual const Notification* FindById(const std::string& id) const OVERRIDE;
  virtual bool RemoveById(const std::string& id) OVERRIDE;
  virtual bool RemoveBySourceOrigin(const GURL& source_origin) OVERRIDE;
  virtual bool RemoveByProfile(Profile* profile) OVERRIDE;
  virtual void RemoveAll() OVERRIDE;
  virtual bool HasSpace() const OVERRIDE;
  virtual void ResizeBalloon(Balloon* balloon, const gfx::Size& size) OVERRIDE;
  virtual void SetPositionPreference(PositionPreference position) OVERRIDE;
  virtual void DisplayChanged() OVERRIDE;
  virtual void OnBalloonClosed(Balloon* source) OVERRIDE;
  virtual const Balloons& GetActiveBalloons() OVERRIDE;

  // content::NotificationObserver interface.
  virtual void Observe(int type,
                       const content::NotificationSource& source,
                       const content::NotificationDetails& details) OVERRIDE;

  // MessageLoopForUI::Observer interface.
#if defined(OS_WIN) || defined(USE_AURA)
  virtual base::EventStatus WillProcessEvent(
      const base::NativeEvent& event) OVERRIDE;
  virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE;
#elif defined(TOOLKIT_GTK)
  virtual void WillProcessEvent(GdkEvent* event) OVERRIDE;
  virtual void DidProcessEvent(GdkEvent* event) OVERRIDE;
#endif

  // base_ is embedded, so this is a simple accessor for the number of
  // balloons in the collection.
  int count() const { return base_.count(); }

  static int min_balloon_width() { return Layout::min_balloon_width(); }
  static int max_balloon_width() { return Layout::max_balloon_width(); }
  static int min_balloon_height() { return Layout::min_balloon_height(); }
  static int max_balloon_height() { return Layout::max_balloon_height(); }

 protected:
  // Calculates layout values for the balloons including
  // the scaling, the max/min sizes, and the upper left corner of each.
  class Layout {
   public:
    Layout();

    // These enumerations all are based on a screen orientation where
    // the origin is the top-left.
    enum Placement {
      INVALID,
      VERTICALLY_FROM_TOP_LEFT,
      VERTICALLY_FROM_TOP_RIGHT,
      VERTICALLY_FROM_BOTTOM_LEFT,
      VERTICALLY_FROM_BOTTOM_RIGHT
    };

    // Refresh the work area and balloon placement.
    void OnDisplaySettingsChanged();

    // TODO(johnnyg): Scale the size to account for the system font factor.
    static int min_balloon_width() { return kBalloonMinWidth; }
    static int max_balloon_width() { return kBalloonMaxWidth; }
    static int min_balloon_height() { return kBalloonMinHeight; }
    static int max_balloon_height() { return kBalloonMaxHeight; }

    // Utility function constrains the input rectangle to the min and max sizes.
    static gfx::Size ConstrainToSizeLimits(const gfx::Size& rect);

    void set_placement(Placement placement) { placement_ = placement; }

    // Returns both the total space available and the maximum
    // allowed per balloon.
    //
    // The size may be a height or length depending on the way that
    // balloons are laid out.
    void GetMaxLinearSize(int* max_balloon_size, int* total_size) const;

    // Refresh the cached values for work area and drawing metrics.
    // The application should call this method to re-acquire metrics after
    // any resolution or settings change.
    // Returns true if and only if a metric changed.
    bool RefreshSystemMetrics();

    // Returns the origin for the sequence of balloons depending on layout.
    // Should not be used to place a balloon -- only to call NextPosition().
    gfx::Point GetLayoutOrigin() const;

    // Compute the position for the next balloon.
    // Start with *position_iterator = GetLayoutOrigin() and call repeatedly
    // to get a sequence of positions. Return value is the upper-left coordinate
    // for each next balloon.
    gfx::Point NextPosition(const gfx::Size& balloon_size,
                            gfx::Point* position_iterator) const;

    // Return a offscreen location which is offscreen for this layout,
    // to be used as the initial position for an animation into view.
    gfx::Point OffScreenLocation() const;

    // Returns true if the layout requires offsetting for keeping the close
    // buttons under the cursor during rapid-close interaction.
    bool RequiresOffsets() const;

    // Returns true if there is change to the offset that requires the balloons
    // to be repositioned.
    bool ComputeOffsetToMoveAbovePanels();

    void enable_computing_panel_offset() {
      need_to_compute_panel_offset_ = true;
    }

   private:
    // Layout parameters
    int VerticalEdgeMargin() const;
    int HorizontalEdgeMargin() const;
    int InterBalloonMargin() const;

    bool NeedToMoveAboveLeftSidePanels() const;
    bool NeedToMoveAboveRightSidePanels() const;

    // Minimum and maximum size of balloon content.
    static const int kBalloonMinWidth = 300;
    static const int kBalloonMaxWidth = 300;
    static const int kBalloonMinHeight = 24;
    static const int kBalloonMaxHeight = 160;

    Placement placement_;
    gfx::Rect work_area_;

    // The flag that indicates that the panel offset computation should be
    // performed.
    bool need_to_compute_panel_offset_;

    // The offset that guarantees that the notificaitions shown in the
    // lower-right or lower-left corner of the screen will go above currently
    // shown panels and will not be obscured by them.
    int offset_to_move_above_panels_;

    DISALLOW_COPY_AND_ASSIGN(Layout);
  };

  // Creates a new balloon. Overridable by derived classes and unit tests.
  // The caller is responsible for freeing the pointer returned.
  virtual Balloon* MakeBalloon(const Notification& notification,
                               Profile* profile);

  // Protected implementation of Add with additional add_to_front parameter
  // for use by derived classes.
  void AddImpl(const Notification& notification,
               Profile* profile,
               bool add_to_front);

  // Gets a bounding box for all the current balloons in screen coordinates.
  gfx::Rect GetBalloonsBoundingBox() const;

  BalloonCollectionBase& base() { return base_; }
  Layout& layout() { return layout_; }

 private:
  // Adjusts the positions of the balloons (e.g., when one is closed).
  // Implemented by each platform for specific UI requirements.
  void PositionBalloons(bool is_reposition);

  // Cross-platform internal implementation for PositionBalloons.
  void PositionBalloonsInternal(bool is_reposition);

#if defined(OS_MACOSX)
  // Get the work area on Mac OS, without inverting the coordinates.
  static gfx::Rect GetMacWorkArea();
#endif

  // Base implementation for the collection of active balloons.
  BalloonCollectionBase base_;

  // The layout parameters for balloons in this collection.
  Layout layout_;

  content::NotificationRegistrar registrar_;

#if USE_OFFSETS
  // Start and stop observing all UI events.
  void AddMessageLoopObserver();
  void RemoveMessageLoopObserver();

  // Cancel all offset and reposition the balloons normally.
  void CancelOffsets();

  // Handles a mouse motion while the balloons are temporarily offset.
  void HandleMouseMoveEvent();

  // Is the current cursor in the balloon area?
  bool IsCursorInBalloonCollection() const;

  // Factory for generating delayed reposition tasks on mouse motion.
  base::WeakPtrFactory<BalloonCollectionImpl> reposition_factory_;

  // Is the balloon collection currently listening for UI events?
  bool added_as_message_loop_observer_;
#endif

  DISALLOW_COPY_AND_ASSIGN(BalloonCollectionImpl);
};

#endif  // CHROME_BROWSER_NOTIFICATIONS_BALLOON_COLLECTION_IMPL_H_