popover: fix focus when inside an unfocused window

Fix popovers to properly gain focus when clicked
inside an unfocused window.

We use the GTK_PHASE_CAPTURE of the 'pressed' event
to early detect that the popover is being clicked
inside an inactive window, this allow us to present
the window (and be focused) before the normal signal
handlers for the popover click/pressed events are run
which would ultimately give focus to popover widget.

This fix works for both modal and 'non modal' popovers
when being clicked inside unfocused windows.

Fixes issue #1871
This commit is contained in:
Nelson Benítez León 2019-08-30 21:14:17 -04:00
parent c2bfcca0e9
commit 58f57aeb3b

View File

@ -159,6 +159,7 @@ struct _GtkPopoverPrivate
GdkRectangle pointing_to;
GtkPopoverConstraint constraint;
GtkProgressTracker tracker;
GtkGesture *multipress_gesture;
guint prev_focus_unmap_id;
guint hierarchy_changed_id;
guint size_allocate_id;
@ -199,6 +200,12 @@ static void gtk_popover_apply_modality (GtkPopover *popover,
static void gtk_popover_set_scrollable_full (GtkPopover *popover,
GtkScrollable *scrollable);
static void gtk_popover_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
gint n_press,
gdouble widget_x,
gdouble widget_y,
GtkPopover *popover);
G_DEFINE_TYPE_WITH_PRIVATE (GtkPopover, gtk_popover, GTK_TYPE_BIN)
static void
@ -206,17 +213,26 @@ gtk_popover_init (GtkPopover *popover)
{
GtkWidget *widget;
GtkStyleContext *context;
GtkPopoverPrivate *priv;
widget = GTK_WIDGET (popover);
gtk_widget_set_has_window (widget, TRUE);
popover->priv = gtk_popover_get_instance_private (popover);
popover->priv->modal = TRUE;
popover->priv->tick_id = 0;
popover->priv->state = STATE_HIDDEN;
popover->priv->visible = FALSE;
popover->priv->transitions_enabled = TRUE;
popover->priv->preferred_position = GTK_POS_TOP;
popover->priv->constraint = GTK_POPOVER_CONSTRAINT_WINDOW;
priv = popover->priv = gtk_popover_get_instance_private (popover);
priv->modal = TRUE;
priv->tick_id = 0;
priv->state = STATE_HIDDEN;
priv->visible = FALSE;
priv->transitions_enabled = TRUE;
priv->preferred_position = GTK_POS_TOP;
priv->constraint = GTK_POPOVER_CONSTRAINT_WINDOW;
priv->multipress_gesture = gtk_gesture_multi_press_new (widget);
g_signal_connect (priv->multipress_gesture, "pressed",
G_CALLBACK (gtk_popover_multipress_gesture_pressed), popover);
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->multipress_gesture), 0);
gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (priv->multipress_gesture), TRUE);
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->multipress_gesture),
GTK_PHASE_CAPTURE);
context = gtk_widget_get_style_context (GTK_WIDGET (popover));
gtk_style_context_add_class (context, GTK_STYLE_CLASS_BACKGROUND);
@ -336,6 +352,8 @@ gtk_popover_finalize (GObject *object)
if (priv->widget)
gtk_popover_update_relative_to (popover, NULL);
g_clear_object (&priv->multipress_gesture);
G_OBJECT_CLASS (gtk_popover_parent_class)->finalize (object);
}
@ -2178,6 +2196,19 @@ gtk_popover_update_preferred_position (GtkPopover *popover,
g_object_notify_by_pspec (G_OBJECT (popover), properties[PROP_POSITION]);
}
static void
gtk_popover_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
gint n_press,
gdouble widget_x,
gdouble widget_y,
GtkPopover *popover)
{
GtkPopoverPrivate *priv = popover->priv;
if (!gtk_window_is_active (priv->window) && gtk_widget_is_drawable (GTK_WIDGET (popover)))
gtk_window_present_with_time (priv->window, gtk_get_current_event_time ());
}
/**
* gtk_popover_new:
* @relative_to: (allow-none): #GtkWidget the popover is related to