diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2013-11-12 16:55:23 +0100 |
---|---|---|
committer | Carlos Garnacho <carlosg@gnome.org> | 2014-01-22 17:10:04 +0100 |
commit | caeb89609614f815f61a64657abb6db797f329f9 (patch) | |
tree | c34b898994c4043394113874d896aade37a53d8e /gtk/gtkbubblewindow.c | |
parent | 8a661b878603669fd6fbefd6ecd71d4850b93c74 (diff) | |
download | gtk+-caeb89609614f815f61a64657abb6db797f329f9.tar.gz |
Introduce GtkPopover
Now that the GtkBubbleWindow object has been cleaned up and made
more generic, rename it as GtkPopover and make it public.
Diffstat (limited to 'gtk/gtkbubblewindow.c')
-rw-r--r-- | gtk/gtkbubblewindow.c | 1086 |
1 files changed, 0 insertions, 1086 deletions
diff --git a/gtk/gtkbubblewindow.c b/gtk/gtkbubblewindow.c deleted file mode 100644 index f0b325bdea..0000000000 --- a/gtk/gtkbubblewindow.c +++ /dev/null @@ -1,1086 +0,0 @@ -/* GTK - The GIMP Toolkit - * Copyright © 2013 Carlos Garnacho <carlosg@gnome.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * GtkBubbleWindow is a bubble-like context window, primarily mean for - * context-dependent helpers on touch interfaces. - * - * In order to place a GtkBubbleWindow to point to some other area, - * use gtk_bubble_window_set_relative_to(), gtk_bubble_window_set_pointing_to() - * and gtk_bubble_window_set_position(). Although it is usually more - * convenient to use gtk_bubble_window_popup() which handles all of those - * at once. - * - * By default, no grabs are performed on the GtkBubbleWindow, leaving - * the popup/popdown semantics up to the caller, gtk_bubble_window_grab() - * can be used to grab the window for a device pair, bringing #GtkMenu alike - * popdown behavior by default on keyboard/pointer interaction. Grabs need - * to be undone through gtk_bubble_window_ungrab(). - */ - -#include "config.h" -#include <gdk/gdk.h> -#include <cairo-gobject.h> -#include "gtkbubblewindowprivate.h" -#include "gtktypebuiltins.h" -#include "gtkmain.h" -#include "gtkprivate.h" -#include "gtkintl.h" - -#define TAIL_GAP_WIDTH 24 -#define TAIL_HEIGHT 12 - -#define POS_IS_VERTICAL(p) ((p) == GTK_POS_TOP || (p) == GTK_POS_BOTTOM) - -typedef struct _GtkBubbleWindowPrivate GtkBubbleWindowPrivate; - -enum { - PROP_RELATIVE_TO = 1, - PROP_POINTING_TO, - PROP_POSITION -}; - -struct _GtkBubbleWindowPrivate -{ - GdkDevice *device; - GtkWidget *widget; - GtkWindow *window; - cairo_rectangle_int_t pointing_to; - gint win_x; - gint win_y; - guint has_pointing_to : 1; - guint preferred_position : 2; - guint final_position : 2; -}; - -G_DEFINE_TYPE_WITH_PRIVATE (GtkBubbleWindow, _gtk_bubble_window, GTK_TYPE_BIN) - -static void -_gtk_bubble_window_init (GtkBubbleWindow *window) -{ - GtkWidget *widget; - - widget = GTK_WIDGET (window); - gtk_widget_set_has_window (widget, TRUE); - window->priv = _gtk_bubble_window_get_instance_private (window); - gtk_style_context_add_class (gtk_widget_get_style_context (widget), - GTK_STYLE_CLASS_OSD); -} - -static void -gtk_bubble_window_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (prop_id) - { - case PROP_RELATIVE_TO: - _gtk_bubble_window_set_relative_to (GTK_BUBBLE_WINDOW (object), - g_value_get_object (value)); - break; - case PROP_POINTING_TO: - _gtk_bubble_window_set_pointing_to (GTK_BUBBLE_WINDOW (object), - g_value_get_boxed (value)); - break; - case PROP_POSITION: - _gtk_bubble_window_set_position (GTK_BUBBLE_WINDOW (object), - g_value_get_enum (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gtk_bubble_window_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkBubbleWindowPrivate *priv = GTK_BUBBLE_WINDOW (object)->priv; - - switch (prop_id) - { - case PROP_RELATIVE_TO: - g_value_set_object (value, priv->widget); - break; - case PROP_POINTING_TO: - g_value_set_boxed (value, &priv->pointing_to); - break; - case PROP_POSITION: - g_value_set_enum (value, priv->preferred_position); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gtk_bubble_window_finalize (GObject *object) -{ - GtkBubbleWindow *window = GTK_BUBBLE_WINDOW (object); - - G_OBJECT_CLASS (_gtk_bubble_window_parent_class)->finalize (object); -} - -static void -gtk_bubble_window_dispose (GObject *object) -{ - GtkBubbleWindow *window = GTK_BUBBLE_WINDOW (object); - GtkBubbleWindowPrivate *priv = window->priv; - - if (priv->window) - gtk_window_remove_popover (priv->window, GTK_WIDGET (object)); - - priv->window = NULL; - priv->widget = NULL; - - G_OBJECT_CLASS (_gtk_bubble_window_parent_class)->dispose (object); -} - -static void -gtk_bubble_window_realize (GtkWidget *widget) -{ - GtkAllocation allocation; - GdkWindowAttr attributes; - gint attributes_mask; - GdkWindow *window; - - gtk_widget_get_allocation (widget, &allocation); - - attributes.x = 0; - attributes.y = 0; - attributes.width = allocation.width; - attributes.height = allocation.height; - attributes.window_type = GDK_WINDOW_CHILD; - attributes.visual = gtk_widget_get_visual (widget); - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.event_mask = - gtk_widget_get_events (widget) | - GDK_BUTTON_MOTION_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_EXPOSURE_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK; - - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; - window = gdk_window_new (gtk_widget_get_parent_window (widget), - &attributes, attributes_mask); - gtk_widget_set_window (widget, window); - gtk_widget_register_window (widget, window); - gtk_widget_set_realized (widget, TRUE); -} - -static void -gtk_bubble_window_map (GtkWidget *widget) -{ - gdk_window_show (gtk_widget_get_window (widget)); - GTK_WIDGET_CLASS (_gtk_bubble_window_parent_class)->map (widget); -} - -static void -gtk_bubble_window_unmap (GtkWidget *widget) -{ - gdk_window_hide (gtk_widget_get_window (widget)); - GTK_WIDGET_CLASS (_gtk_bubble_window_parent_class)->unmap (widget); -} - -static void -gtk_bubble_window_get_pointed_to_coords (GtkBubbleWindow *window, - gint *x, - gint *y, - cairo_rectangle_int_t *rect_out) -{ - GtkBubbleWindowPrivate *priv = window->priv; - cairo_rectangle_int_t rect; - GtkAllocation window_alloc; - - _gtk_bubble_window_get_pointing_to (window, &rect); - gtk_widget_get_allocation (GTK_WIDGET (priv->window), &window_alloc); - gtk_widget_translate_coordinates (priv->widget, GTK_WIDGET (priv->window), - rect.x, rect.y, &rect.x, &rect.y); - - if (POS_IS_VERTICAL (priv->final_position)) - { - *x = CLAMP (rect.x + (rect.width / 2), 0, window_alloc.width); - *y = rect.y; - - if (priv->final_position == GTK_POS_BOTTOM) - (*y) += rect.height; - } - else - { - *y = CLAMP (rect.y + (rect.height / 2), 0, window_alloc.height); - *x = rect.x; - - if (priv->final_position == GTK_POS_RIGHT) - (*x) += rect.width; - } - - if (rect_out) - *rect_out = rect; -} - -static void -gtk_bubble_window_get_gap_coords (GtkBubbleWindow *window, - gint *initial_x_out, - gint *initial_y_out, - gint *tip_x_out, - gint *tip_y_out, - gint *final_x_out, - gint *final_y_out, - GtkPositionType *gap_side_out) -{ - GtkBubbleWindowPrivate *priv = window->priv; - gint base, tip, x, y; - gint initial_x, initial_y; - gint tip_x, tip_y; - gint final_x, final_y; - GtkPositionType gap_side; - GtkAllocation allocation; - gint border_radius; - - gtk_bubble_window_get_pointed_to_coords (window, &x, &y, NULL); - gtk_widget_get_allocation (GTK_WIDGET (window), &allocation); - - gtk_style_context_get (gtk_widget_get_style_context (GTK_WIDGET (window)), - gtk_widget_get_state_flags (GTK_WIDGET (window)), - GTK_STYLE_PROPERTY_BORDER_RADIUS, &border_radius, - NULL); - - base = tip = 0; - gap_side = GTK_POS_LEFT; - - if (priv->final_position == GTK_POS_BOTTOM || - priv->final_position == GTK_POS_RIGHT) - { - base = TAIL_HEIGHT; - tip = 0; - - gap_side = (priv->final_position == GTK_POS_BOTTOM) ? GTK_POS_TOP : GTK_POS_LEFT; - } - else if (priv->final_position == GTK_POS_TOP) - { - base = allocation.height - TAIL_HEIGHT; - tip = allocation.height; - gap_side = GTK_POS_BOTTOM; - } - else if (priv->final_position == GTK_POS_LEFT) - { - base = allocation.width - TAIL_HEIGHT; - tip = allocation.width; - gap_side = GTK_POS_RIGHT; - } - - if (POS_IS_VERTICAL (priv->final_position)) - { - initial_x = CLAMP (x - priv->win_x - TAIL_GAP_WIDTH / 2, - border_radius, allocation.width - TAIL_GAP_WIDTH); - initial_y = base; - - tip_x = CLAMP (x - priv->win_x, 0, allocation.width); - tip_y = tip; - - final_x = CLAMP (x - priv->win_x + TAIL_GAP_WIDTH / 2, - TAIL_GAP_WIDTH, allocation.width - border_radius); - final_y = base; - } - else - { - initial_x = base; - initial_y = CLAMP (y - priv->win_y - TAIL_GAP_WIDTH / 2, - border_radius, allocation.height - TAIL_GAP_WIDTH); - - tip_x = tip; - tip_y = CLAMP (y - priv->win_y, 0, allocation.height); - - final_x = base; - final_y = CLAMP (y - priv->win_y + TAIL_GAP_WIDTH / 2, - TAIL_GAP_WIDTH, allocation.height - border_radius); - } - - if (initial_x_out) - *initial_x_out = initial_x; - if (initial_y_out) - *initial_y_out = initial_y; - - if (tip_x_out) - *tip_x_out = tip_x; - if (tip_y_out) - *tip_y_out = tip_y; - - if (final_x_out) - *final_x_out = final_x; - if (final_y_out) - *final_y_out = final_y; - - if (gap_side_out) - *gap_side_out = gap_side; -} - -static void -gtk_bubble_window_get_rect_coords (GtkBubbleWindow *window, - gint *x1_out, - gint *y1_out, - gint *x2_out, - gint *y2_out) -{ - GtkBubbleWindowPrivate *priv = window->priv; - gint x1, x2, y1, y2; - GtkAllocation allocation; - - x1 = y1 = x2 = y2 = 0; - gtk_widget_get_allocation (GTK_WIDGET (window), &allocation); - - if (priv->final_position == GTK_POS_TOP) - { - x1 = 0; - y1 = 0; - x2 = allocation.width; - y2 = allocation.height - TAIL_HEIGHT; - } - else if (priv->final_position == GTK_POS_BOTTOM) - { - x1 = 0; - y1 = TAIL_HEIGHT; - x2 = allocation.width; - y2 = allocation.height; - } - else if (priv->final_position == GTK_POS_LEFT) - { - x1 = 0; - y1 = 0; - x2 = allocation.width - TAIL_HEIGHT; - y2 = allocation.height; - } - else if (priv->final_position == GTK_POS_RIGHT) - { - x1 = TAIL_HEIGHT; - y1 = 0; - x2 = allocation.width; - y2 = allocation.height; - } - - if (x1_out) - *x1_out = x1; - if (y1_out) - *y1_out = y1; - if (x2_out) - *x2_out = x2; - if (y2_out) - *y2_out = y2; -} - -static void -gtk_bubble_window_apply_tail_path (GtkBubbleWindow *window, - cairo_t *cr) -{ - gint initial_x, initial_y; - gint tip_x, tip_y; - gint final_x, final_y; - - gtk_bubble_window_get_gap_coords (window, - &initial_x, &initial_y, - &tip_x, &tip_y, - &final_x, &final_y, - NULL); - - cairo_move_to (cr, initial_x, initial_y); - cairo_line_to (cr, tip_x, tip_y); - cairo_line_to (cr, final_x, final_y); -} - -static void -gtk_bubble_window_apply_border_path (GtkBubbleWindow *window, - cairo_t *cr) -{ - GtkBubbleWindowPrivate *priv; - GtkAllocation allocation; - gint x1, y1, x2, y2; - - priv = window->priv; - gtk_widget_get_allocation (GTK_WIDGET (window), &allocation); - - gtk_bubble_window_apply_tail_path (window, cr); - gtk_bubble_window_get_rect_coords (window, &x1, &y1, &x2, &y2); - - if (priv->final_position == GTK_POS_TOP) - { - cairo_line_to (cr, x2, y2); - cairo_line_to (cr, x2, y1); - cairo_line_to (cr, x1, y1); - cairo_line_to (cr, x1, y2); - } - else if (priv->final_position == GTK_POS_BOTTOM) - { - cairo_line_to (cr, x2, y1); - cairo_line_to (cr, x2, y2); - cairo_line_to (cr, x1, y2); - cairo_line_to (cr, x1, y1); - } - else if (priv->final_position == GTK_POS_LEFT) - { - cairo_line_to (cr, x2, y2); - cairo_line_to (cr, x1, y2); - cairo_line_to (cr, x1, y1); - cairo_line_to (cr, x2, y1); - } - else if (priv->final_position == GTK_POS_RIGHT) - { - cairo_line_to (cr, x1, y1); - cairo_line_to (cr, x2, y1); - cairo_line_to (cr, x2, y2); - cairo_line_to (cr, x1, y2); - } - - cairo_close_path (cr); -} - -static void -gtk_bubble_window_update_shape (GtkBubbleWindow *window) -{ - cairo_surface_t *surface; - cairo_region_t *region; - GdkWindow *win; - cairo_t *cr; - - win = gtk_widget_get_window (GTK_WIDGET (window)); - surface = - gdk_window_create_similar_surface (win, - CAIRO_CONTENT_COLOR_ALPHA, - gdk_window_get_width (win), - gdk_window_get_height (win)); - - cr = cairo_create (surface); - gtk_bubble_window_apply_border_path (window, cr); - cairo_fill (cr); - cairo_destroy (cr); - - region = gdk_cairo_region_create_from_surface (surface); - cairo_surface_destroy (surface); - - gtk_widget_shape_combine_region (GTK_WIDGET (window), region); - cairo_region_destroy (region); -} - -static void -gtk_bubble_window_update_position (GtkBubbleWindow *window) -{ - GtkAllocation allocation, window_alloc; - GtkBubbleWindowPrivate *priv; - cairo_rectangle_int_t rect; - gint win_x, win_y, x, y; - - priv = window->priv; - gtk_widget_get_allocation (GTK_WIDGET (window), &allocation); - gtk_widget_get_allocation (GTK_WIDGET (priv->window), &window_alloc); - priv->final_position = priv->preferred_position; - _gtk_bubble_window_get_pointing_to (window, &rect); - - gtk_bubble_window_get_pointed_to_coords (window, &x, &y, &rect); - - /* Check whether there's enough room on the - * preferred side, move to the opposite one if not. - */ - if (priv->preferred_position == GTK_POS_TOP && rect.y < allocation.height) - priv->final_position = GTK_POS_BOTTOM; - else if (priv->preferred_position == GTK_POS_BOTTOM && - rect.y > window_alloc.height - allocation.height) - priv->final_position = GTK_POS_TOP; - else if (priv->preferred_position == GTK_POS_LEFT && rect.x < allocation.width) - priv->final_position = GTK_POS_RIGHT; - else if (priv->preferred_position == GTK_POS_RIGHT && - rect.x > window_alloc.width - allocation.width) - priv->final_position = GTK_POS_LEFT; - - if (POS_IS_VERTICAL (priv->final_position)) - { - win_x = CLAMP (x - allocation.width / 2, - 0, window_alloc.width - allocation.width); - win_y = y; - - if (priv->final_position == GTK_POS_TOP) - win_y -= allocation.height; - else if (priv->final_position == GTK_POS_BOTTOM) - win_y += rect.height; - } - else - { - win_y = CLAMP (y - allocation.height / 2, - 0, window_alloc.height - allocation.height); - win_x = x; - - if (priv->final_position == GTK_POS_LEFT) - win_x -= allocation.width; - else if (priv->final_position == GTK_POS_RIGHT) - win_x += rect.width; - } - - priv->win_x = win_x; - priv->win_y = win_y; - - gtk_window_set_popover_position (priv->window, - GTK_WIDGET (window), - win_x, win_y); - - gtk_bubble_window_update_shape (window); -} - -static gboolean -gtk_bubble_window_draw (GtkWidget *widget, - cairo_t *cr) -{ - GtkStyleContext *context; - GtkAllocation allocation; - GtkWidget *child; - GtkBorder border; - GdkRGBA border_color; - gint rect_x1, rect_x2, rect_y1, rect_y2; - gint initial_x, initial_y, final_x, final_y; - gint gap_start, gap_end; - GtkPositionType gap_side; - GtkStateFlags state; - - context = gtk_widget_get_style_context (widget); - state = gtk_widget_get_state_flags (widget); - gtk_widget_get_allocation (widget, &allocation); - - gtk_bubble_window_get_rect_coords (GTK_BUBBLE_WINDOW (widget), - &rect_x1, &rect_y1, - &rect_x2, &rect_y2); - - /* Render the rect background */ - gtk_render_background (context, cr, - rect_x1, rect_y1, - rect_x2 - rect_x1, rect_y2 - rect_y1); - - gtk_bubble_window_get_gap_coords (GTK_BUBBLE_WINDOW (widget), - &initial_x, &initial_y, - NULL, NULL, - &final_x, &final_y, - &gap_side); - - if (POS_IS_VERTICAL (gap_side)) - { - gap_start = initial_x; - gap_end = final_x; - } - else - { - gap_start = initial_y; - gap_end = final_y; - } - - /* Now render the frame, without the gap for the arrow tip */ - gtk_render_frame_gap (context, cr, - rect_x1, rect_y1, - rect_x2 - rect_x1, rect_y2 - rect_y1, - gap_side, - gap_start, gap_end); - - /* Clip to the arrow shape */ - cairo_save (cr); - - gtk_bubble_window_apply_tail_path (GTK_BUBBLE_WINDOW (widget), cr); - cairo_clip (cr); - - /* Render the arrow background */ - gtk_render_background (context, cr, - 0, 0, - allocation.width, allocation.height); - - /* Render the border of the arrow tip */ - gtk_style_context_get_border (context, state, &border); - - if (border.bottom > 0) - { - gtk_style_context_get_border_color (context, state, &border_color); - gtk_bubble_window_apply_tail_path (GTK_BUBBLE_WINDOW (widget), cr); - gdk_cairo_set_source_rgba (cr, &border_color); - - cairo_set_line_width (cr, border.bottom); - cairo_stroke (cr); - } - - /* We're done */ - cairo_restore (cr); - - child = gtk_bin_get_child (GTK_BIN (widget)); - - if (child) - gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr); - - return TRUE; -} - -static void -get_padding_and_border (GtkWidget *widget, - GtkBorder *border) -{ - GtkStyleContext *context; - GtkStateFlags state; - GtkBorder tmp; - - context = gtk_widget_get_style_context (widget); - state = gtk_widget_get_state_flags (widget); - - gtk_style_context_get_padding (context, state, border); - gtk_style_context_get_border (context, state, &tmp); - border->top += tmp.top; - border->right += tmp.right; - border->bottom += tmp.bottom; - border->left += tmp.left; -} - -static void -gtk_bubble_window_get_preferred_width (GtkWidget *widget, - gint *minimum_width, - gint *natural_width) -{ - GtkBubbleWindowPrivate *priv; - GtkWidget *child; - gint min, nat; - GtkBorder border; - - priv = GTK_BUBBLE_WINDOW (widget)->priv; - child = gtk_bin_get_child (GTK_BIN (widget)); - min = nat = 0; - - if (child) - gtk_widget_get_preferred_width (child, &min, &nat); - - get_padding_and_border (widget, &border); - min += border.left + border.right; - nat += border.left + border.right; - - if (!POS_IS_VERTICAL (priv->final_position)) - { - min += TAIL_HEIGHT; - nat += TAIL_HEIGHT; - } - - if (minimum_width) - *minimum_width = MAX (min, TAIL_GAP_WIDTH); - - if (natural_width) - *natural_width = MAX (nat, TAIL_GAP_WIDTH); -} - -static void -gtk_bubble_window_get_preferred_height (GtkWidget *widget, - gint *minimum_height, - gint *natural_height) -{ - GtkBubbleWindowPrivate *priv; - GtkWidget *child; - gint min, nat; - GtkBorder border; - - priv = GTK_BUBBLE_WINDOW (widget)->priv; - child = gtk_bin_get_child (GTK_BIN (widget)); - min = nat = 0; - - if (child) - gtk_widget_get_preferred_height (child, &min, &nat); - - get_padding_and_border (widget, &border); - min += border.top + border.bottom; - nat += border.top + border.bottom; - - if (POS_IS_VERTICAL (priv->final_position)) - { - min += TAIL_HEIGHT; - nat += TAIL_HEIGHT; - } - - if (minimum_height) - *minimum_height = MAX (min, TAIL_GAP_WIDTH); - - if (natural_height) - *natural_height = MAX (nat, TAIL_GAP_WIDTH); -} - -static void -gtk_bubble_window_size_allocate (GtkWidget *widget, - GtkAllocation *allocation) -{ - GtkBubbleWindowPrivate *priv; - GtkWidget *child; - - priv = GTK_BUBBLE_WINDOW (widget)->priv; - gtk_widget_set_allocation (widget, allocation); - child = gtk_bin_get_child (GTK_BIN (widget)); - - if (gtk_widget_get_visible (widget)) - gtk_bubble_window_update_position (GTK_BUBBLE_WINDOW (widget)); - - if (child) - { - GtkAllocation child_alloc; - GtkBorder border; - - get_padding_and_border (widget, &border); - - child_alloc.x = allocation->x + border.left; - child_alloc.y = allocation->y + border.top; - child_alloc.width = allocation->width - border.left - border.right; - child_alloc.height = allocation->height - border.top - border.bottom; - - if (POS_IS_VERTICAL (priv->final_position)) - child_alloc.height -= TAIL_HEIGHT; - else - child_alloc.width -= TAIL_HEIGHT; - - if (priv->final_position == GTK_POS_BOTTOM) - child_alloc.y += TAIL_HEIGHT; - else if (priv->final_position == GTK_POS_RIGHT) - child_alloc.x += TAIL_HEIGHT; - - gtk_widget_size_allocate (child, &child_alloc); - } - - if (gtk_widget_get_realized (widget)) - { - gdk_window_move_resize (gtk_widget_get_window (widget), - 0, 0, allocation->width, allocation->height); - gtk_bubble_window_update_shape (GTK_BUBBLE_WINDOW (widget)); - } -} - -static gboolean -gtk_bubble_window_button_press (GtkWidget *widget, - GdkEventButton *event) -{ - GtkWidget *child; - - child = gtk_bin_get_child (GTK_BIN (widget)); - - if (child && event->window == gtk_widget_get_window (widget)) - { - GtkAllocation child_alloc; - - gtk_widget_get_allocation (child, &child_alloc); - - if (event->x < child_alloc.x || - event->x > child_alloc.x + child_alloc.width || - event->y < child_alloc.y || - event->y > child_alloc.y + child_alloc.height) - gtk_widget_hide (widget); - } - else - gtk_widget_hide (widget); - - return GDK_EVENT_PROPAGATE; -} - -static gboolean -gtk_bubble_window_key_press (GtkWidget *widget, - GdkEventKey *event) -{ - if (event->keyval == GDK_KEY_Escape) - { - gtk_widget_hide (widget); - return GDK_EVENT_STOP; - } - - return GDK_EVENT_PROPAGATE; -} - -static void -_gtk_bubble_window_class_init (GtkBubbleWindowClass *klass) -{ - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->set_property = gtk_bubble_window_set_property; - object_class->get_property = gtk_bubble_window_get_property; - object_class->finalize = gtk_bubble_window_finalize; - object_class->dispose = gtk_bubble_window_dispose; - - widget_class->realize = gtk_bubble_window_realize; - widget_class->map = gtk_bubble_window_map; - widget_class->unmap = gtk_bubble_window_unmap; - widget_class->get_preferred_width = gtk_bubble_window_get_preferred_width; - widget_class->get_preferred_height = gtk_bubble_window_get_preferred_height; - widget_class->size_allocate = gtk_bubble_window_size_allocate; - widget_class->draw = gtk_bubble_window_draw; - widget_class->button_press_event = gtk_bubble_window_button_press; - widget_class->key_press_event = gtk_bubble_window_key_press; - - g_object_class_install_property (object_class, - PROP_RELATIVE_TO, - g_param_spec_object ("relative-to", - P_("Relative to"), - P_("Window the bubble window points to"), - GTK_TYPE_WIDGET, - GTK_PARAM_READWRITE)); - g_object_class_install_property (object_class, - PROP_POINTING_TO, - g_param_spec_boxed ("pointing-to", - P_("Pointing to"), - P_("Rectangle the bubble window points to"), - CAIRO_GOBJECT_TYPE_RECTANGLE_INT, - GTK_PARAM_READWRITE)); - g_object_class_install_property (object_class, - PROP_POSITION, - g_param_spec_enum ("position", - P_("Position"), - P_("Position to place the bubble window"), - GTK_TYPE_POSITION_TYPE, GTK_POS_TOP, - GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -} - -static void -gtk_bubble_window_update_relative_to (GtkBubbleWindow *window, - GtkWidget *relative_to) -{ - GtkBubbleWindowPrivate *priv; - - priv = window->priv; - - if (priv->widget == relative_to) - return; - - if (priv->window) - { - gtk_window_remove_popover (priv->window, GTK_WIDGET (window)); - priv->window = NULL; - } - - priv->widget = relative_to; - g_object_notify (G_OBJECT (window), "relative-to"); - - if (priv->widget) - priv->window = - GTK_WINDOW (gtk_widget_get_ancestor (priv->widget, GTK_TYPE_WINDOW)); - - if (priv->window) - gtk_window_add_popover (priv->window, GTK_WIDGET (window)); -} - -static void -gtk_bubble_window_update_pointing_to (GtkBubbleWindow *window, - cairo_rectangle_int_t *pointing_to) -{ - GtkBubbleWindowPrivate *priv; - - priv = window->priv; - - if (pointing_to) - { - priv->pointing_to = *pointing_to; - priv->has_pointing_to = TRUE; - } - else - priv->has_pointing_to = FALSE; - - g_object_notify (G_OBJECT (window), "pointing-to"); -} - -static void -gtk_bubble_window_update_preferred_position (GtkBubbleWindow *window, - GtkPositionType position) -{ - GtkBubbleWindowPrivate *priv; - - priv = window->priv; - priv->preferred_position = position; - g_object_notify (G_OBJECT (window), "position"); -} - -/* - * gtk_bubble_window_new: - * - * Returns a newly created #GtkBubblewindow - * - * Returns: a new #GtkBubbleWindow - * - * Since: 3.8 - */ -GtkWidget * -_gtk_bubble_window_new (GtkWidget *relative_to) -{ - return g_object_new (GTK_TYPE_BUBBLE_WINDOW, - "relative-to", relative_to, - NULL); -} - -/* - * gtk_bubble_window_set_relative_to: - * @window: a #GtkBubbleWindow - * @relative_to: a #GdkWindow - * - * Sets the #GdkWindow to act as the origin of coordinates of - * @window, or %NULL to use the root window. See - * gtk_bubble_window_set_pointing_to() - * - * If @window is currently visible, it will be moved to reflect - * this change. - * - * Since: 3.8 - */ -void -_gtk_bubble_window_set_relative_to (GtkBubbleWindow *window, - GtkWidget *relative_to) -{ - g_return_if_fail (GTK_IS_BUBBLE_WINDOW (window)); - g_return_if_fail (GTK_IS_WIDGET (relative_to)); - - gtk_bubble_window_update_relative_to (window, relative_to); - - if (gtk_widget_get_visible (GTK_WIDGET (window))) - gtk_bubble_window_update_position (window); -} - -/* - * gtk_bubble_window_get_relative_to: - * @window: a #GtkBubbleWindow - * - * Returns the #GdkWindow used as the origin of coordinates. - * If @window is currently visible, it will be moved to reflect - * this change. - * - * Returns: the #GdkWindow @window is placed upon - * - * Since: 3.8 - */ -GtkWidget * -_gtk_bubble_window_get_relative_to (GtkBubbleWindow *window) -{ - GtkBubbleWindowPrivate *priv; - - g_return_val_if_fail (GTK_IS_BUBBLE_WINDOW (window), NULL); - - priv = window->priv; - - return priv->widget; -} - -/* - * gtk_bubble_window_set_pointing_to: - * @window: a #GtkBubbleWindow - * @rect: rectangle to point to - * - * Sets the rectangle that @window will point to, the coordinates - * of this rectangle are relative to the #GdkWindow set through - * gtk_bubble_window_set_relative_to(). - * - * Since: 3.8 - */ -void -_gtk_bubble_window_set_pointing_to (GtkBubbleWindow *window, - cairo_rectangle_int_t *rect) -{ - g_return_if_fail (GTK_IS_BUBBLE_WINDOW (window)); - g_return_if_fail (rect != NULL); - - gtk_bubble_window_update_pointing_to (window, rect); - - if (gtk_widget_get_visible (GTK_WIDGET (window))) - gtk_bubble_window_update_position (window); -} - -/* - * gtk_bubble_window_get_pointing_to: - * @window: a #GtkBubbleWindow - * @rect: (out): location to store the rectangle - * - * If a rectangle to point to is set, this function will return - * %TRUE and fill in @rect with the rectangle @window is currently - * pointing to. - * - * Returns: %TRUE if a rectangle is set - * - * Since: 3.8 - */ -gboolean -_gtk_bubble_window_get_pointing_to (GtkBubbleWindow *window, - cairo_rectangle_int_t *rect) -{ - GtkBubbleWindowPrivate *priv; - - g_return_val_if_fail (GTK_IS_BUBBLE_WINDOW (window), FALSE); - - priv = window->priv; - - if (rect) - { - if (priv->has_pointing_to) - *rect = priv->pointing_to; - else if (priv->widget) - { - gtk_widget_get_allocation (priv->widget, rect); - rect->x = rect->y = 0; - } - } - - return priv->has_pointing_to; -} - -/* - * gtk_bubble_window_set_position: - * @window: a #GtkBubbleWindow - * @position: preferred bubble position - * - * Sets the preferred position for @window to appear. - * If @window is currently visible, it will be moved to reflect - * this change. - * - * <note> - * This preference will be respected where possible, although - * on lack of space (eg. if close to the screen edges), the - * #GtkBubbleWindow may choose to appear on the opposite side - * </note> - * - * Since: 3.8 - */ -void -_gtk_bubble_window_set_position (GtkBubbleWindow *window, - GtkPositionType position) -{ - g_return_if_fail (GTK_IS_BUBBLE_WINDOW (window)); - g_return_if_fail (position >= GTK_POS_LEFT && position <= GTK_POS_BOTTOM); - - gtk_bubble_window_update_preferred_position (window, position); - - if (gtk_widget_get_visible (GTK_WIDGET (window))) - gtk_bubble_window_update_position (window); -} - -/* - * gtk_bubble_window_get_position: - * @window: a #GtkBubbleWindow - * - * Returns the preferred position to place @window - * - * Returns: The preferred position - * - * Since: 3.8 - */ -GtkPositionType -_gtk_bubble_window_get_position (GtkBubbleWindow *window) -{ - GtkBubbleWindowPrivate *priv; - - g_return_val_if_fail (GTK_IS_BUBBLE_WINDOW (window), GTK_POS_TOP); - - priv = window->priv; - - return priv->preferred_position; -} |