diff options
Diffstat (limited to 'chrome/browser/gtk/info_bubble_gtk.h')
-rw-r--r-- | chrome/browser/gtk/info_bubble_gtk.h | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/chrome/browser/gtk/info_bubble_gtk.h b/chrome/browser/gtk/info_bubble_gtk.h new file mode 100644 index 0000000..d13a965 --- /dev/null +++ b/chrome/browser/gtk/info_bubble_gtk.h @@ -0,0 +1,212 @@ +// Copyright (c) 2009 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. + +// This is the GTK implementation of InfoBubbles. InfoBubbles are like +// dialogs, but they point to a given element on the screen. You should call +// InfoBubbleGtk::Show, which will create and display a bubble. The object is +// self deleting, when the bubble is closed, you will be notified via +// InfoBubbleGtkDelegate::InfoBubbleClosing(). Then the widgets and the +// underlying object will be destroyed. You can also close and destroy the +// bubble by calling Close(). + +#ifndef CHROME_BROWSER_GTK_INFO_BUBBLE_GTK_H_ +#define CHROME_BROWSER_GTK_INFO_BUBBLE_GTK_H_ + +#include <gtk/gtk.h> + +#include "app/gtk_signal.h" +#include "app/gtk_signal_registrar.h" +#include "base/basictypes.h" +#include "chrome/common/notification_registrar.h" +#include "gfx/point.h" +#include "gfx/rect.h" + +class GtkThemeProvider; +class InfoBubbleGtk; +namespace gfx { +class Rect; +} + +class InfoBubbleGtkDelegate { + public: + // Called when the InfoBubble is closing and is about to be deleted. + // |closed_by_escape| is true if the close is the result of pressing escape. + virtual void InfoBubbleClosing(InfoBubbleGtk* info_bubble, + bool closed_by_escape) = 0; + + // NOTE: The Views interface has CloseOnEscape, except I can't find a place + // where it ever returns false, so we always allow you to close via escape. + + protected: + virtual ~InfoBubbleGtkDelegate() {} +}; + +class InfoBubbleGtk : public NotificationObserver { + public: + // Where should the arrow be placed relative to the bubble? + enum ArrowLocationGtk { + // TODO(derat): Support placing arrows on the bottoms of the bubbles. + ARROW_LOCATION_TOP_LEFT, + ARROW_LOCATION_TOP_RIGHT, + }; + + // Show an InfoBubble, pointing at the area |rect| (in coordinates relative to + // |anchor_widget|'s origin). An info bubble will try to fit on the screen, + // so it can point to any edge of |rect|. If |rect| is NULL, the widget's + // entire area will be used. The bubble will host the |content| + // widget. Its arrow will be drawn at |arrow_location| if possible. The + // |delegate| will be notified when the bubble is closed. The bubble will + // perform an X grab of the pointer and keyboard, and will close itself if a + // click is received outside of the bubble. + static InfoBubbleGtk* Show(GtkWidget* anchor_widget, + const gfx::Rect* rect, + GtkWidget* content, + ArrowLocationGtk arrow_location, + bool match_system_theme, + bool grab_input, + GtkThemeProvider* provider, + InfoBubbleGtkDelegate* delegate); + + // Close the bubble if it's open. This will delete the widgets and object, + // so you shouldn't hold a InfoBubbleGtk pointer after calling Close(). + void Close(); + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // If the content contains widgets that can steal our pointer and keyboard + // grabs (e.g. GtkComboBox), this method should be called after a widget + // releases the grabs so we can reacquire them. Note that this causes a race + // condition; another client could grab them before we do (ideally, GDK would + // transfer the grabs back to us when the widget releases them). The window + // is small, though, and the worst-case scenario for this seems to just be + // that the content's widgets will appear inactive even after the user clicks + // in them. + void HandlePointerAndKeyboardUngrabbedByContent(); + + private: + enum FrameType { + FRAME_MASK, + FRAME_STROKE, + }; + + explicit InfoBubbleGtk(GtkThemeProvider* provider, bool match_system_theme); + virtual ~InfoBubbleGtk(); + + // Creates the InfoBubble. + void Init(GtkWidget* anchor_widget, + const gfx::Rect* rect, + GtkWidget* content, + ArrowLocationGtk arrow_location, + bool grab_input); + + // Make the points for our polygon frame, either for fill (the mask), or for + // when we stroke the border. + static std::vector<GdkPoint> MakeFramePolygonPoints( + ArrowLocationGtk arrow_location, + int width, + int height, + FrameType type); + + // Get the location where the arrow should be placed (which is a function of + // the preferred location and of the direction that the bubble should be + // facing to fit onscreen). |arrow_x| is the X component in screen + // coordinates of the point at which the bubble's arrow should be aimed, and + // |width| is the bubble's width. + static ArrowLocationGtk GetArrowLocation( + ArrowLocationGtk preferred_location, int arrow_x, int width); + + // Updates |arrow_location_| based on the toplevel window's current position + // and the bubble's size. If the |force_move_and_reshape| is true or the + // location changes, moves and reshapes the window and returns true. + bool UpdateArrowLocation(bool force_move_and_reshape); + + // Reshapes the window and updates |mask_region_|. + void UpdateWindowShape(); + + // Calculate the current screen position for the bubble's window (per + // |toplevel_window_|'s position as of its most-recent ConfigureNotify event + // and |rect_|) and move it there. + void MoveWindow(); + + // Restack the bubble's window directly above |toplevel_window_|. + void StackWindow(); + + // Sets the delegate. + void set_delegate(InfoBubbleGtkDelegate* delegate) { delegate_ = delegate; } + + // Grab (in the X sense) the pointer and keyboard. This is needed to make + // sure that we have the input focus. + void GrabPointerAndKeyboard(); + + CHROMEG_CALLBACK_3(InfoBubbleGtk, gboolean, OnEscape, GtkAccelGroup*, + GObject*, guint, GdkModifierType); + + CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnExpose, GdkEventExpose*); + CHROMEGTK_CALLBACK_1(InfoBubbleGtk, void, OnSizeAllocate, GtkAllocation*); + CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnButtonPress, GdkEventButton*); + CHROMEGTK_CALLBACK_0(InfoBubbleGtk, gboolean, OnDestroy); + CHROMEGTK_CALLBACK_0(InfoBubbleGtk, void, OnHide); + CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnToplevelConfigure, + GdkEventConfigure*); + CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnToplevelUnmap, GdkEvent*); + CHROMEGTK_CALLBACK_1(InfoBubbleGtk, void, OnAnchorAllocate, GtkAllocation*); + + // The caller supplied delegate, can be NULL. + InfoBubbleGtkDelegate* delegate_; + + // Our GtkWindow popup window, we don't technically "own" the widget, since + // it deletes us when it is destroyed. + GtkWidget* window_; + + // Provides colors and stuff. + GtkThemeProvider* theme_provider_; + + // The accel group attached to |window_|, to handle closing with escape. + GtkAccelGroup* accel_group_; + + // The window for which we're being shown (and to which |rect_| is relative). + // Note that it's possible for |toplevel_window_| to be NULL if the + // window is destroyed before this object is destroyed, so it's important + // to check for that case. + GtkWindow* toplevel_window_; + + // The widget that we use to relatively position the popup window. + GtkWidget* anchor_widget_; + + // Provides an offset from |anchor_widget_|'s origin for MoveWindow() to + // use. + gfx::Rect rect_; + + // The current shape of |window_| (used to test whether clicks fall in it or + // not). + GdkRegion* mask_region_; + + // Where would we prefer for the arrow be drawn relative to the bubble, and + // where is it currently drawn? + ArrowLocationGtk preferred_arrow_location_; + ArrowLocationGtk current_arrow_location_; + + // Whether the background should match the system theme, when the system theme + // is being used. For example, the bookmark bubble does, but extension popups + // do not. + bool match_system_theme_; + + // If true, the popup owns all X input for the duration of its existence. + // This will usually be true, the exception being when inspecting extension + // popups with dev tools. + bool grab_input_; + + bool closed_by_escape_; + + NotificationRegistrar registrar_; + + GtkSignalRegistrar signals_; + + DISALLOW_COPY_AND_ASSIGN(InfoBubbleGtk); +}; + +#endif // CHROME_BROWSER_GTK_INFO_BUBBLE_GTK_H_ |