diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index de3a84ecf6..d77b04a3a8 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1332,6 +1332,27 @@ rewrite_event_for_grabs (GdkEvent *event) event_widget = gtk_get_event_widget (event); grab_widget = gtk_native_get_for_surface (grab_surface); + switch ((guint) event->any.type) + { + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + case GDK_TOUCH_BEGIN: + case GDK_TOUCH_END: + case GDK_TOUCH_CANCEL: + case GDK_TOUCHPAD_SWIPE: + case GDK_TOUCHPAD_PINCH: + if (grab_surface != event->any.surface && + GTK_IS_POPOVER (grab_widget)) + { + gtk_widget_hide (grab_widget); + return NULL; + } + break; + default:; + } + if (grab_widget && gtk_main_get_window_group (grab_widget) != gtk_main_get_window_group (event_widget)) return rewrite_event_for_surface (event, grab_surface); diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c index 70632e8eac..f5bff1ca6d 100644 --- a/gtk/gtkpopover.c +++ b/gtk/gtkpopover.c @@ -1,5 +1,8 @@ /* GTK - The GIMP Toolkit - * Copyright © 2013 Carlos Garnacho + * Copyright (C) 2019 Red Hat, Inc. + * + * Authors: + * - Matthias Clasen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -104,129 +107,245 @@ #include "config.h" #include "gtkpopoverprivate.h" - -#include "gtkadjustment.h" -#include "gtkbox.h" -#include "gtkbutton.h" -#include "gtkcontainerprivate.h" +#include "gtknative.h" +#include "gtkwidgetprivate.h" +#include "gtkeventcontrollerkey.h" #include "gtkcssnodeprivate.h" -#include "gtkentry.h" +#include "gtkbindings.h" +#include "gtkenums.h" +#include "gtktypebuiltins.h" +#include "gtkmnemonichash.h" #include "gtkgizmoprivate.h" #include "gtkintl.h" -#include "gtklabel.h" -#include "gtkmain.h" -#include "gtkmenusectionbox.h" -#include "gtkmenutracker.h" -#include "gtkmodelbutton.h" #include "gtkprivate.h" -#include "gtkprogresstrackerprivate.h" -#include "gtkrender.h" -#include "gtkroundedboxprivate.h" -#include "gtkscrollable.h" -#include "gtksettingsprivate.h" -#include "gtksizegroup.h" -#include "gtksnapshot.h" +#include "gtkmain.h" #include "gtkstack.h" -#include "gtkstylecontextprivate.h" -#include "gtktypebuiltins.h" -#include "gtkwidgetprivate.h" -#include "gtkwindowprivate.h" -#include "gtkgesturemultipress.h" -#include "gtkeventcontrollerkey.h" +#include "gtkmenusectionbox.h" +#include "gdk/gdkeventsprivate.h" +#include "gtkpointerfocusprivate.h" +#include "gtkcssnodeprivate.h" -#include "a11y/gtkpopoveraccessible.h" +static GListStore *popover_list = NULL; -#include "gsk/gskroundedrectprivate.h" +typedef struct { + GdkSurface *surface; + GskRenderer *renderer; + GtkWidget *default_widget; -#ifdef GDK_WINDOWING_WAYLAND -#include "wayland/gdkwayland.h" -#endif + GdkSurfaceState state; + GtkWidget *relative_to; + GdkRectangle pointing_to; + gboolean has_pointing_to; + guint surface_transform_changed_cb; + GtkPositionType position; + gboolean modal; + gboolean has_grab; -#define TAIL_GAP_WIDTH 24 -#define TAIL_HEIGHT 12 -#define TRANSITION_DIFF 20 -#define TRANSITION_DURATION 150 * 1000 + GtkWidget *contents_widget; +} GtkPopoverPrivate; -#define POS_IS_VERTICAL(p) ((p) == GTK_POS_TOP || (p) == GTK_POS_BOTTOM) +enum { + CLOSED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; enum { PROP_RELATIVE_TO = 1, PROP_POINTING_TO, PROP_POSITION, PROP_MODAL, - PROP_CONSTRAIN_TO, PROP_DEFAULT_WIDGET, NUM_PROPERTIES }; -enum { - CLOSED, - N_SIGNALS -}; +static GParamSpec *properties[NUM_PROPERTIES] = { NULL }; -enum { - STATE_SHOWING, - STATE_SHOWN, - STATE_HIDING, - STATE_HIDDEN -}; +static void gtk_popover_native_interface_init (GtkNativeInterface *iface); -typedef struct _GtkPopoverPrivate GtkPopoverPrivate; -struct _GtkPopoverPrivate +G_DEFINE_TYPE_WITH_CODE (GtkPopover, gtk_popover, GTK_TYPE_BIN, + G_ADD_PRIVATE (GtkPopover) + G_IMPLEMENT_INTERFACE (GTK_TYPE_NATIVE, + gtk_popover_native_interface_init)) + + +static GdkSurface * +gtk_popover_native_get_surface (GtkNative *native) { - GtkWidget *widget; - GtkWidget *contents_widget; - GtkCssNode *arrow_node; - GtkWindow *window; - GtkWidget *prev_focus_widget; - GtkWidget *default_widget; - GtkWidget *prev_default; - GtkScrollable *parent_scrollable; - GtkAdjustment *vadj; - GtkAdjustment *hadj; - GdkRectangle pointing_to; - GtkPopoverConstraint constraint; - GtkProgressTracker tracker; - guint prev_focus_unmap_id; - guint hierarchy_changed_id; - guint size_allocate_id; - guint unmap_id; - guint scrollable_notify_id; - guint grab_notify_id; - guint state_changed_id; - guint has_pointing_to : 1; - guint preferred_position : 2; - guint final_position : 2; - guint current_position : 2; - guint modal : 1; - guint button_pressed : 1; - guint grab_notify_blocked : 1; - guint state : 2; - guint visible : 1; - guint first_frame_skipped : 1; - gint transition_diff; - guint tick_id; + GtkPopover *popover = GTK_POPOVER (native); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - gint tip_x; - gint tip_y; -}; + return priv->surface; +} -static GParamSpec *properties[NUM_PROPERTIES]; -static GQuark quark_widget_popovers = 0; -static guint signals[N_SIGNALS] = { 0 }; +static GskRenderer * +gtk_popover_native_get_renderer (GtkNative *native) +{ + GtkPopover *popover = GTK_POPOVER (native); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); -static void gtk_popover_update_relative_to (GtkPopover *popover, - GtkWidget *relative_to); -static void gtk_popover_set_state (GtkPopover *popover, - guint state); -static void gtk_popover_apply_modality (GtkPopover *popover, - gboolean modal); + return priv->renderer; +} -static void gtk_popover_set_scrollable_full (GtkPopover *popover, - GtkScrollable *scrollable); +static void +gtk_popover_native_get_surface_transform (GtkNative *native, + int *x, + int *y) +{ + GtkStyleContext *context; + GtkBorder margin, border, padding; -G_DEFINE_TYPE_WITH_PRIVATE (GtkPopover, gtk_popover, GTK_TYPE_BIN) + context = gtk_widget_get_style_context (GTK_WIDGET (native)); + gtk_style_context_get_margin (context, &margin); + gtk_style_context_get_border (context, &border); + gtk_style_context_get_padding (context, &padding); + *x = margin.left + border.left + padding.left; + *y = margin.top + border.top + padding.top; +} + +static void +move_to_rect (GtkPopover *popover) +{ + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + GdkRectangle rect; + GdkGravity parent_anchor; + GdkGravity surface_anchor; + GdkAnchorHints anchor_hints; + + gtk_widget_get_surface_allocation (priv->relative_to, &rect); + if (priv->has_pointing_to) + { + rect.x += priv->pointing_to.x; + rect.y += priv->pointing_to.y; + rect.width = priv->pointing_to.width; + rect.height = priv->pointing_to.height; + } + + switch (priv->position) + { + case GTK_POS_LEFT: + parent_anchor = GDK_GRAVITY_WEST; + surface_anchor = GDK_GRAVITY_EAST; + anchor_hints = GDK_ANCHOR_FLIP_X | GDK_ANCHOR_SLIDE_Y; + break; + + case GTK_POS_RIGHT: + parent_anchor = GDK_GRAVITY_EAST; + surface_anchor = GDK_GRAVITY_WEST; + anchor_hints = GDK_ANCHOR_FLIP_X | GDK_ANCHOR_SLIDE_Y; + break; + + case GTK_POS_TOP: + parent_anchor = GDK_GRAVITY_NORTH; + surface_anchor = GDK_GRAVITY_SOUTH; + anchor_hints = GDK_ANCHOR_FLIP_Y | GDK_ANCHOR_SLIDE_X; + break; + + case GTK_POS_BOTTOM: + parent_anchor = GDK_GRAVITY_SOUTH; + surface_anchor = GDK_GRAVITY_NORTH; + anchor_hints = GDK_ANCHOR_FLIP_Y | GDK_ANCHOR_SLIDE_X; + break; + + default: + g_assert_not_reached (); + } + + gdk_surface_move_to_rect (priv->surface, + &rect, + parent_anchor, + surface_anchor, + anchor_hints, + 0, 0); +} + +static void +gtk_popover_move_resize (GtkPopover *popover) +{ + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + GtkRequisition req; + + if (priv->surface) + { + gtk_widget_get_preferred_size (GTK_WIDGET (popover), NULL, &req); + gdk_surface_resize (priv->surface, req.width, req.height); + move_to_rect (popover); + } +} + +static void +gtk_popover_native_check_resize (GtkNative *native) +{ + GtkPopover *popover = GTK_POPOVER (native); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + GtkWidget *widget = GTK_WIDGET (popover); + + if (!_gtk_widget_get_alloc_needed (widget)) + gtk_widget_ensure_allocate (widget); + else if (gtk_widget_get_visible (widget)) + { + gtk_popover_move_resize (popover); + if (priv->surface) + gtk_widget_allocate (GTK_WIDGET (popover), + gdk_surface_get_width (priv->surface), + gdk_surface_get_height (priv->surface), + -1, NULL); + } +} + + +static void +gtk_popover_focus_in (GtkWidget *widget) +{ +} + +static void +gtk_popover_focus_out (GtkWidget *widget) +{ +} + +static gboolean +gtk_popover_key_pressed (GtkWidget *widget, + guint keyval, + guint keycode, + GdkModifierType state) +{ + if (keyval == GDK_KEY_Escape) + { + gtk_popover_popdown (GTK_POPOVER (widget)); + return TRUE; + } + + return FALSE; +} + +static void +surface_state_changed (GtkWidget *widget) +{ + GtkPopover *popover = GTK_POPOVER (widget); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + GdkSurfaceState new_surface_state; + GdkSurfaceState changed_mask; + + new_surface_state = gdk_surface_get_state (_gtk_widget_get_surface (widget)); + changed_mask = new_surface_state ^ priv->state; + priv->state = new_surface_state; + + if (changed_mask & GDK_SURFACE_STATE_WITHDRAWN) + { + if (priv->state & GDK_SURFACE_STATE_WITHDRAWN) + gtk_widget_hide (widget); + } +} + +static void +surface_size_changed (GtkWindow *window, + guint width, + guint height) +{ +} static void measure_contents (GtkGizmo *gizmo, @@ -257,95 +376,10 @@ allocate_contents (GtkGizmo *gizmo, if (child) gtk_widget_size_allocate (child, - &(GtkAllocation) { - 0, 0, - width, height + &(GtkAllocation) { 0, 0, width, height }, -1); } -static void -node_style_changed_cb (GtkCssNode *node, - GtkCssStyleChange *change, - GtkWidget *widget) -{ - if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_SIZE)) - gtk_widget_queue_resize (widget); - else - gtk_widget_queue_draw (widget); -} - -static gboolean -key_controller_key_pressed (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType modifiers, - GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkWidget *toplevel, *focus; - - if (keyval == GDK_KEY_Escape) - { - gtk_popover_popdown (popover); - return TRUE; - } - - if (priv->modal) - { - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (popover)); - - if (GTK_IS_WINDOW (toplevel)) - { - focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); - - if (focus && gtk_widget_is_ancestor (focus, GTK_WIDGET (popover))) - { - if (gtk_event_controller_key_forward (key, focus)) - return TRUE; - } - - /* Piggyback on the toplevel to have it handle key navigation */ - return gtk_event_controller_key_forward (key, toplevel); - } - } - - return FALSE; -} - -static void -gesture_pressed (GtkGestureMultiPress *gesture, - gint n_press, - gdouble x, - gdouble y, - GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - priv->button_pressed = TRUE; -} - -static void -gesture_released (GtkGestureMultiPress *gesture, - gint n_press, - gdouble x, - gdouble y, - GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - graphene_rect_t child_bounds; - GtkWidget *child; - - if (!priv->button_pressed) - return; - - child = gtk_bin_get_child (GTK_BIN (popover)); - if (!child || - !gtk_widget_compute_bounds (child, GTK_WIDGET (popover), &child_bounds) || - !graphene_rect_contains_point (&child_bounds, - &(graphene_point_t){x, y})) - gtk_popover_popdown (popover); -} - static void activate_default_cb (GSimpleAction *action, GVariant *parameter, @@ -355,9 +389,13 @@ activate_default_cb (GSimpleAction *action, GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); GtkWidget *focus_widget; - focus_widget = gtk_window_get_focus (GTK_WINDOW (gtk_widget_get_root (priv->widget))); + focus_widget = gtk_window_get_focus (GTK_WINDOW (gtk_widget_get_root (priv->relative_to))); + if (!gtk_widget_is_ancestor (focus_widget, GTK_WIDGET (popover))) + focus_widget = NULL; + if (priv->default_widget && gtk_widget_is_sensitive (priv->default_widget) && - (!focus_widget || !gtk_widget_get_receives_default (focus_widget))) + (!focus_widget || !gtk_widget_get_receives_default (focus_widget) +)) gtk_widget_activate (priv->default_widget); else if (focus_widget && gtk_widget_is_sensitive (focus_widget)) gtk_widget_activate (focus_widget); @@ -384,178 +422,201 @@ static void gtk_popover_init (GtkPopover *popover) { GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkWidget *widget; - GtkStyleContext *context; GtkEventController *controller; + GtkStyleContext *context; - widget = GTK_WIDGET (popover); - gtk_widget_set_has_surface (widget, TRUE); + gtk_widget_set_has_surface (GTK_WIDGET (popover), TRUE); + + priv->position = GTK_POS_TOP; priv->modal = TRUE; - priv->tick_id = 0; - priv->state = STATE_HIDDEN; - priv->visible = FALSE; - priv->preferred_position = GTK_POS_TOP; - priv->constraint = GTK_POPOVER_CONSTRAINT_WINDOW; - - priv->arrow_node = gtk_css_node_new (); - gtk_css_node_set_name (priv->arrow_node, I_("arrow")); - gtk_css_node_set_parent (priv->arrow_node, gtk_widget_get_css_node (widget)); - gtk_css_node_set_state (priv->arrow_node, - gtk_css_node_get_state (gtk_widget_get_css_node (widget))); - g_signal_connect_object (priv->arrow_node, "style-changed", - G_CALLBACK (node_style_changed_cb), popover, 0); - g_object_unref (priv->arrow_node); - - priv->contents_widget = gtk_gizmo_new ("contents", measure_contents, allocate_contents, NULL, NULL); - - gtk_widget_set_parent (priv->contents_widget, widget); - - context = gtk_widget_get_style_context (priv->contents_widget); - gtk_style_context_add_class (context, GTK_STYLE_CLASS_BACKGROUND); controller = gtk_event_controller_key_new (); - g_signal_connect (controller, "key-pressed", - G_CALLBACK (key_controller_key_pressed), popover); - gtk_widget_add_controller (widget, controller); + g_signal_connect_swapped (controller, "focus-in", G_CALLBACK (gtk_popover_focus_in), popover); + g_signal_connect_swapped (controller, "focus-out", G_CALLBACK (gtk_popover_focus_out), popover); + g_signal_connect_swapped (controller, "key-pressed", G_CALLBACK (gtk_popover_key_pressed), popover); + gtk_widget_add_controller (GTK_WIDGET (popover), controller); - controller = GTK_EVENT_CONTROLLER (gtk_gesture_multi_press_new ()); - g_signal_connect (controller, "pressed", - G_CALLBACK (gesture_pressed), popover); - g_signal_connect (controller, "released", - G_CALLBACK (gesture_released), popover); - gtk_widget_add_controller (widget, controller); + priv->contents_widget = gtk_gizmo_new ("contents", + measure_contents, + allocate_contents, + NULL, + NULL); + gtk_widget_set_parent (priv->contents_widget, GTK_WIDGET (popover)); + + context = gtk_widget_get_style_context (GTK_WIDGET (popover)); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_BACKGROUND); add_actions (popover); } static void -gtk_popover_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) +gtk_popover_realize (GtkWidget *widget) { - switch (prop_id) + GtkPopover *popover = GTK_POPOVER (widget); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + GdkRectangle parent_rect; + GdkDisplay *display; + + gtk_widget_get_surface_allocation (priv->relative_to, &parent_rect); + + display = gtk_widget_get_display (priv->relative_to); + + priv->surface = gdk_surface_new_popup_full (display, gtk_widget_get_surface (priv->relative_to)); + + gtk_widget_set_surface (widget, priv->surface); + g_signal_connect_swapped (priv->surface, "notify::state", G_CALLBACK (surface_state_changed), widget); + g_signal_connect_swapped (priv->surface, "size-changed", G_CALLBACK (surface_size_changed), widget); + + gtk_widget_register_surface (widget, priv->surface); + + GTK_WIDGET_CLASS (gtk_popover_parent_class)->realize (widget); + + priv->renderer = gsk_renderer_new_for_surface (priv->surface); +} + +static void +gtk_popover_unrealize (GtkWidget *widget) +{ + GtkPopover *popover = GTK_POPOVER (widget); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + GTK_WIDGET_CLASS (gtk_popover_parent_class)->unrealize (widget); + + gsk_renderer_unrealize (priv->renderer); + g_clear_object (&priv->renderer); + + g_signal_handlers_disconnect_by_func (priv->surface, surface_state_changed, widget); + g_signal_handlers_disconnect_by_func (priv->surface, surface_size_changed, widget); + + g_clear_object (&priv->surface); +} + +static void +gtk_popover_move_focus (GtkWidget *widget, + GtkDirectionType dir) +{ + gtk_widget_child_focus (widget, dir); + + if (!gtk_widget_get_focus_child (widget)) + gtk_root_set_focus (GTK_ROOT (widget), NULL); +} + +static void +gtk_popover_show (GtkWidget *widget) +{ + GtkPopover *popover = GTK_POPOVER (widget); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + _gtk_widget_set_visible_flag (widget, TRUE); + gtk_css_node_validate (gtk_widget_get_css_node (widget)); + gtk_widget_realize (widget); + gtk_popover_native_check_resize (GTK_NATIVE (widget)); + gtk_widget_map (widget); + + if (priv->modal) { - case PROP_RELATIVE_TO: - gtk_popover_set_relative_to (GTK_POPOVER (object), - g_value_get_object (value)); - break; - case PROP_POINTING_TO: - gtk_popover_set_pointing_to (GTK_POPOVER (object), - g_value_get_boxed (value)); - break; - case PROP_POSITION: - gtk_popover_set_position (GTK_POPOVER (object), - g_value_get_enum (value)); - break; - case PROP_MODAL: - gtk_popover_set_modal (GTK_POPOVER (object), - g_value_get_boolean (value)); - break; - case PROP_CONSTRAIN_TO: - gtk_popover_set_constrain_to (GTK_POPOVER (object), - g_value_get_enum (value)); - break; - case PROP_DEFAULT_WIDGET: - gtk_popover_set_default_widget (GTK_POPOVER (object), - g_value_get_object (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + if (!gtk_widget_get_focus_child (widget)) + gtk_widget_child_focus (widget, GTK_DIR_TAB_FORWARD); } } static void -gtk_popover_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) +gtk_popover_hide (GtkWidget *widget) { - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (GTK_POPOVER (object)); + _gtk_widget_set_visible_flag (widget, FALSE); + gtk_widget_unmap (widget); + g_signal_emit (widget, signals[CLOSED], 0); +} - 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; - case PROP_MODAL: - g_value_set_boolean (value, priv->modal); - break; - case PROP_CONSTRAIN_TO: - g_value_set_enum (value, priv->constraint); - break; - case PROP_DEFAULT_WIDGET: - g_value_set_object (value, priv->default_widget); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } +static void +grab_prepare_func (GdkSeat *seat, + GdkSurface *surface, + gpointer data) +{ + gdk_surface_show (surface); +} + +static void +unset_surface_transform_changed_cb (gpointer data) +{ + GtkPopover *popover = data; + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + priv->surface_transform_changed_cb = 0; } static gboolean -transitions_enabled (GtkPopover *popover) +surface_transform_changed_cb (GtkWidget *widget, + const graphene_matrix_t *transform, + gpointer user_data) { - return gtk_settings_get_enable_animations (gtk_widget_get_settings (GTK_WIDGET (popover))); + move_to_rect (GTK_POPOVER (user_data)); + + return G_SOURCE_CONTINUE; } static void -gtk_popover_hide_internal (GtkPopover *popover) +gtk_popover_map (GtkWidget *widget) { + GtkPopover *popover = GTK_POPOVER (widget); GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkWidget *widget = GTK_WIDGET (popover); - - if (!priv->visible) - return; - - priv->visible = FALSE; - g_signal_emit (widget, signals[CLOSED], 0); + GtkWidget *child; + GdkRectangle parent_rect; + GdkDisplay *display; if (priv->modal) - gtk_popover_apply_modality (popover, FALSE); - - if (gtk_widget_get_realized (widget)) { - cairo_region_t *region = cairo_region_create (); - gdk_surface_input_shape_combine_region (gtk_widget_get_surface (widget), - region, 0, 0); - cairo_region_destroy (region); + display = gtk_widget_get_display (priv->relative_to); + gdk_seat_grab (gdk_display_get_default_seat (display), + priv->surface, + GDK_SEAT_CAPABILITY_ALL, + TRUE, + NULL, NULL, grab_prepare_func, NULL); + priv->has_grab = TRUE; } + + gtk_widget_get_surface_allocation (priv->relative_to, &parent_rect); + move_to_rect (popover); + + priv->surface_transform_changed_cb = + gtk_widget_add_surface_transform_changed_callback (priv->relative_to, + surface_transform_changed_cb, + popover, + unset_surface_transform_changed_cb); + + GTK_WIDGET_CLASS (gtk_popover_parent_class)->map (widget); + + child = gtk_bin_get_child (GTK_BIN (widget)); + if (child != NULL && gtk_widget_get_visible (child)) + gtk_widget_map (child); } static void -gtk_popover_finalize (GObject *object) +gtk_popover_unmap (GtkWidget *widget) { - GtkPopover *popover = GTK_POPOVER (object); + GtkPopover *popover = GTK_POPOVER (widget); GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + GtkWidget *child; - if (priv->widget) - gtk_popover_update_relative_to (popover, NULL); + gtk_widget_remove_surface_transform_changed_callback (priv->relative_to, + priv->surface_transform_changed_cb); + priv->surface_transform_changed_cb = 0; - G_OBJECT_CLASS (gtk_popover_parent_class)->finalize (object); -} + GTK_WIDGET_CLASS (gtk_popover_parent_class)->unmap (widget); -static void -popover_unset_prev_focus (GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (!priv->prev_focus_widget) - return; - - if (priv->prev_focus_unmap_id) + gdk_surface_hide (priv->surface); + if (priv->has_grab) { - g_signal_handler_disconnect (priv->prev_focus_widget, - priv->prev_focus_unmap_id); - priv->prev_focus_unmap_id = 0; + GdkDisplay *display; + + display = gtk_widget_get_display (priv->relative_to); + gdk_seat_ungrab (gdk_display_get_default_seat (display)); + priv->has_grab = FALSE; } - g_clear_object (&priv->prev_focus_widget); + child = gtk_bin_get_child (GTK_BIN (widget)); + if (child != NULL) + gtk_widget_unmap (child); } static void @@ -563,840 +624,45 @@ gtk_popover_dispose (GObject *object) { GtkPopover *popover = GTK_POPOVER (object); GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + guint i; + GtkWidget *child; - if (priv->modal) - gtk_popover_apply_modality (popover, FALSE); - - if (priv->window) + for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (popover_list)); i++) { - g_signal_handlers_disconnect_by_data (priv->window, popover); - _gtk_window_remove_popover (priv->window, GTK_WIDGET (object)); - } - - priv->window = NULL; - - if (priv->widget) - gtk_popover_update_relative_to (popover, NULL); - - popover_unset_prev_focus (popover); - - g_clear_object (&priv->default_widget); - - if (priv->contents_widget) - { - GtkWidget *child = gtk_bin_get_child (GTK_BIN (popover)); - - if (child) + gpointer item = g_list_model_get_item (G_LIST_MODEL (popover_list), i); + if (item == object) { - /* Parent is content_widget! */ - gtk_widget_unparent (child); - _gtk_bin_set_child (GTK_BIN (popover), NULL); + g_list_store_remove (popover_list, i); + break; } - - gtk_widget_unparent (priv->contents_widget); - priv->contents_widget = NULL; + else + g_object_unref (item); } + child = gtk_bin_get_child (GTK_BIN (popover)); + + if (child) + { + gtk_widget_unparent (child); + _gtk_bin_set_child (GTK_BIN (popover), NULL); + } + + g_clear_pointer (&priv->contents_widget, gtk_widget_unparent); + G_OBJECT_CLASS (gtk_popover_parent_class)->dispose (object); } static void -gtk_popover_realize (GtkWidget *widget) +gtk_popover_finalize (GObject *object) { - GtkAllocation allocation; - GdkSurface *surface; - - gtk_widget_get_surface_allocation (widget, &allocation); - - surface = gdk_surface_new_child (gtk_widget_get_surface (gtk_widget_get_parent (widget)), &allocation); - - gtk_widget_set_surface (widget, surface); - gtk_widget_register_surface (widget, surface); - - GTK_WIDGET_CLASS (gtk_popover_parent_class)->realize (widget); + G_OBJECT_CLASS (gtk_popover_parent_class)->finalize (object); } static void -window_active_changed (GtkWindow *window, - GParamSpec *pspec, - GtkPopover *popover) +gtk_popover_constructed (GObject *object) { - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (!priv->modal || - !gtk_widget_is_drawable (GTK_WIDGET (popover))) - return; - - if (gtk_window_is_active (window)) - { - /* Regain the grab when the window is focused */ - GtkWidget *focus; - - gtk_grab_add (GTK_WIDGET (popover)); - - focus = gtk_window_get_focus (window); - - if (focus == NULL || !gtk_widget_is_ancestor (focus, GTK_WIDGET (popover))) - gtk_widget_grab_focus (GTK_WIDGET (popover)); - - if (priv->grab_notify_blocked) - g_signal_handler_unblock (priv->widget, priv->grab_notify_id); - - priv->grab_notify_blocked = FALSE; - } - else - { - /* Temporarily remove the grab when unfocused */ - g_signal_handler_block (priv->widget, priv->grab_notify_id); - gtk_grab_remove (GTK_WIDGET (popover)); - - priv->grab_notify_blocked = TRUE; - } -} - -static void -window_set_focus (GtkWindow *window, - GParamSpec *pspec, - GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkWidget *widget = gtk_root_get_focus (GTK_ROOT (window)); - - if (!priv->modal || !widget || !gtk_widget_is_drawable (GTK_WIDGET (popover))) - return; - - widget = gtk_widget_get_ancestor (widget, GTK_TYPE_POPOVER); - while (widget != NULL) - { - if (widget == GTK_WIDGET (popover)) - return; - - widget = gtk_popover_get_relative_to (GTK_POPOVER (widget)); - if (widget == NULL) - break; - widget = gtk_widget_get_ancestor (widget, GTK_TYPE_POPOVER); - } - - popover_unset_prev_focus (popover); - gtk_widget_hide (GTK_WIDGET (popover)); -} - -static void -prev_focus_unmap_cb (GtkWidget *widget, - GtkPopover *popover) -{ - popover_unset_prev_focus (popover); -} - -static void -gtk_popover_apply_modality (GtkPopover *popover, - gboolean modal) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (!priv->window) - return; - - if (modal) - { - GtkWidget *prev_focus; - - prev_focus = gtk_window_get_focus (priv->window); - priv->prev_focus_widget = prev_focus; - if (priv->prev_focus_widget) - { - priv->prev_focus_unmap_id = - g_signal_connect (prev_focus, "unmap", - G_CALLBACK (prev_focus_unmap_cb), popover); - g_object_ref (prev_focus); - } - gtk_grab_add (GTK_WIDGET (popover)); - gtk_window_set_focus (priv->window, NULL); - gtk_widget_grab_focus (GTK_WIDGET (popover)); - - g_signal_connect (priv->window, "notify::is-active", - G_CALLBACK (window_active_changed), popover); - g_signal_connect (priv->window, "notify::focus-widget", - G_CALLBACK (window_set_focus), popover); - } - else - { - g_signal_handlers_disconnect_by_data (priv->window, popover); - if (priv->prev_focus_widget == GTK_WIDGET (priv->window)) - priv->prev_focus_unmap_id = 0; - gtk_grab_remove (GTK_WIDGET (popover)); - - /* Let prev_focus_widget regain focus */ - if (priv->prev_focus_widget && - gtk_widget_is_drawable (priv->prev_focus_widget)) - { - if (GTK_IS_ENTRY (priv->prev_focus_widget)) - gtk_entry_grab_focus_without_selecting (GTK_ENTRY (priv->prev_focus_widget)); - else - gtk_widget_grab_focus (priv->prev_focus_widget); - } - else if (priv->window) - gtk_widget_grab_focus (GTK_WIDGET (priv->window)); - - popover_unset_prev_focus (popover); - } -} - -static gboolean -show_animate_cb (GtkWidget *widget, - GdkFrameClock *frame_clock, - gpointer user_data) -{ - GtkPopover *popover = GTK_POPOVER (widget); - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - gdouble t; - - if (priv->first_frame_skipped) - gtk_progress_tracker_advance_frame (&priv->tracker, - gdk_frame_clock_get_frame_time (frame_clock)); - else - priv->first_frame_skipped = TRUE; - - t = gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE); - - if (priv->state == STATE_SHOWING) - { - priv->transition_diff = TRANSITION_DIFF - (TRANSITION_DIFF * t); - gtk_widget_set_opacity (widget, t); - } - else if (priv->state == STATE_HIDING) - { - priv->transition_diff = -TRANSITION_DIFF * t; - gtk_widget_set_opacity (widget, 1.0 - t); - } - - gtk_popover_update_position (popover); - gtk_widget_queue_allocate (GTK_WIDGET (priv->window)); - - if (gtk_progress_tracker_get_state (&priv->tracker) == GTK_PROGRESS_STATE_AFTER) - { - if (priv->state == STATE_SHOWING) - { - gtk_popover_set_state (popover, STATE_SHOWN); - - if (!priv->visible) - gtk_popover_set_state (popover, STATE_HIDING); - } - else - { - gtk_widget_hide (widget); - } - - priv->tick_id = 0; - return G_SOURCE_REMOVE; - } - else - return G_SOURCE_CONTINUE; -} - -static void -gtk_popover_stop_transition (GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (priv->tick_id != 0) - { - gtk_widget_remove_tick_callback (GTK_WIDGET (popover), priv->tick_id); - priv->tick_id = 0; - } -} - -static void -gtk_popover_start_transition (GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (priv->tick_id != 0) - return; - - priv->first_frame_skipped = FALSE; - gtk_progress_tracker_start (&priv->tracker, TRANSITION_DURATION, 0, 1.0); - priv->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (popover), - show_animate_cb, - popover, NULL); -} - -static void -gtk_popover_set_state (GtkPopover *popover, - guint state) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (!transitions_enabled (popover) || - !gtk_widget_get_realized (GTK_WIDGET (popover))) - { - if (state == STATE_SHOWING) - state = STATE_SHOWN; - else if (state == STATE_HIDING) - state = STATE_HIDDEN; - } - - priv->state = state; - - if (state == STATE_SHOWING || state == STATE_HIDING) - gtk_popover_start_transition (popover); - else - { - gtk_popover_stop_transition (popover); - - gtk_widget_set_visible (GTK_WIDGET (popover), state == STATE_SHOWN); - } -} - -static void -gtk_popover_map (GtkWidget *widget) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (GTK_POPOVER (widget)); - - priv->prev_default = gtk_window_get_default_widget (priv->window); - if (priv->prev_default) - g_object_ref (priv->prev_default); - - GTK_WIDGET_CLASS (gtk_popover_parent_class)->map (widget); - - gdk_surface_show (gtk_widget_get_surface (widget)); - gtk_popover_update_position (GTK_POPOVER (widget)); - - gtk_window_set_default_widget (priv->window, priv->default_widget); -} - -static void -gtk_popover_unmap (GtkWidget *widget) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (GTK_POPOVER (widget)); - - priv->button_pressed = FALSE; - - gdk_surface_hide (gtk_widget_get_surface (widget)); - GTK_WIDGET_CLASS (gtk_popover_parent_class)->unmap (widget); - - if (gtk_window_get_default_widget (priv->window) == priv->default_widget) - gtk_window_set_default_widget (priv->window, priv->prev_default); - g_clear_object (&priv->prev_default); -} - -static GtkPositionType -get_effective_position (GtkPopover *popover, - GtkPositionType pos) -{ - if (_gtk_widget_get_direction (GTK_WIDGET (popover)) == GTK_TEXT_DIR_RTL) - { - if (pos == GTK_POS_LEFT) - pos = GTK_POS_RIGHT; - else if (pos == GTK_POS_RIGHT) - pos = GTK_POS_LEFT; - } - - return pos; -} - -static void -get_margin (GtkWidget *widget, - GtkBorder *border) -{ - GtkStyleContext *context; - - context = gtk_widget_get_style_context (widget); - gtk_style_context_get_margin (context, - border); -} - -static void -gtk_popover_get_gap_coords (GtkPopover *popover, - gint *initial_x_out, - gint *initial_y_out, - gint *tip_x_out, - gint *tip_y_out, - gint *final_x_out, - gint *final_y_out) -{ - GtkWidget *widget = GTK_WIDGET (popover); - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GdkRectangle rect = { 0 }; - gint base, tip, tip_pos; - gint initial_x, initial_y; - gint tip_x, tip_y; - gint final_x, final_y; - GtkPositionType pos; - gint border_radius; - GtkStyleContext *context; - GtkBorder border; - int popover_width, popover_height; - - gtk_popover_get_pointing_to (popover, &rect); - popover_width = gtk_widget_get_width (widget); - popover_height = gtk_widget_get_height (widget); - -#ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (widget))) - { - gint win_x, win_y; - - gtk_widget_translate_coordinates (priv->widget, GTK_WIDGET (priv->window), - rect.x, rect.y, &rect.x, &rect.y); - gdk_surface_get_origin (gtk_widget_get_surface (GTK_WIDGET (popover)), - &win_x, &win_y); - rect.x -= win_x; - rect.y -= win_y; - } - else -#endif - gtk_widget_translate_coordinates (priv->widget, widget, - rect.x, rect.y, &rect.x, &rect.y); - - context = gtk_widget_get_style_context (priv->contents_widget); - gtk_style_context_get_border (context, &border); - - pos = get_effective_position (popover, priv->final_position); - - gtk_style_context_get_border (context, &border); - gtk_style_context_get (context, - GTK_STYLE_PROPERTY_BORDER_RADIUS, &border_radius, - NULL); - - if (pos == GTK_POS_BOTTOM || pos == GTK_POS_RIGHT) - { - tip = 0; - base = tip + TAIL_HEIGHT + border.top; - } - else if (pos == GTK_POS_TOP) - { - base = popover_height - border.bottom - TAIL_HEIGHT; - tip = base + TAIL_HEIGHT; - } - else if (pos == GTK_POS_LEFT) - { - base = popover_width - border.right - TAIL_HEIGHT; - tip = base + TAIL_HEIGHT; - } - else - g_assert_not_reached (); - - if (POS_IS_VERTICAL (pos)) - { - tip_pos = rect.x + (rect.width / 2); - initial_x = CLAMP (tip_pos - TAIL_GAP_WIDTH / 2, - border_radius, - popover_width - TAIL_GAP_WIDTH - border_radius); - initial_y = base; - - tip_x = CLAMP (tip_pos, 0, popover_width); - tip_y = tip; - - final_x = CLAMP (tip_pos + TAIL_GAP_WIDTH / 2, - border_radius + TAIL_GAP_WIDTH, - popover_width - border_radius); - final_y = base; - } - else - { - tip_pos = rect.y + (rect.height / 2); - - initial_x = base; - initial_y = CLAMP (tip_pos - TAIL_GAP_WIDTH / 2, - border_radius, - popover_height - TAIL_GAP_WIDTH - border_radius); - - tip_x = tip; - tip_y = CLAMP (tip_pos, 0, popover_height); - - final_x = base; - final_y = CLAMP (tip_pos + TAIL_GAP_WIDTH / 2, - border_radius + TAIL_GAP_WIDTH, - popover_height - border_radius); - } - - *initial_x_out = initial_x; - *initial_y_out = initial_y; - - *tip_x_out = tip_x; - *tip_y_out = tip_y; - - *final_x_out = final_x; - *final_y_out = final_y; -} - -static void -gtk_popover_get_rect_for_size (GtkPopover *popover, - int popover_width, - int popover_height, - GdkRectangle *rect) -{ - GtkWidget *widget = GTK_WIDGET (popover); - int x, y, w, h; - GtkBorder margin; - - get_margin (widget, &margin); - - x = 0; - y = 0; - w = popover_width; - h = popover_height; - - x += MAX (TAIL_HEIGHT, margin.left); - y += MAX (TAIL_HEIGHT, margin.top); - w -= x + MAX (TAIL_HEIGHT, margin.right); - h -= y + MAX (TAIL_HEIGHT, margin.bottom); - - rect->x = x; - rect->y = y; - rect->width = w; - rect->height = h; -} - -static void -gtk_popover_get_rect_coords (GtkPopover *popover, - int *x_out, - int *y_out, - int *w_out, - int *h_out) -{ - GtkWidget *widget = GTK_WIDGET (popover); - GdkRectangle rect; - GtkAllocation allocation; - - gtk_widget_get_allocation (widget, &allocation); - gtk_popover_get_rect_for_size (popover, allocation.width, allocation.height, &rect); - - *x_out = rect.x; - *y_out = rect.y; - *w_out = rect.width; - *h_out = rect.height; -} - -static void -gtk_popover_apply_tail_path (GtkPopover *popover, - cairo_t *cr) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - gint initial_x, initial_y; - gint tip_x, tip_y; - gint final_x, final_y; - GtkStyleContext *context; - GtkBorder border; - - if (!priv->widget) - return; - - context = gtk_widget_get_style_context (priv->contents_widget); - gtk_style_context_get_border (context, &border); - - cairo_set_line_width (cr, 1); - gtk_popover_get_gap_coords (popover, - &initial_x, &initial_y, - &tip_x, &tip_y, - &final_x, &final_y); - - 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_popover_fill_border_path (GtkPopover *popover, - cairo_t *cr) -{ - GtkWidget *widget = GTK_WIDGET (popover); - GtkAllocation allocation; - GtkStyleContext *context; - int x, y, w, h; - GskRoundedRect box; - - context = gtk_widget_get_style_context (widget); - gtk_widget_get_allocation (widget, &allocation); - - cairo_set_source_rgba (cr, 0, 0, 0, 1); - - gtk_popover_apply_tail_path (popover, cr); - cairo_close_path (cr); - cairo_fill (cr); - - gtk_popover_get_rect_coords (popover, &x, &y, &w, &h); - - gtk_rounded_boxes_init_for_style (&box, - NULL, NULL, - gtk_style_context_lookup_style (context), - x, y, w, h); - gsk_rounded_rect_path (&box, cr); - cairo_fill (cr); -} - -static void -gtk_popover_update_shape (GtkPopover *popover) -{ - GtkWidget *widget = GTK_WIDGET (popover); - cairo_surface_t *cairo_surface; - cairo_region_t *region; - GdkSurface *surface; - cairo_t *cr; - -#ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (widget))) - return; -#endif - - surface = gtk_widget_get_surface (widget); - cairo_surface = - gdk_surface_create_similar_surface (surface, - CAIRO_CONTENT_COLOR_ALPHA, - gdk_surface_get_width (surface), - gdk_surface_get_height (surface)); - - cr = cairo_create (cairo_surface); - gtk_popover_fill_border_path (popover, cr); - cairo_destroy (cr); - - region = gdk_cairo_region_create_from_surface (cairo_surface); - cairo_surface_destroy (cairo_surface); - - gtk_widget_input_shape_combine_region (widget, region); - cairo_region_destroy (region); - - gdk_surface_set_child_input_shapes (gtk_widget_get_surface (widget)); -} - -static void -_gtk_popover_update_child_visible (GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkWidget *widget = GTK_WIDGET (popover); - GdkRectangle rect; - GtkAllocation allocation; - GtkWidget *parent; - - if (!priv->parent_scrollable) - { - gtk_widget_set_child_visible (widget, TRUE); - return; - } - - parent = gtk_widget_get_parent (GTK_WIDGET (priv->parent_scrollable)); - gtk_popover_get_pointing_to (popover, &rect); - - gtk_widget_translate_coordinates (priv->widget, parent, - rect.x, rect.y, &rect.x, &rect.y); - - gtk_widget_get_allocation (GTK_WIDGET (parent), &allocation); - - if (rect.x + rect.width < 0 || rect.x > allocation.width || - rect.y + rect.height < 0 || rect.y > allocation.height) - gtk_widget_set_child_visible (widget, FALSE); - else - gtk_widget_set_child_visible (widget, TRUE); -} - -static GtkPositionType -opposite_position (GtkPositionType pos) -{ - switch (pos) - { - default: - case GTK_POS_LEFT: return GTK_POS_RIGHT; - case GTK_POS_RIGHT: return GTK_POS_LEFT; - case GTK_POS_TOP: return GTK_POS_BOTTOM; - case GTK_POS_BOTTOM: return GTK_POS_TOP; - } -} - -void -gtk_popover_update_position (GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkWidget *widget = GTK_WIDGET (popover); - GtkAllocation window_alloc; - GtkBorder window_shadow; - GdkRectangle rect = { 0 }; - GtkRequisition req; - GtkPositionType pos; - gint overshoot[4]; - gint i, j; - gint best; - - if (!priv->window || !gtk_widget_get_mapped (widget)) - return; - - gtk_widget_get_preferred_size (widget, NULL, &req); - gtk_widget_get_allocation (GTK_WIDGET (priv->window), &window_alloc); - _gtk_window_get_shadow_width (priv->window, &window_shadow); - priv->final_position = priv->preferred_position; - - gtk_popover_get_pointing_to (popover, &rect); - gtk_widget_translate_coordinates (priv->widget, GTK_WIDGET (priv->window), - rect.x, rect.y, &rect.x, &rect.y); - - pos = get_effective_position (popover, priv->preferred_position); - - overshoot[GTK_POS_TOP] = req.height - rect.y + window_shadow.top; - overshoot[GTK_POS_BOTTOM] = rect.y + rect.height + req.height - window_alloc.height - + window_shadow.bottom; - overshoot[GTK_POS_LEFT] = req.width - rect.x + window_shadow.left; - overshoot[GTK_POS_RIGHT] = rect.x + rect.width + req.width - window_alloc.width - + window_shadow.right; - -#ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (widget)) && - priv->constraint == GTK_POPOVER_CONSTRAINT_NONE) - { - priv->final_position = priv->preferred_position; - } - else -#endif - if (overshoot[pos] <= 0) - { - priv->final_position = priv->preferred_position; - } - else if (overshoot[opposite_position (pos)] <= 0) - { - priv->final_position = opposite_position (priv->preferred_position); - } - else - { - best = G_MAXINT; - pos = 0; - for (i = 0; i < 4; i++) - { - j = get_effective_position (popover, i); - if (overshoot[j] < best) - { - pos = i; - best = overshoot[j]; - } - } - priv->final_position = pos; - } - - switch (priv->final_position) - { - case GTK_POS_TOP: - rect.y += priv->transition_diff; - break; - case GTK_POS_BOTTOM: - rect.y -= priv->transition_diff; - break; - case GTK_POS_LEFT: - rect.x += priv->transition_diff; - break; - case GTK_POS_RIGHT: - rect.x -= priv->transition_diff; - break; - default: - break; - } - - _gtk_window_set_popover_position (priv->window, widget, - priv->final_position, &rect); - - if (priv->final_position != priv->current_position) - { - if (gtk_widget_is_drawable (widget)) - gtk_popover_update_shape (popover); - - priv->current_position = priv->final_position; - } - - _gtk_popover_update_child_visible (popover); -} - -GtkWidget * -gtk_popover_get_contents_widget (GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - return priv->contents_widget; -} - -static void -gtk_popover_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - GtkPopover *popover = GTK_POPOVER (widget); - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkStyleContext *context; - GtkBorder border; - cairo_t *cr; - - /* Draw the child first so we can draw the arrow (partially) over it */ - gtk_widget_snapshot_child (widget, priv->contents_widget, snapshot); - - cr = gtk_snapshot_append_cairo (snapshot, - &GRAPHENE_RECT_INIT ( - 0, 0, - gtk_widget_get_width (widget), - gtk_widget_get_height (widget) - )); - - /* Clip to the arrow shape */ - cairo_save (cr); - - gtk_popover_apply_tail_path (popover, cr); - - cairo_clip (cr); - - context = gtk_widget_get_style_context (widget); - gtk_style_context_save_to_node (context, priv->arrow_node); - gtk_style_context_get_border (context, &border); - - /* Render the arrow background */ - gtk_render_background (context, cr, - 0, 0, - gtk_widget_get_width (widget), - gtk_widget_get_height (widget)); - - /* Render the border of the arrow tip */ - if (border.bottom > 0) - { - GdkRGBA *border_color; - - gtk_style_context_get (context, "border-color", &border_color, NULL); - gtk_popover_apply_tail_path (popover, cr); - gdk_cairo_set_source_rgba (cr, border_color); - - cairo_set_line_width (cr, border.bottom + 1); - cairo_stroke (cr); - gdk_rgba_free (border_color); - } - - cairo_restore (cr); - /* We're done */ - cairo_destroy (cr); - - gtk_style_context_restore (context); -} - -static gint -get_border_radius (GtkWidget *widget) -{ - GtkStyleContext *context; - gint border_radius; - - context = gtk_widget_get_style_context (widget); - gtk_style_context_get (context, - GTK_STYLE_PROPERTY_BORDER_RADIUS, &border_radius, - NULL); - return border_radius; -} - -static gint -get_minimal_size (GtkPopover *popover, - GtkOrientation orientation) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkPositionType pos; - gint minimal_size; - - minimal_size = 2 * get_border_radius (GTK_WIDGET (popover)); - pos = get_effective_position (popover, priv->preferred_position); - - if ((orientation == GTK_ORIENTATION_HORIZONTAL && POS_IS_VERTICAL (pos)) || - (orientation == GTK_ORIENTATION_VERTICAL && !POS_IS_VERTICAL (pos))) - minimal_size += TAIL_GAP_WIDTH; - - return minimal_size; + g_list_store_append (popover_list, object); + g_object_unref (object); } static void @@ -1410,22 +676,11 @@ gtk_popover_measure (GtkWidget *widget, { GtkPopover *popover = GTK_POPOVER (widget); GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - int minimal_size; - *minimum = 0; - *natural = 0; - - if (for_size >= 0) - for_size -= TAIL_HEIGHT; - - gtk_widget_measure (priv->contents_widget, orientation, for_size, minimum, natural, NULL, NULL); - - minimal_size = get_minimal_size (popover, orientation); - *minimum = MAX (*minimum, minimal_size); - *natural = MAX (*natural, minimal_size); - - *minimum += TAIL_HEIGHT; - *natural += TAIL_HEIGHT; + gtk_widget_measure (priv->contents_widget, + orientation, for_size, + minimum, natural, + minimum_baseline, natural_baseline); } static void @@ -1436,133 +691,96 @@ gtk_popover_size_allocate (GtkWidget *widget, { GtkPopover *popover = GTK_POPOVER (widget); GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkAllocation child_alloc = (GtkAllocation) {0, 0, width, height}; - /* Note that we in measure() we add TAIL_HEIGHT in both directions, regardless - * of the popovers position. This is to ensure that we get enough space - * even priv->current_position changes between measure and size-allocate. - */ - child_alloc.height -= TAIL_HEIGHT; - child_alloc.width -= TAIL_HEIGHT; + if (priv->surface) + gtk_popover_move_resize (popover); - switch (priv->current_position) - { - case GTK_POS_TOP: - child_alloc.x += TAIL_HEIGHT / 2; - break; - case GTK_POS_BOTTOM: - child_alloc.x += TAIL_HEIGHT / 2; - child_alloc.y += TAIL_HEIGHT; - break; - case GTK_POS_LEFT: - child_alloc.y += TAIL_HEIGHT / 2; - break; - case GTK_POS_RIGHT: - child_alloc.x += TAIL_HEIGHT; - child_alloc.y += TAIL_HEIGHT / 2; - break; - default: - break; - } - - gtk_widget_size_allocate (priv->contents_widget, &child_alloc, -1); - - if (gtk_widget_get_realized (widget)) - { - GtkAllocation a; - gtk_widget_get_surface_allocation (widget, &a); - gdk_surface_move_resize (gtk_widget_get_surface (widget), - a.x, a.y, a.width, a.height); - gtk_popover_update_shape (popover); - } + gtk_widget_size_allocate (priv->contents_widget, + &(GtkAllocation) { 0, 0, width, height }, + baseline); } static void -gtk_popover_grab_focus (GtkWidget *widget) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (GTK_POPOVER (widget)); - GtkWidget *child; - - if (!priv->visible) - return; - - /* Focus the first natural child */ - child = gtk_bin_get_child (GTK_BIN (widget)); - - if (child) - gtk_widget_child_focus (child, GTK_DIR_TAB_FORWARD); -} - -static gboolean -gtk_popover_focus (GtkWidget *widget, - GtkDirectionType direction) +gtk_popover_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) { GtkPopover *popover = GTK_POPOVER (widget); GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - if (!priv->visible) - return FALSE; + gtk_widget_snapshot_child (widget, priv->contents_widget, snapshot); +} - if (!GTK_WIDGET_CLASS (gtk_popover_parent_class)->focus (widget, direction)) +static void +gtk_popover_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkPopover *popover = GTK_POPOVER (object); + + switch (prop_id) { - GtkWidget *focus; + case PROP_RELATIVE_TO: + gtk_popover_set_relative_to (popover, g_value_get_object (value)); + break; - focus = gtk_window_get_focus (priv->window); - focus = gtk_widget_get_parent (focus); + case PROP_POINTING_TO: + gtk_popover_set_pointing_to (popover, g_value_get_boxed (value)); + break; - /* Unset focus child through children, so it is next stepped from - * scratch. - */ - while (focus && focus != widget) - { - gtk_widget_set_focus_child (focus, NULL); - focus = gtk_widget_get_parent (focus); - } + case PROP_POSITION: + gtk_popover_set_position (popover, g_value_get_enum (value)); + break; - return gtk_widget_child_focus (gtk_bin_get_child (GTK_BIN (widget)), - direction); + case PROP_MODAL: + gtk_popover_set_modal (popover, g_value_get_boolean (value)); + break; + + case PROP_DEFAULT_WIDGET: + gtk_popover_set_default_widget (popover, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } - - return TRUE; } static void -gtk_popover_show (GtkWidget *widget) +gtk_popover_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (GTK_POPOVER (widget)); + GtkPopover *popover = GTK_POPOVER (object); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - if (priv->window) - _gtk_window_raise_popover (priv->window, widget); + switch (prop_id) + { + case PROP_RELATIVE_TO: + g_value_set_object (value, priv->relative_to); + break; - priv->visible = TRUE; + case PROP_POINTING_TO: + g_value_set_boxed (value, &priv->pointing_to); + break; - GTK_WIDGET_CLASS (gtk_popover_parent_class)->show (widget); + case PROP_POSITION: + g_value_set_enum (value, priv->position); + break; - if (priv->modal) - gtk_popover_apply_modality (GTK_POPOVER (widget), TRUE); + case PROP_MODAL: + g_value_set_boolean (value, priv->modal); + break; - priv->state = STATE_SHOWN; + case PROP_DEFAULT_WIDGET: + g_value_set_object (value, priv->default_widget); + break; - if (gtk_widget_get_realized (widget)) - gdk_surface_input_shape_combine_region (gtk_widget_get_surface (widget), - NULL, 0, 0); -} - -static void -gtk_popover_hide (GtkWidget *widget) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (GTK_POPOVER (widget)); - - gtk_popover_hide_internal (GTK_POPOVER (widget)); - - gtk_popover_stop_transition (GTK_POPOVER (widget)); - priv->state = STATE_HIDDEN; - priv->transition_diff = 0; - gtk_progress_tracker_finish (&priv->tracker); - gtk_widget_set_opacity (widget, 1.0); - - - GTK_WIDGET_CLASS (gtk_popover_parent_class)->hide (widget); + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } } static void @@ -1573,10 +791,9 @@ gtk_popover_add (GtkContainer *container, GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); _gtk_bin_set_child (GTK_BIN (popover), child); - gtk_widget_set_parent (child, priv->contents_widget); } - + static void gtk_popover_remove (GtkContainer *container, GtkWidget *child) @@ -1587,20 +804,6 @@ gtk_popover_remove (GtkContainer *container, gtk_widget_unparent (child); } -static void -gtk_popover_state_flags_changed (GtkWidget *widget, - GtkStateFlags previous_state) -{ - GtkPopover *popover = GTK_POPOVER (widget); - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkStateFlags state; - - state = gtk_widget_get_state_flags (widget); - gtk_css_node_set_state (priv->arrow_node, state); - - GTK_WIDGET_CLASS (gtk_popover_parent_class)->state_flags_changed (widget, previous_state); -} - static void gtk_popover_class_init (GtkPopoverClass *klass) { @@ -1608,31 +811,29 @@ gtk_popover_class_init (GtkPopoverClass *klass) GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + if (popover_list == NULL) + popover_list = g_list_store_new (GTK_TYPE_WIDGET); + + object_class->constructed = gtk_popover_constructed; + object_class->dispose = gtk_popover_dispose; + object_class->finalize = gtk_popover_finalize; object_class->set_property = gtk_popover_set_property; object_class->get_property = gtk_popover_get_property; - object_class->finalize = gtk_popover_finalize; - object_class->dispose = gtk_popover_dispose; widget_class->realize = gtk_popover_realize; + widget_class->unrealize = gtk_popover_unrealize; widget_class->map = gtk_popover_map; widget_class->unmap = gtk_popover_unmap; + widget_class->show = gtk_popover_show; + widget_class->hide = gtk_popover_hide; widget_class->measure = gtk_popover_measure; widget_class->size_allocate = gtk_popover_size_allocate; widget_class->snapshot = gtk_popover_snapshot; - widget_class->grab_focus = gtk_popover_grab_focus; - widget_class->focus = gtk_popover_focus; - widget_class->show = gtk_popover_show; - widget_class->hide = gtk_popover_hide; - widget_class->state_flags_changed = gtk_popover_state_flags_changed; + widget_class->move_focus = gtk_popover_move_focus; container_class->add = gtk_popover_add; container_class->remove = gtk_popover_remove; - /** - * GtkPopover:relative-to: - * - * Sets the attached widget. - */ properties[PROP_RELATIVE_TO] = g_param_spec_object ("relative-to", P_("Relative to"), @@ -1640,11 +841,6 @@ gtk_popover_class_init (GtkPopoverClass *klass) GTK_TYPE_WIDGET, GTK_PARAM_READWRITE); - /** - * GtkPopover:pointing-to: - * - * Marks a specific rectangle to be pointed. - */ properties[PROP_POINTING_TO] = g_param_spec_boxed ("pointing-to", P_("Pointing to"), @@ -1652,11 +848,6 @@ gtk_popover_class_init (GtkPopoverClass *klass) GDK_TYPE_RECTANGLE, GTK_PARAM_READWRITE); - /** - * GtkPopover:position - * - * Sets the preferred position of the popover. - */ properties[PROP_POSITION] = g_param_spec_enum ("position", P_("Position"), @@ -1664,12 +855,6 @@ gtk_popover_class_init (GtkPopoverClass *klass) GTK_TYPE_POSITION_TYPE, GTK_POS_TOP, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - /** - * GtkPopover:modal - * - * Sets whether the popover is modal (so other elements in the window do not - * receive input while the popover is visible). - */ properties[PROP_MODAL] = g_param_spec_boolean ("modal", P_("Modal"), @@ -1677,18 +862,6 @@ gtk_popover_class_init (GtkPopoverClass *klass) TRUE, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - /** - * GtkPopover:constrain-to: - * - * Sets a constraint for the popover position. - */ - properties[PROP_CONSTRAIN_TO] = - g_param_spec_enum ("constrain-to", - P_("Constraint"), - P_("Constraint for the popover position"), - GTK_TYPE_POPOVER_CONSTRAINT, GTK_POPOVER_CONSTRAINT_WINDOW, - GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); - properties[PROP_DEFAULT_WIDGET] = g_param_spec_object ("default-widget", P_("Default widget"), @@ -1698,406 +871,86 @@ gtk_popover_class_init (GtkPopoverClass *klass) g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); - /** - * GtkPopover::closed: - * - * This signal is emitted when the popover is dismissed either through - * API or user interaction. - */ signals[CLOSED] = g_signal_new (I_("closed"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkPopoverClass, closed), - NULL, NULL, NULL, - G_TYPE_NONE, 0); + NULL, NULL, + NULL, + G_TYPE_NONE, + 0); - quark_widget_popovers = g_quark_from_static_string ("gtk-quark-widget-popovers"); - gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_POPOVER_ACCESSIBLE); - gtk_widget_class_set_css_name (widget_class, I_("popover")); + gtk_widget_class_set_css_name (widget_class, "popover"); } -static void -gtk_popover_update_scrollable (GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkScrollable *scrollable; - - scrollable = GTK_SCROLLABLE (gtk_widget_get_ancestor (priv->widget, - GTK_TYPE_SCROLLABLE)); - gtk_popover_set_scrollable_full (popover, scrollable); -} - -static void -_gtk_popover_parent_hierarchy_changed (GtkWidget *widget, - GParamSpec *pspec, - GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - GtkWindow *new_window; - - new_window = GTK_WINDOW (gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW)); - - if (priv->window == new_window) - return; - - g_object_ref (popover); - - if (gtk_widget_has_grab (GTK_WIDGET (popover))) - gtk_popover_apply_modality (popover, FALSE); - - if (priv->window) - _gtk_window_remove_popover (priv->window, GTK_WIDGET (popover)); - - if (priv->parent_scrollable) - gtk_popover_set_scrollable_full (popover, NULL); - - priv->window = new_window; - - if (new_window) - { - _gtk_window_add_popover (new_window, GTK_WIDGET (popover), priv->widget, TRUE); - gtk_popover_update_scrollable (popover); - gtk_popover_update_position (popover); - } - - if (gtk_widget_is_visible (GTK_WIDGET (popover))) - gtk_widget_queue_resize (GTK_WIDGET (popover)); - - g_object_unref (popover); -} - -static void -_popover_propagate_state (GtkPopover *popover, - GtkStateFlags state, - GtkStateFlags old_state, - GtkStateFlags flag) -{ - if ((state & flag) != (old_state & flag)) - { - if ((state & flag) == flag) - gtk_widget_set_state_flags (GTK_WIDGET (popover), flag, FALSE); - else - gtk_widget_unset_state_flags (GTK_WIDGET (popover), flag); - } -} - -static void -_gtk_popover_parent_state_changed (GtkWidget *widget, - GtkStateFlags old_state, - GtkPopover *popover) -{ - guint state; - - state = gtk_widget_get_state_flags (widget); - _popover_propagate_state (popover, state, old_state, - GTK_STATE_FLAG_INSENSITIVE); - _popover_propagate_state (popover, state, old_state, - GTK_STATE_FLAG_BACKDROP); -} - -static void -_gtk_popover_parent_grab_notify (GtkWidget *widget, - gboolean was_shadowed, - GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (priv->modal && - gtk_widget_is_visible (GTK_WIDGET (popover)) && - !gtk_widget_has_grab (GTK_WIDGET (popover))) - { - GtkWidget *grab_widget; - - grab_widget = gtk_grab_get_current (); - - if (!grab_widget || !GTK_IS_POPOVER (grab_widget)) - gtk_popover_popdown (popover); - } -} - -static void -_gtk_popover_parent_unmap (GtkWidget *widget, - GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (priv->state == STATE_SHOWING) - priv->visible = FALSE; - else if (priv->state == STATE_SHOWN) - gtk_popover_set_state (popover, STATE_HIDING); -} - -static void -gtk_popover_parent_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline, - GtkPopover *popover) -{ - gtk_popover_update_position (popover); -} - -static void -_unmanage_popover (GObject *object) -{ - gtk_popover_update_relative_to (GTK_POPOVER (object), NULL); - g_object_unref (object); -} - -static void -widget_manage_popover (GtkWidget *widget, - GtkPopover *popover) -{ - GHashTable *popovers; - - popovers = g_object_get_qdata (G_OBJECT (widget), quark_widget_popovers); - - if (G_UNLIKELY (!popovers)) - { - popovers = g_hash_table_new_full (NULL, NULL, - (GDestroyNotify) _unmanage_popover, NULL); - g_object_set_qdata_full (G_OBJECT (widget), - quark_widget_popovers, popovers, - (GDestroyNotify) g_hash_table_unref); - } - - g_hash_table_add (popovers, g_object_ref_sink (popover)); -} - -static void -widget_unmanage_popover (GtkWidget *widget, - GtkPopover *popover) -{ - GHashTable *popovers; - - popovers = g_object_get_qdata (G_OBJECT (widget), quark_widget_popovers); - - if (G_UNLIKELY (!popovers)) - return; - - g_hash_table_remove (popovers, popover); -} - -static void -adjustment_changed_cb (GtkAdjustment *adjustment, - GtkPopover *popover) -{ - gtk_popover_update_position (popover); -} - -static void -_gtk_popover_set_scrollable (GtkPopover *popover, - GtkScrollable *scrollable) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (priv->parent_scrollable) - { - if (priv->vadj) - { - g_signal_handlers_disconnect_by_data (priv->vadj, popover); - g_object_unref (priv->vadj); - priv->vadj = NULL; - } - - if (priv->hadj) - { - g_signal_handlers_disconnect_by_data (priv->hadj, popover); - g_object_unref (priv->hadj); - priv->hadj = NULL; - } - - g_object_unref (priv->parent_scrollable); - } - - priv->parent_scrollable = scrollable; - - if (scrollable) - { - g_object_ref (scrollable); - priv->vadj = gtk_scrollable_get_vadjustment (scrollable); - priv->hadj = gtk_scrollable_get_hadjustment (scrollable); - - if (priv->vadj) - { - g_object_ref (priv->vadj); - g_signal_connect (priv->vadj, "changed", - G_CALLBACK (adjustment_changed_cb), popover); - g_signal_connect (priv->vadj, "value-changed", - G_CALLBACK (adjustment_changed_cb), popover); - } - - if (priv->hadj) - { - g_object_ref (priv->hadj); - g_signal_connect (priv->hadj, "changed", - G_CALLBACK (adjustment_changed_cb), popover); - g_signal_connect (priv->hadj, "value-changed", - G_CALLBACK (adjustment_changed_cb), popover); - } - } -} - -static void -scrollable_notify_cb (GObject *object, - GParamSpec *pspec, - GtkPopover *popover) -{ - if (pspec->value_type == GTK_TYPE_ADJUSTMENT) - _gtk_popover_set_scrollable (popover, GTK_SCROLLABLE (object)); -} - -static void -gtk_popover_set_scrollable_full (GtkPopover *popover, - GtkScrollable *scrollable) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (priv->scrollable_notify_id != 0 && - g_signal_handler_is_connected (priv->parent_scrollable, priv->scrollable_notify_id)) - { - g_signal_handler_disconnect (priv->parent_scrollable, priv->scrollable_notify_id); - priv->scrollable_notify_id = 0; - } - - _gtk_popover_set_scrollable (popover, scrollable); - - if (scrollable) - { - priv->scrollable_notify_id = - g_signal_connect (priv->parent_scrollable, "notify", - G_CALLBACK (scrollable_notify_cb), popover); - } -} - -static void -gtk_popover_update_relative_to (GtkPopover *popover, - GtkWidget *relative_to) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (priv->widget == relative_to) - return; - - g_object_ref (popover); - - if (priv->window) - { - _gtk_window_remove_popover (priv->window, GTK_WIDGET (popover)); - priv->window = NULL; - } - - popover_unset_prev_focus (popover); - - if (priv->widget) - { - if (g_signal_handler_is_connected (priv->widget, priv->hierarchy_changed_id)) - g_signal_handler_disconnect (priv->widget, priv->hierarchy_changed_id); - if (g_signal_handler_is_connected (priv->widget, priv->size_allocate_id)) - g_signal_handler_disconnect (priv->widget, priv->size_allocate_id); - if (g_signal_handler_is_connected (priv->widget, priv->unmap_id)) - g_signal_handler_disconnect (priv->widget, priv->unmap_id); - if (g_signal_handler_is_connected (priv->widget, priv->state_changed_id)) - g_signal_handler_disconnect (priv->widget, priv->state_changed_id); - if (g_signal_handler_is_connected (priv->widget, priv->grab_notify_id)) - g_signal_handler_disconnect (priv->widget, priv->grab_notify_id); - - widget_unmanage_popover (priv->widget, popover); - } - - if (priv->parent_scrollable) - gtk_popover_set_scrollable_full (popover, NULL); - - priv->widget = relative_to; - g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_RELATIVE_TO]); - - if (priv->widget) - { - priv->window = - GTK_WINDOW (gtk_widget_get_ancestor (priv->widget, GTK_TYPE_WINDOW)); - - priv->hierarchy_changed_id = - g_signal_connect (priv->widget, "notify::root", - G_CALLBACK (_gtk_popover_parent_hierarchy_changed), - popover); - priv->size_allocate_id = - g_signal_connect (priv->widget, "size-allocate", - G_CALLBACK (gtk_popover_parent_size_allocate), - popover); - priv->unmap_id = - g_signal_connect (priv->widget, "unmap", - G_CALLBACK (_gtk_popover_parent_unmap), - popover); - priv->state_changed_id = - g_signal_connect (priv->widget, "state-flags-changed", - G_CALLBACK (_gtk_popover_parent_state_changed), - popover); - priv->grab_notify_id = - g_signal_connect (priv->widget, "grab-notify", - G_CALLBACK (_gtk_popover_parent_grab_notify), - popover); - - /* Give ownership of the popover to widget */ - widget_manage_popover (priv->widget, popover); - } - - if (priv->window) - _gtk_window_add_popover (priv->window, GTK_WIDGET (popover), priv->widget, TRUE); - - if (priv->widget) - gtk_popover_update_scrollable (popover); - - _gtk_widget_update_parent_muxer (GTK_WIDGET (popover)); - g_object_unref (popover); -} - -static void -gtk_popover_update_pointing_to (GtkPopover *popover, - const GdkRectangle *pointing_to) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (pointing_to) - { - priv->pointing_to = *pointing_to; - priv->has_pointing_to = TRUE; - } - else - priv->has_pointing_to = FALSE; - - g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_POINTING_TO]); -} - -static void -gtk_popover_update_preferred_position (GtkPopover *popover, - GtkPositionType position) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (priv->preferred_position == position) - return; - - priv->preferred_position = position; - g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_POSITION]); -} - -/** - * gtk_popover_new: - * @relative_to: (allow-none): #GtkWidget the popover is related to - * - * Creates a new popover to point to @relative_to - * - * Returns: a new #GtkPopover - **/ GtkWidget * gtk_popover_new (GtkWidget *relative_to) { - g_return_val_if_fail (relative_to == NULL || GTK_IS_WIDGET (relative_to), NULL); + return GTK_WIDGET (g_object_new (GTK_TYPE_POPOVER, + "relative-to", relative_to, + NULL)); +} - return g_object_new (GTK_TYPE_POPOVER, - "relative-to", relative_to, - NULL); +static void +size_changed (GtkWidget *widget, + int width, + int height, + int baseline, + GtkPopover *popover) +{ + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + if (priv->surface) + gtk_popover_move_resize (popover); +} + +GListModel * +gtk_popover_get_popovers (void) +{ + if (popover_list == NULL) + popover_list = g_list_store_new (GTK_TYPE_WIDGET); + + return G_LIST_MODEL (popover_list); +} + +void +gtk_popover_set_default_widget (GtkPopover *popover, + GtkWidget *widget) +{ + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + + g_return_if_fail (GTK_IS_POPOVER (popover)); + + if (priv->default_widget == widget) + return; + + if (priv->default_widget) + { + _gtk_widget_set_has_default (priv->default_widget, FALSE); + gtk_widget_queue_draw (priv->default_widget); + g_object_notify (G_OBJECT (priv->default_widget), "has-default"); + } + + g_set_object (&priv->default_widget, widget); + + if (priv->default_widget) + { + _gtk_widget_set_has_default (priv->default_widget, TRUE); + gtk_widget_queue_draw (priv->default_widget); + g_object_notify (G_OBJECT (priv->default_widget), "has-default"); + } + + g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_DEFAULT_WIDGET]); +} + +static void +gtk_popover_native_interface_init (GtkNativeInterface *iface) +{ + iface->get_surface = gtk_popover_native_get_surface; + iface->get_renderer = gtk_popover_native_get_renderer; + iface->get_surface_transform = gtk_popover_native_get_surface_transform; + iface->check_resize = gtk_popover_native_check_resize; } /** @@ -2117,13 +970,31 @@ void gtk_popover_set_relative_to (GtkPopover *popover, GtkWidget *relative_to) { + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + g_return_if_fail (GTK_IS_POPOVER (popover)); - g_return_if_fail (relative_to == NULL || GTK_IS_WIDGET (relative_to)); - gtk_popover_update_relative_to (popover, relative_to); + g_object_ref (popover); - if (relative_to) - gtk_popover_update_position (popover); + if (priv->relative_to) + { + g_signal_handlers_disconnect_by_func (priv->relative_to, size_changed, popover); + gtk_widget_unparent (GTK_WIDGET (popover)); + } + + priv->relative_to = relative_to; + + if (priv->relative_to) + { + g_signal_connect (priv->relative_to, "size-allocate", G_CALLBACK (size_changed), popover); + gtk_css_node_set_parent (gtk_widget_get_css_node (GTK_WIDGET (popover)), + gtk_widget_get_css_node (relative_to)); + gtk_widget_set_parent (GTK_WIDGET (popover), relative_to); + } + + g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_RELATIVE_TO]); + + g_object_unref (popover); } /** @@ -2141,7 +1012,7 @@ gtk_popover_get_relative_to (GtkPopover *popover) g_return_val_if_fail (GTK_IS_POPOVER (popover), NULL); - return priv->widget; + return priv->relative_to; } /** @@ -2157,11 +1028,19 @@ void gtk_popover_set_pointing_to (GtkPopover *popover, const GdkRectangle *rect) { - g_return_if_fail (GTK_IS_POPOVER (popover)); - g_return_if_fail (rect != NULL); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - gtk_popover_update_pointing_to (popover, rect); - gtk_popover_update_position (popover); + g_return_if_fail (GTK_IS_POPOVER (popover)); + + if (rect) + { + priv->pointing_to = *rect; + priv->has_pointing_to = TRUE; + } + else + priv->has_pointing_to = FALSE; + + g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_POINTING_TO]); } /** @@ -2187,18 +1066,6 @@ gtk_popover_get_pointing_to (GtkPopover *popover, if (priv->has_pointing_to) *rect = priv->pointing_to; - else if (priv->widget) - { - graphene_rect_t r; - - if (!gtk_widget_compute_bounds (priv->widget, priv->widget, &r)) - return FALSE; - - rect->x = floorf (r.origin.x); - rect->y = floorf (r.origin.y); - rect->width = ceilf (r.size.width); - rect->height = ceilf (r.size.height); - } return priv->has_pointing_to; } @@ -2219,11 +1086,16 @@ void gtk_popover_set_position (GtkPopover *popover, GtkPositionType position) { - g_return_if_fail (GTK_IS_POPOVER (popover)); - g_return_if_fail (position >= GTK_POS_LEFT && position <= GTK_POS_BOTTOM); + GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - gtk_popover_update_preferred_position (popover, position); - gtk_popover_update_position (popover); + g_return_if_fail (GTK_IS_POPOVER (popover)); + + if (priv->position == position) + return; + + priv->position = position; + + g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_POSITION]); } /** @@ -2241,7 +1113,7 @@ gtk_popover_get_position (GtkPopover *popover) g_return_val_if_fail (GTK_IS_POPOVER (popover), GTK_POS_TOP); - return priv->preferred_position; + return priv->position; } /** @@ -2269,9 +1141,6 @@ gtk_popover_set_modal (GtkPopover *popover, priv->modal = modal; - if (gtk_widget_is_visible (GTK_WIDGET (popover))) - gtk_popover_apply_modality (popover, priv->modal); - g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_MODAL]); } @@ -2294,6 +1163,38 @@ gtk_popover_get_modal (GtkPopover *popover) return priv->modal; } +/** + * gtk_popover_popup: + * @popover: a #GtkPopover + * + * Pops @popover up. This is different than a gtk_widget_show() call + * in that it shows the popover with a transition. If you want to show + * the popover without a transition, use gtk_widget_show(). + */ +void +gtk_popover_popup (GtkPopover *popover) +{ + g_return_if_fail (GTK_IS_POPOVER (popover)); + + gtk_widget_show (GTK_WIDGET (popover)); +} + +/** + * gtk_popover_popdown: + * @popover: a #GtkPopover + * + * Pops @popover down.This is different than a gtk_widget_hide() call + * in that it shows the popover with a transition. If you want to hide + * the popover without a transition, use gtk_widget_hide(). + */ +void +gtk_popover_popdown (GtkPopover *popover) +{ + g_return_if_fail (GTK_IS_POPOVER (popover)); + + gtk_widget_hide (GTK_WIDGET (popover)); +} + static void back_to_main (GtkWidget *popover) { @@ -2340,7 +1241,6 @@ gtk_popover_bind_model (GtkPopover *popover, GMenuModel *model, const gchar *action_namespace) { - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); GtkWidget *child; GtkWidget *stack; GtkStyleContext *style_context; @@ -2352,7 +1252,7 @@ gtk_popover_bind_model (GtkPopover *popover, if (child) gtk_widget_destroy (child); - style_context = gtk_widget_get_style_context (priv->contents_widget); + style_context = gtk_widget_get_style_context (GTK_WIDGET (popover)); if (model) { @@ -2412,156 +1312,11 @@ gtk_popover_new_from_model (GtkWidget *relative_to, return popover; } -/** - * gtk_popover_set_default_widget: - * @popover: a #GtkPopover - * @widget: (allow-none): the new default widget, or %NULL - * - * Sets the widget that should be set as default widget while - * the popover is shown (see gtk_window_set_default()). #GtkPopover - * remembers the previous default widget and reestablishes it - * when the popover is dismissed. - */ -void -gtk_popover_set_default_widget (GtkPopover *popover, - GtkWidget *widget) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - g_return_if_fail (GTK_IS_POPOVER (popover)); - - if (priv->default_widget == widget) - return; - - if (priv->default_widget) - g_object_unref (priv->default_widget); - - priv->default_widget = widget; - - if (priv->default_widget) - g_object_ref (priv->default_widget); - - if (gtk_widget_get_mapped (GTK_WIDGET (popover))) - gtk_window_set_default_widget (priv->window, priv->default_widget); - - g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_DEFAULT_WIDGET]); -} - -/** - * gtk_popover_get_default_widget: - * @popover: a #GtkPopover - * - * Gets the widget that should be set as the default while - * the popover is shown. - * - * Returns: (nullable) (transfer none): the default widget, - * or %NULL if there is none - */ GtkWidget * -gtk_popover_get_default_widget (GtkPopover *popover) +gtk_popover_get_contents_widget (GtkPopover *popover) { GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - g_return_val_if_fail (GTK_IS_POPOVER (popover), NULL); - - return priv->default_widget; -} - -/** - * gtk_popover_set_constrain_to: - * @popover: a #GtkPopover - * @constraint: the new constraint - * - * Sets a constraint for positioning this popover. - * - * Note that not all platforms support placing popovers freely, - * and may already impose constraints. - */ -void -gtk_popover_set_constrain_to (GtkPopover *popover, - GtkPopoverConstraint constraint) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - g_return_if_fail (GTK_IS_POPOVER (popover)); - - if (priv->constraint == constraint) - return; - - priv->constraint = constraint; - gtk_popover_update_position (popover); - - g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_CONSTRAIN_TO]); -} - -/** - * gtk_popover_get_constrain_to: - * @popover: a #GtkPopover - * - * Returns the constraint for placing this popover. - * See gtk_popover_set_constrain_to(). - * - * Returns: the constraint for placing this popover. - */ -GtkPopoverConstraint -gtk_popover_get_constrain_to (GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - g_return_val_if_fail (GTK_IS_POPOVER (popover), GTK_POPOVER_CONSTRAINT_WINDOW); - - return priv->constraint; -} - -/** - * gtk_popover_popup: - * @popover: a #GtkPopover - * - * Pops @popover up. This is different than a gtk_widget_show() call - * in that it shows the popover with a transition. If you want to show - * the popover without a transition, use gtk_widget_show(). - */ -void -gtk_popover_popup (GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - g_return_if_fail (GTK_IS_POPOVER (popover)); - - if (priv->state == STATE_SHOWING || - priv->state == STATE_SHOWN) - return; - - gtk_widget_show (GTK_WIDGET (popover)); - - if (transitions_enabled (popover)) - gtk_popover_set_state (popover, STATE_SHOWING); -} - -/** - * gtk_popover_popdown: - * @popover: a #GtkPopover - * - * Pops @popover down.This is different than a gtk_widget_hide() call - * in that it shows the popover with a transition. If you want to hide - * the popover without a transition, use gtk_widget_hide(). - */ -void -gtk_popover_popdown (GtkPopover *popover) -{ - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - g_return_if_fail (GTK_IS_POPOVER (popover)); - - if (priv->state == STATE_HIDING || - priv->state == STATE_HIDDEN) - return; - - - if (!transitions_enabled (popover)) - gtk_widget_hide (GTK_WIDGET (popover)); - else - gtk_popover_set_state (popover, STATE_HIDING); - - gtk_popover_hide_internal (popover); + return priv->contents_widget; } diff --git a/gtk/gtkpopover.h b/gtk/gtkpopover.h index 619cf9247a..c4905f3b5d 100644 --- a/gtk/gtkpopover.h +++ b/gtk/gtkpopover.h @@ -1,5 +1,8 @@ /* GTK - The GIMP Toolkit - * Copyright © 2013 Carlos Garnacho + * Copyright (C) 2019 Red Hat, Inc. + * + * Authors: + * - Matthias Clasen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,23 +25,23 @@ #error "Only can be included directly." #endif -#include +#include G_BEGIN_DECLS -#define GTK_TYPE_POPOVER (gtk_popover_get_type ()) -#define GTK_POPOVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_POPOVER, GtkPopover)) -#define GTK_POPOVER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_POPOVER, GtkPopoverClass)) -#define GTK_IS_POPOVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_POPOVER)) -#define GTK_IS_POPOVER_CLASS(o) (G_TYPE_CHECK_CLASS_TYPE ((o), GTK_TYPE_POPOVER)) -#define GTK_POPOVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_POPOVER, GtkPopoverClass)) +#define GTK_TYPE_POPOVER (gtk_popover_get_type ()) +#define GTK_POPOVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_POPOVER, GtkPopover)) +#define GTK_POPOVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_POPOVER, GtkPopoverClass)) +#define GTK_IS_POPOVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_POPOVER)) +#define GTK_IS_POPOVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_POPOVER)) +#define GTK_POPOVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_POPOVER, GtkPopoverClass)) -typedef struct _GtkPopover GtkPopover; -typedef struct _GtkPopoverClass GtkPopoverClass; +typedef struct _GtkPopover GtkPopover; +typedef struct _GtkPopoverClass GtkPopoverClass; struct _GtkPopover { - GtkBin parent_instance; + GtkBin parent; }; struct _GtkPopoverClass @@ -53,63 +56,55 @@ struct _GtkPopoverClass }; GDK_AVAILABLE_IN_ALL -GType gtk_popover_get_type (void) G_GNUC_CONST; +GType gtk_popover_get_type (void) G_GNUC_CONST; GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_popover_new (GtkWidget *relative_to); +GtkWidget * gtk_popover_new (GtkWidget *relative_to); GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_popover_new_from_model (GtkWidget *relative_to, - GMenuModel *model); +GtkWidget * gtk_popover_new_from_model (GtkWidget *relative_to, + GMenuModel *model); GDK_AVAILABLE_IN_ALL -void gtk_popover_set_relative_to (GtkPopover *popover, - GtkWidget *relative_to); -GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_popover_get_relative_to (GtkPopover *popover); +void gtk_popover_bind_model (GtkPopover *popover, + GMenuModel *model, + const gchar *action_namespace); GDK_AVAILABLE_IN_ALL -void gtk_popover_set_pointing_to (GtkPopover *popover, - const GdkRectangle *rect); +void gtk_popover_set_relative_to (GtkPopover *popover, + GtkWidget *relative_to); GDK_AVAILABLE_IN_ALL -gboolean gtk_popover_get_pointing_to (GtkPopover *popover, - GdkRectangle *rect); -GDK_AVAILABLE_IN_ALL -void gtk_popover_set_position (GtkPopover *popover, - GtkPositionType position); -GDK_AVAILABLE_IN_ALL -GtkPositionType gtk_popover_get_position (GtkPopover *popover); +GtkWidget * gtk_popover_get_relative_to (GtkPopover *popover); GDK_AVAILABLE_IN_ALL -void gtk_popover_set_modal (GtkPopover *popover, - gboolean modal); +void gtk_popover_set_pointing_to (GtkPopover *popover, + const GdkRectangle *rect); GDK_AVAILABLE_IN_ALL -gboolean gtk_popover_get_modal (GtkPopover *popover); +gboolean gtk_popover_get_pointing_to (GtkPopover *popover, + GdkRectangle *rect); +GDK_AVAILABLE_IN_ALL +void gtk_popover_set_position (GtkPopover *popover, + GtkPositionType position); +GDK_AVAILABLE_IN_ALL +GtkPositionType gtk_popover_get_position (GtkPopover *popover); GDK_AVAILABLE_IN_ALL -void gtk_popover_bind_model (GtkPopover *popover, - GMenuModel *model, - const gchar *action_namespace); +void gtk_popover_set_modal (GtkPopover *popover, + gboolean modal); +GDK_AVAILABLE_IN_ALL +gboolean gtk_popover_get_modal (GtkPopover *popover); GDK_AVAILABLE_IN_ALL -void gtk_popover_set_default_widget (GtkPopover *popover, - GtkWidget *widget); +void gtk_popover_popup (GtkPopover *popover); GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_popover_get_default_widget (GtkPopover *popover); +void gtk_popover_popdown (GtkPopover *popover); GDK_AVAILABLE_IN_ALL -void gtk_popover_set_constrain_to (GtkPopover *popover, - GtkPopoverConstraint constraint); +void gtk_popover_set_default_widget (GtkPopover *popover, + GtkWidget *widget); GDK_AVAILABLE_IN_ALL -GtkPopoverConstraint gtk_popover_get_constrain_to (GtkPopover *popover); - -GDK_AVAILABLE_IN_ALL -void gtk_popover_popup (GtkPopover *popover); - -GDK_AVAILABLE_IN_ALL -void gtk_popover_popdown (GtkPopover *popover); - +GListModel * gtk_popover_get_popovers (void); G_END_DECLS diff --git a/gtk/gtkpopoverprivate.h b/gtk/gtkpopoverprivate.h index fe24a5d775..db5fa6e57a 100644 --- a/gtk/gtkpopoverprivate.h +++ b/gtk/gtkpopoverprivate.h @@ -22,8 +22,6 @@ G_BEGIN_DECLS -void gtk_popover_update_position (GtkPopover *popover); - GtkWidget *gtk_popover_get_contents_widget (GtkPopover *popover); G_END_DECLS diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 0f21d1d360..d34a527e0d 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -7343,10 +7343,12 @@ gtk_widget_get_scale_factor (GtkWidget *widget) * just returning 1: */ display = _gtk_widget_get_display (widget); - monitor = gdk_display_get_monitor (display, 0); - - if (monitor) - return gdk_monitor_get_scale_factor (monitor); + if (display) + { + monitor = gdk_display_get_monitor (display, 0); + if (monitor) + return gdk_monitor_get_scale_factor (monitor); + } return 1; } diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index f96b516fdb..1d7f7c141e 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -5895,8 +5895,10 @@ popover_size_allocate (GtkWindowPopover *popover, if (!gtk_widget_get_mapped (popover->widget)) return; +#if 0 if (GTK_IS_POPOVER (popover->widget)) gtk_popover_update_position (GTK_POPOVER (popover->widget)); +#endif popover_get_rect (popover, window, &rect); gtk_widget_size_allocate (popover->widget, &rect, -1); diff --git a/gtk/inspector/object-tree.c b/gtk/inspector/object-tree.c index f2caf34708..f977096d69 100644 --- a/gtk/inspector/object-tree.c +++ b/gtk/inspector/object-tree.c @@ -41,6 +41,7 @@ #include "gtklabel.h" #include "gtklistbox.h" #include "gtkmenuitem.h" +#include "gtkpopover.h" #include "gtksettings.h" #include "gtksizegroup.h" #include "gtktextview.h" @@ -1144,6 +1145,7 @@ create_root_model (void) g_object_unref); gtk_filter_list_model_set_model (filter, gtk_window_get_toplevels ()); g_list_store_append (list, filter); + g_list_store_append (list, gtk_popover_get_popovers ()); g_object_unref (filter); flatten = gtk_flatten_list_model_new (G_TYPE_OBJECT, G_LIST_MODEL (list)); diff --git a/tests/meson.build b/tests/meson.build index f70401b2d4..677fbb74e6 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -114,7 +114,6 @@ gtk_tests = [ ['testpopover'], ['gdkgears', ['gtkgears.c']], ['listmodel'], - ['testpopup'], ['testgaction'], ['testwidgetfocus'], ['testwidgettransforms'], diff --git a/tests/testpopup.c b/tests/testpopup.c deleted file mode 100644 index 00a2ca78bf..0000000000 --- a/tests/testpopup.c +++ /dev/null @@ -1,62 +0,0 @@ -#include - -static void -draw_popup (GtkDrawingArea *da, - cairo_t *cr, - int width, - int height, - gpointer data) -{ - cairo_set_source_rgb (cr, 1, 0, 0); - cairo_paint (cr); -} - -static void -place_popup (GtkEventControllerMotion *motion, - gdouble x, - gdouble y, - GtkWidget *popup) -{ -} - -static gboolean -on_map (GtkWidget *parent) -{ - GtkWidget *popup, *da; - GtkEventController *motion; - - popup = gtk_window_new (GTK_WINDOW_POPUP); - da = gtk_drawing_area_new (); - gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_popup, NULL, NULL); - gtk_container_add (GTK_CONTAINER (popup), da); - - gtk_widget_set_size_request (GTK_WIDGET (popup), 20, 20); - gtk_window_set_transient_for (GTK_WINDOW (popup), GTK_WINDOW (parent)); - - motion = gtk_event_controller_motion_new (); - gtk_widget_add_controller (parent, motion); - g_signal_connect (motion, "motion", G_CALLBACK (place_popup), popup); - - gtk_widget_show (popup); - - return FALSE; -} - -int -main (int argc, char *argv[]) -{ - GtkWidget *window; - - gtk_init (); - - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - - g_signal_connect (window, "destroy", gtk_main_quit, NULL); - g_signal_connect (window, "map", G_CALLBACK (on_map), NULL); - - gtk_widget_show (window); - - gtk_main (); - - return 0; -}