From 92f1bdcf45dbb87ad9a537879bd5c5eae59964e7 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 00:54:56 -0500 Subject: [PATCH] Redo focus handling GtkWindow has a focus_widget that points to the current input focus. GtkWidget has a focus_child that points to the child that contains the input focus. Following the focus_child chain from the toplevel always leads to the focus_widget. We never unset focus_child, we only set it. We bubble focus change events. --- gtk/gtkwidget.c | 109 ++-------------------------------- gtk/gtkwindow.c | 155 +++++++++++++----------------------------------- 2 files changed, 46 insertions(+), 218 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index cb37b01ff2..fb085f2f80 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -5340,88 +5340,16 @@ gtk_widget_grab_focus (GtkWidget *widget) if (!gtk_widget_is_sensitive (widget)) return; - g_object_ref (widget); GTK_WIDGET_GET_CLASS (widget)->grab_focus (widget); - g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_HAS_FOCUS]); - g_object_unref (widget); -} - -static void -reset_focus_recurse (GtkWidget *widget, - gpointer data) -{ - gtk_widget_set_focus_child (widget, NULL); - - gtk_widget_forall (widget, - reset_focus_recurse, - NULL); } static void gtk_widget_real_grab_focus (GtkWidget *focus_widget) { GtkWidget *toplevel; - GtkWidget *widget; - /* clear the current focus setting, break if the current widget - * is the focus widget's parent, since containers above that will - * be set by the next loop. - */ - toplevel = _gtk_widget_get_toplevel (focus_widget); - if (_gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel)) - { - widget = gtk_window_get_focus (GTK_WINDOW (toplevel)); - - if (widget == focus_widget) - { - /* We call _gtk_window_internal_set_focus() here so that the - * toplevel window can request the focus if necessary. - * This is needed when the toplevel is a GtkPlug - */ - if (!gtk_widget_has_focus (widget)) - _gtk_window_internal_set_focus (GTK_WINDOW (toplevel), focus_widget); - - return; - } - - if (widget) - { - GtkWidget *common_ancestor = gtk_widget_common_ancestor (widget, focus_widget); - - if (widget != common_ancestor) - { - while (widget->priv->parent) - { - widget = widget->priv->parent; - gtk_widget_set_focus_child (widget, NULL); - if (widget == common_ancestor) - break; - } - } - } - } - else if (toplevel != focus_widget) - { - /* gtk_widget_grab_focus() operates on a tree without window... - * actually, this is very questionable behavior. - */ - - gtk_widget_forall (toplevel, - reset_focus_recurse, - NULL); - } - - /* now propagate the new focus up the widget tree and finally - * set it on the window - */ - widget = focus_widget; - while (widget->priv->parent) - { - gtk_widget_set_focus_child (widget->priv->parent, widget); - widget = widget->priv->parent; - } - if (GTK_IS_WINDOW (widget)) - _gtk_window_internal_set_focus (GTK_WINDOW (widget), focus_widget); + toplevel = gtk_widget_get_toplevel (focus_widget); + gtk_window_set_focus (GTK_WINDOW (toplevel), focus_widget); } static gboolean @@ -5560,10 +5488,7 @@ gtk_widget_real_move_focus (GtkWidget *widget, GtkWidget *toplevel = _gtk_widget_get_toplevel (widget); if (widget != toplevel && GTK_IS_WINDOW (toplevel)) - { - g_signal_emit (toplevel, widget_signals[MOVE_FOCUS], 0, - direction); - } + g_signal_emit (toplevel, widget_signals[MOVE_FOCUS], 0, direction); } static gboolean @@ -11733,22 +11658,17 @@ gtk_widget_send_focus_change (GtkWidget *widget, GdkEvent *event) { GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); - gboolean res; g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); g_return_val_if_fail (event != NULL && event->any.type == GDK_FOCUS_CHANGE, FALSE); - g_object_ref (widget); - priv->has_focus = event->focus_change.in; - res = gtk_widget_event (widget, event); + gtk_propagate_event (widget, event); g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_HAS_FOCUS]); - g_object_unref (widget); - - return res; + return TRUE; } /** @@ -13470,26 +13390,7 @@ gtk_widget_set_focus_child (GtkWidget *widget, g_return_if_fail (gtk_widget_get_parent (child) == widget); } - if (priv->focus_child) - gtk_widget_unset_state_flags (priv->focus_child, - GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_FOCUS_VISIBLE); - - if (child) - { - GtkWidget *toplevel; - GtkStateFlags flags = GTK_STATE_FLAG_FOCUSED; - - toplevel = _gtk_widget_get_toplevel (widget); - if (!GTK_IS_WINDOW (toplevel) || gtk_window_get_focus_visible (GTK_WINDOW (toplevel))) - flags |= GTK_STATE_FLAG_FOCUS_VISIBLE; - - gtk_widget_set_state_flags (child, flags, FALSE); - } - g_set_object (&priv->focus_child, child); - - if (GTK_IS_CONTAINER (widget)) - gtk_container_set_focus_child (GTK_CONTAINER (widget), child); } /** diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 600e96e515..10e80d195d 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -2787,52 +2787,9 @@ void gtk_window_set_focus (GtkWindow *window, GtkWidget *focus) { - GtkWindowPrivate *priv = gtk_window_get_instance_private (window); - GtkWidget *parent; - g_return_if_fail (GTK_IS_WINDOW (window)); - if (focus) - { - g_return_if_fail (GTK_IS_WIDGET (focus)); - g_return_if_fail (gtk_widget_get_can_focus (focus)); - - if (!gtk_widget_get_visible (GTK_WIDGET (window))) - priv->initial_focus = focus; - else - gtk_widget_grab_focus (focus); - } - else - { - /* Clear the existing focus chain, so that when we focus into - * the window again, we start at the beginnning. - */ - GtkWidget *widget = priv->focus_widget; - if (widget) - { - while ((parent = _gtk_widget_get_parent (widget))) - { - widget = parent; - gtk_widget_set_focus_child (widget, NULL); - } - } - - _gtk_window_internal_set_focus (window, NULL); - } -} - -void -_gtk_window_internal_set_focus (GtkWindow *window, - GtkWidget *focus) -{ - GtkWindowPrivate *priv = gtk_window_get_instance_private (window); - - g_return_if_fail (GTK_IS_WINDOW (window)); - - priv->initial_focus = NULL; - if ((priv->focus_widget != focus) || - (focus && !gtk_widget_has_focus (focus))) - g_signal_emit (window, window_signals[SET_FOCUS], 0, focus); + g_signal_emit (window, window_signals[SET_FOCUS], 0, focus); } /** @@ -7325,80 +7282,50 @@ gtk_window_move_focus (GtkWidget *widget, gtk_window_set_focus (GTK_WINDOW (widget), NULL); } +static void +unset_focus_widget (GtkWindow *window) +{ + GtkWindowPrivate *priv = gtk_window_get_instance_private (window); + GtkWidget *f; + + for (f = priv->focus_widget; f; f = gtk_widget_get_parent (f)) + gtk_widget_unset_state_flags (f, GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_FOCUS_VISIBLE); + + if (priv->focus_widget) + do_focus_change (priv->focus_widget, FALSE); + g_set_object (&priv->focus_widget, NULL); +} + +static void +set_focus_widget (GtkWindow *window, + GtkWidget *focus) +{ + GtkWindowPrivate *priv = gtk_window_get_instance_private (window); + GtkWidget *f; + GtkStateFlags flags = GTK_STATE_FLAG_FOCUSED; + + if (gtk_window_get_focus_visible (window)) + flags |= GTK_STATE_FLAG_FOCUS_VISIBLE; + + for (f = focus; f; f = gtk_widget_get_parent (f)) + { + GtkWidget *parent = gtk_widget_get_parent (f); + gtk_widget_set_state_flags (f, flags, FALSE); + if (parent) + gtk_widget_set_focus_child (parent, f); + } + + g_set_object (&priv->focus_widget, focus); + if (priv->focus_widget) + do_focus_change (priv->focus_widget, TRUE); +} + static void gtk_window_real_set_focus (GtkWindow *window, GtkWidget *focus) { - GtkWindowPrivate *priv = gtk_window_get_instance_private (window); - GtkWidget *old_focus = priv->focus_widget; - - if (old_focus) - { - g_object_ref (old_focus); - g_object_freeze_notify (G_OBJECT (old_focus)); - } - if (focus) - { - g_object_ref (focus); - g_object_freeze_notify (G_OBJECT (focus)); - } - - if (priv->focus_widget) - { - if (gtk_widget_get_receives_default (priv->focus_widget) && - (priv->focus_widget != priv->default_widget)) - { - _gtk_widget_set_has_default (priv->focus_widget, FALSE); - - if (priv->default_widget) - _gtk_widget_set_has_default (priv->default_widget, TRUE); - } - - priv->focus_widget = NULL; - - if (priv->is_active) - do_focus_change (old_focus, FALSE); - - g_object_notify (G_OBJECT (old_focus), "is-focus"); - } - - /* The above notifications may have set a new focus widget, - * if so, we don't want to override it. - */ - if (focus && !priv->focus_widget) - { - priv->focus_widget = focus; - - if (gtk_widget_get_receives_default (priv->focus_widget) && - (priv->focus_widget != priv->default_widget)) - { - if (gtk_widget_get_can_default (priv->focus_widget)) - _gtk_widget_set_has_default (priv->focus_widget, TRUE); - - if (priv->default_widget) - _gtk_widget_set_has_default (priv->default_widget, FALSE); - } - - if (priv->is_active) - do_focus_change (priv->focus_widget, TRUE); - - /* It's possible for do_focus_change() above to have callbacks - * that clear priv->focus_widget here. - */ - if (priv->focus_widget) - g_object_notify (G_OBJECT (priv->focus_widget), "is-focus"); - } - - if (old_focus) - { - g_object_thaw_notify (G_OBJECT (old_focus)); - g_object_unref (old_focus); - } - if (focus) - { - g_object_thaw_notify (G_OBJECT (focus)); - g_object_unref (focus); - } + unset_focus_widget (window); + set_focus_widget (window, focus); } static void