• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 // This is the GTK implementation of InfoBubbles.  InfoBubbles are like
6 // dialogs, but they point to a given element on the screen.  You should call
7 // InfoBubbleGtk::Show, which will create and display a bubble.  The object is
8 // self deleting, when the bubble is closed, you will be notified via
9 // InfoBubbleGtkDelegate::InfoBubbleClosing().  Then the widgets and the
10 // underlying object will be destroyed.  You can also close and destroy the
11 // bubble by calling Close().
12 
13 #ifndef CHROME_BROWSER_UI_GTK_INFO_BUBBLE_GTK_H_
14 #define CHROME_BROWSER_UI_GTK_INFO_BUBBLE_GTK_H_
15 #pragma once
16 
17 #include <gtk/gtk.h>
18 #include <vector>
19 
20 #include "base/basictypes.h"
21 #include "content/common/notification_observer.h"
22 #include "content/common/notification_registrar.h"
23 #include "ui/base/gtk/gtk_signal.h"
24 #include "ui/base/gtk/gtk_signal_registrar.h"
25 #include "ui/gfx/point.h"
26 #include "ui/gfx/rect.h"
27 
28 class GtkThemeService;
29 class InfoBubbleGtk;
30 namespace gfx {
31 class Rect;
32 }
33 
34 class InfoBubbleGtkDelegate {
35  public:
36   // Called when the InfoBubble is closing and is about to be deleted.
37   // |closed_by_escape| is true if the close is the result of pressing escape.
38   virtual void InfoBubbleClosing(InfoBubbleGtk* info_bubble,
39                                  bool closed_by_escape) = 0;
40 
41   // NOTE: The Views interface has CloseOnEscape, except I can't find a place
42   // where it ever returns false, so we always allow you to close via escape.
43 
44  protected:
~InfoBubbleGtkDelegate()45   virtual ~InfoBubbleGtkDelegate() {}
46 };
47 
48 class InfoBubbleGtk : public NotificationObserver {
49  public:
50   // Where should the arrow be placed relative to the bubble?
51   enum ArrowLocationGtk {
52     // TODO(derat): Support placing arrows on the bottoms of the bubbles.
53     ARROW_LOCATION_TOP_LEFT,
54     ARROW_LOCATION_TOP_RIGHT,
55   };
56 
57   // Show an InfoBubble, pointing at the area |rect| (in coordinates relative to
58   // |anchor_widget|'s origin).  An info bubble will try to fit on the screen,
59   // so it can point to any edge of |rect|.  If |rect| is NULL, the widget's
60   // entire area will be used. The bubble will host the |content|
61   // widget.  Its arrow will be drawn at |arrow_location| if possible.  The
62   // |delegate| will be notified when the bubble is closed.  The bubble will
63   // perform an X grab of the pointer and keyboard, and will close itself if a
64   // click is received outside of the bubble.
65   static InfoBubbleGtk* Show(GtkWidget* anchor_widget,
66                              const gfx::Rect* rect,
67                              GtkWidget* content,
68                              ArrowLocationGtk arrow_location,
69                              bool match_system_theme,
70                              bool grab_input,
71                              GtkThemeService* provider,
72                              InfoBubbleGtkDelegate* delegate);
73 
74   // Close the bubble if it's open.  This will delete the widgets and object,
75   // so you shouldn't hold a InfoBubbleGtk pointer after calling Close().
76   void Close();
77 
78   // NotificationObserver implementation.
79   virtual void Observe(NotificationType type,
80                        const NotificationSource& source,
81                        const NotificationDetails& details);
82 
83   // If the content contains widgets that can steal our pointer and keyboard
84   // grabs (e.g. GtkComboBox), this method should be called after a widget
85   // releases the grabs so we can reacquire them.  Note that this causes a race
86   // condition; another client could grab them before we do (ideally, GDK would
87   // transfer the grabs back to us when the widget releases them).  The window
88   // is small, though, and the worst-case scenario for this seems to just be
89   // that the content's widgets will appear inactive even after the user clicks
90   // in them.
91   void HandlePointerAndKeyboardUngrabbedByContent();
92 
93  private:
94   enum FrameType {
95     FRAME_MASK,
96     FRAME_STROKE,
97   };
98 
99   explicit InfoBubbleGtk(GtkThemeService* provider, bool match_system_theme);
100   virtual ~InfoBubbleGtk();
101 
102   // Creates the InfoBubble.
103   void Init(GtkWidget* anchor_widget,
104             const gfx::Rect* rect,
105             GtkWidget* content,
106             ArrowLocationGtk arrow_location,
107             bool grab_input);
108 
109   // Make the points for our polygon frame, either for fill (the mask), or for
110   // when we stroke the border.
111   static std::vector<GdkPoint> MakeFramePolygonPoints(
112       ArrowLocationGtk arrow_location,
113       int width,
114       int height,
115       FrameType type);
116 
117   // Get the location where the arrow should be placed (which is a function of
118   // the preferred location and of the direction that the bubble should be
119   // facing to fit onscreen).  |arrow_x| is the X component in screen
120   // coordinates of the point at which the bubble's arrow should be aimed, and
121   // |width| is the bubble's width.
122   static ArrowLocationGtk GetArrowLocation(
123       ArrowLocationGtk preferred_location, int arrow_x, int width);
124 
125   // Updates |arrow_location_| based on the toplevel window's current position
126   // and the bubble's size.  If the |force_move_and_reshape| is true or the
127   // location changes, moves and reshapes the window and returns true.
128   bool UpdateArrowLocation(bool force_move_and_reshape);
129 
130   // Reshapes the window and updates |mask_region_|.
131   void UpdateWindowShape();
132 
133   // Calculate the current screen position for the bubble's window (per
134   // |toplevel_window_|'s position as of its most-recent ConfigureNotify event
135   // and |rect_|) and move it there.
136   void MoveWindow();
137 
138   // Restack the bubble's window directly above |toplevel_window_|.
139   void StackWindow();
140 
141   // Sets the delegate.
set_delegate(InfoBubbleGtkDelegate * delegate)142   void set_delegate(InfoBubbleGtkDelegate* delegate) { delegate_ = delegate; }
143 
144   // Grab (in the X sense) the pointer and keyboard.  This is needed to make
145   // sure that we have the input focus.
146   void GrabPointerAndKeyboard();
147 
148   CHROMEG_CALLBACK_3(InfoBubbleGtk, gboolean, OnGtkAccelerator, GtkAccelGroup*,
149                      GObject*, guint, GdkModifierType);
150 
151   CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnExpose, GdkEventExpose*);
152   CHROMEGTK_CALLBACK_1(InfoBubbleGtk, void, OnSizeAllocate, GtkAllocation*);
153   CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnButtonPress, GdkEventButton*);
154   CHROMEGTK_CALLBACK_0(InfoBubbleGtk, gboolean, OnDestroy);
155   CHROMEGTK_CALLBACK_0(InfoBubbleGtk, void, OnHide);
156   CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnToplevelConfigure,
157                        GdkEventConfigure*);
158   CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnToplevelUnmap, GdkEvent*);
159   CHROMEGTK_CALLBACK_1(InfoBubbleGtk, void, OnAnchorAllocate, GtkAllocation*);
160 
161   // The caller supplied delegate, can be NULL.
162   InfoBubbleGtkDelegate* delegate_;
163 
164   // Our GtkWindow popup window, we don't technically "own" the widget, since
165   // it deletes us when it is destroyed.
166   GtkWidget* window_;
167 
168   // Provides colors and stuff.
169   GtkThemeService* theme_service_;
170 
171   // The accel group attached to |window_|, to handle closing with escape.
172   GtkAccelGroup* accel_group_;
173 
174   // The window for which we're being shown (and to which |rect_| is relative).
175   // Note that it's possible for |toplevel_window_| to be NULL if the
176   // window is destroyed before this object is destroyed, so it's important
177   // to check for that case.
178   GtkWindow* toplevel_window_;
179 
180   // The widget that we use to relatively position the popup window.
181   GtkWidget* anchor_widget_;
182 
183   // Provides an offset from |anchor_widget_|'s origin for MoveWindow() to
184   // use.
185   gfx::Rect rect_;
186 
187   // The current shape of |window_| (used to test whether clicks fall in it or
188   // not).
189   GdkRegion* mask_region_;
190 
191   // Where would we prefer for the arrow be drawn relative to the bubble, and
192   // where is it currently drawn?
193   ArrowLocationGtk preferred_arrow_location_;
194   ArrowLocationGtk current_arrow_location_;
195 
196   // Whether the background should match the system theme, when the system theme
197   // is being used. For example, the bookmark bubble does, but extension popups
198   // do not.
199   bool match_system_theme_;
200 
201   // If true, the popup owns all X input for the duration of its existence.
202   // This will usually be true, the exception being when inspecting extension
203   // popups with dev tools.
204   bool grab_input_;
205 
206   bool closed_by_escape_;
207 
208   NotificationRegistrar registrar_;
209 
210   ui::GtkSignalRegistrar signals_;
211 
212   DISALLOW_COPY_AND_ASSIGN(InfoBubbleGtk);
213 };
214 
215 #endif  // CHROME_BROWSER_UI_GTK_INFO_BUBBLE_GTK_H_
216