OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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 "app/gtk_signal.h" |
| 21 #include "app/gtk_signal_registrar.h" |
| 22 #include "base/basictypes.h" |
| 23 #include "chrome/common/notification_observer.h" |
| 24 #include "chrome/common/notification_registrar.h" |
| 25 #include "gfx/point.h" |
| 26 #include "gfx/rect.h" |
| 27 |
| 28 class GtkThemeProvider; |
| 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: |
| 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 GtkThemeProvider* 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(GtkThemeProvider* 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. |
| 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 GtkThemeProvider* theme_provider_; |
| 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 GtkSignalRegistrar signals_; |
| 211 |
| 212 DISALLOW_COPY_AND_ASSIGN(InfoBubbleGtk); |
| 213 }; |
| 214 |
| 215 #endif // CHROME_BROWSER_UI_GTK_INFO_BUBBLE_GTK_H_ |
OLD | NEW |