Fix unsetting focus

Make _gtk_window_unset_focus_and_default queue the changes
for after the next draw. This achieves two things: first,
it avoids invalidating css at the wrong time (e.g. when
setting child-visible during size-allocation), and second,
it defers the focus change until after the widget is
hidden, so that moving the focus has the desired effect
of picking a different, visible widget.

Fixes: #3623
This commit is contained in:
Matthias Clasen 2021-01-29 21:41:16 -05:00
parent 456a2f3bcf
commit 3dbf5038fa

View File

@ -225,6 +225,9 @@ typedef struct
guint hide_on_close : 1; guint hide_on_close : 1;
guint in_emit_close_request : 1; guint in_emit_close_request : 1;
guint move_focus : 1;
guint unset_default : 1;
GtkGesture *click_gesture; GtkGesture *click_gesture;
GtkEventController *application_shortcut_controller; GtkEventController *application_shortcut_controller;
@ -4547,12 +4550,33 @@ surface_size_changed (GtkWidget *widget,
gtk_widget_queue_allocate (widget); gtk_widget_queue_allocate (widget);
} }
static void
maybe_unset_focus_and_default (GtkWindow *window)
{
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
if (priv->move_focus)
{
gtk_widget_child_focus (GTK_WIDGET (window), GTK_DIR_TAB_FORWARD);
priv->move_focus = FALSE;
}
if (priv->unset_default)
{
gtk_window_set_default_widget (window, NULL);
priv->unset_default = FALSE;
}
}
static gboolean static gboolean
surface_render (GdkSurface *surface, surface_render (GdkSurface *surface,
cairo_region_t *region, cairo_region_t *region,
GtkWidget *widget) GtkWidget *widget)
{ {
GtkWindow *window = GTK_WINDOW (widget);
gtk_widget_render (widget, surface, region); gtk_widget_render (widget, surface, region);
maybe_unset_focus_and_default (window);
return TRUE; return TRUE;
} }
@ -4991,39 +5015,14 @@ _gtk_window_unset_focus_and_default (GtkWindow *window,
{ {
GtkWindowPrivate *priv = gtk_window_get_instance_private (window); GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
GtkWidget *child; GtkWidget *child;
GtkWidget *parent;
GtkWidget *focus;
g_object_ref (window); child = priv->focus_widget;
g_object_ref (widget); if (child && (child == widget || gtk_widget_is_ancestor (child, widget)))
priv->move_focus = TRUE;
focus = priv->focus_widget;
if (focus && (focus == widget || gtk_widget_is_ancestor (focus, widget)))
{
parent = _gtk_widget_get_parent (widget);
while (parent)
{
if (_gtk_widget_get_visible (parent))
{
if (gtk_widget_grab_focus (parent))
break;
}
parent = gtk_widget_get_parent (parent);
}
}
child = priv->default_widget; child = priv->default_widget;
if (child && (child == widget || gtk_widget_is_ancestor (child, widget)))
while (child && child != widget) priv->unset_default = TRUE;
child = _gtk_widget_get_parent (child);
if (child == widget)
gtk_window_set_default_widget (window, NULL);
g_object_unref (widget);
g_object_unref (window);
} }
#undef INCLUDE_CSD_SIZE #undef INCLUDE_CSD_SIZE