From ac5040ef8a3d6c44660899652eaa5147327f70ce Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 00:54:56 -0500 Subject: [PATCH 01/70] 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 | 139 ++++++++++++------------------------------------ 2 files changed, 38 insertions(+), 210 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index cf7f1b1f489..c35a9d12fd9 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 600e96e515a..10e80d195db 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); } /** @@ -7326,79 +7283,49 @@ gtk_window_move_focus (GtkWidget *widget, } static void -gtk_window_real_set_focus (GtkWindow *window, - GtkWidget *focus) +unset_focus_widget (GtkWindow *window) { GtkWindowPrivate *priv = gtk_window_get_instance_private (window); - GtkWidget *old_focus = priv->focus_widget; + GtkWidget *f; - 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)); - } + 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) - { - 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); - } + do_focus_change (priv->focus_widget, FALSE); + g_set_object (&priv->focus_widget, NULL); +} - 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 (priv->is_active) - do_focus_change (old_focus, FALSE); + if (gtk_window_get_focus_visible (window)) + flags |= GTK_STATE_FLAG_FOCUS_VISIBLE; - 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) + for (f = focus; f; f = gtk_widget_get_parent (f)) { - 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"); + GtkWidget *parent = gtk_widget_get_parent (f); + gtk_widget_set_state_flags (f, flags, FALSE); + if (parent) + gtk_widget_set_focus_child (parent, f); } - 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); - } + 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) +{ + unset_focus_widget (window); + set_focus_widget (window, focus); } static void -- GitLab From 835ee90c0d48fbf376f434fd66714726d64dece9 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 01:34:43 -0500 Subject: [PATCH 02/70] notebook: Stop using set_focus_child We can instead use focus_child to find the last focus location inside a page. --- gtk/gtknotebook.c | 132 ++++++++++++---------------------------------- 1 file changed, 33 insertions(+), 99 deletions(-) diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index d11ee425848..7677682b822 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -211,7 +211,6 @@ struct _GtkNotebookPrivate guint32 timer; - guint child_has_focus : 1; guint click_child : 3; guint remove_in_detach : 1; guint focus_out : 1; /* Flag used by ::move-focus-out implementation */ @@ -303,7 +302,6 @@ struct _GtkNotebookPage GtkWidget *child; GtkWidget *tab_label; GtkWidget *menu_label; - GtkWidget *last_focus_child; /* Last descendant of the page that had focus */ GtkWidget *tab_widget; /* widget used for the tab itself */ @@ -678,8 +676,6 @@ static void gtk_notebook_add (GtkContainer *container, GtkWidget *widget); static void gtk_notebook_remove (GtkContainer *container, GtkWidget *widget); -static void gtk_notebook_set_focus_child (GtkContainer *container, - GtkWidget *child); static GType gtk_notebook_child_type (GtkContainer *container); static void gtk_notebook_forall (GtkContainer *container, GtkCallback callback, @@ -937,7 +933,6 @@ gtk_notebook_class_init (GtkNotebookClass *class) container_class->add = gtk_notebook_add; container_class->remove = gtk_notebook_remove; container_class->forall = gtk_notebook_forall; - container_class->set_focus_child = gtk_notebook_set_focus_child; container_class->child_type = gtk_notebook_child_type; class->switch_page = gtk_notebook_real_switch_page; @@ -1258,7 +1253,6 @@ gtk_notebook_init (GtkNotebook *notebook) priv->scrollable = FALSE; priv->click_child = ARROW_NONE; priv->need_timer = 0; - priv->child_has_focus = FALSE; priv->focus_out = FALSE; priv->group = 0; @@ -3215,13 +3209,7 @@ gtk_notebook_switch_tab_timeout (gpointer data) priv->switch_tab = NULL; if (switch_tab) - { - /* FIXME: hack, we don't want the - * focus to move fom the source widget - */ - priv->child_has_focus = FALSE; - gtk_notebook_switch_focus_tab (notebook, switch_tab); - } + gtk_notebook_switch_focus_tab (notebook, switch_tab); return FALSE; } @@ -3481,7 +3469,6 @@ gtk_notebook_drag_data_received (GtkWidget *widget, * gtk_notebook_add * gtk_notebook_remove * gtk_notebook_focus - * gtk_notebook_set_focus_child * gtk_notebook_child_type * gtk_notebook_forall */ @@ -3546,7 +3533,6 @@ focus_tabs_in (GtkNotebook *notebook) if (priv->show_tabs && gtk_notebook_has_current_page (notebook)) { gtk_widget_grab_focus (GTK_WIDGET (notebook)); - gtk_notebook_set_focus_child (GTK_CONTAINER (notebook), NULL); gtk_notebook_switch_focus_tab (notebook, g_list_find (priv->children, priv->cur_page)); @@ -3789,73 +3775,6 @@ gtk_notebook_focus (GtkWidget *widget, return FALSE; } -static void -gtk_notebook_set_focus_child (GtkContainer *container, - GtkWidget *child) -{ - GtkNotebook *notebook = GTK_NOTEBOOK (container); - GtkNotebookPrivate *priv = notebook->priv; - GtkWidget *page_child; - GtkWidget *toplevel; - - /* If the old focus widget was within a page of the notebook, - * (child may either be NULL or not in this case), record it - * for future use if we switch to the page with a mnemonic. - */ - - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container)); - if (toplevel && gtk_widget_is_toplevel (toplevel)) - { - page_child = gtk_window_get_focus (GTK_WINDOW (toplevel)); - while (page_child) - { - if (gtk_widget_get_parent (page_child) == GTK_WIDGET (container)) - { - GList *list = gtk_notebook_find_child (notebook, page_child); - if (list != NULL) - { - GtkNotebookPage *page = list->data; - - if (page->last_focus_child) - g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child); - - page->last_focus_child = gtk_window_get_focus (GTK_WINDOW (toplevel)); - g_object_add_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child); - - break; - } - } - - page_child = gtk_widget_get_parent (page_child); - } - } - - if (child) - { - g_return_if_fail (GTK_IS_WIDGET (child)); - - priv->child_has_focus = TRUE; - if (!priv->focus_tab) - { - GList *children; - GtkNotebookPage *page; - - children = priv->children; - while (children) - { - page = children->data; - if (page->child == child || page->tab_label == child) - gtk_notebook_switch_focus_tab (notebook, children); - children = children->next; - } - } - } - else - priv->child_has_focus = FALSE; - - GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->set_focus_child (container, child); -} - static void gtk_notebook_forall (GtkContainer *container, GtkCallback callback, @@ -4305,12 +4224,6 @@ gtk_notebook_real_remove (GtkNotebook *notebook, g_list_free (list); - if (page->last_focus_child) - { - g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child); - page->last_focus_child = NULL; - } - gtk_widget_unparent (page->tab_widget); g_object_unref (page); @@ -5405,6 +5318,25 @@ gtk_notebook_calc_tabs (GtkNotebook *notebook, } } +static GtkWidget * +find_last_focus (GtkWidget *widget) +{ + GtkWidget *f = widget; + + while (f) + { + GtkWidget *focus_child = gtk_widget_get_focus_child (f); + if (focus_child == NULL) + break; + f = focus_child; + } + + if (f != widget) + return f; + + return NULL; +} + /* Private GtkNotebook Page Switch Methods: * * gtk_notebook_real_switch_page @@ -5417,16 +5349,19 @@ gtk_notebook_real_switch_page (GtkNotebook *notebook, GtkNotebookPrivate *priv = notebook->priv; GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child)); GtkNotebookPage *page = GTK_NOTEBOOK_PAGE_FROM_LIST (list); - gboolean child_has_focus; + gboolean child_has_focus = FALSE; if (priv->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child))) return; - /* save the value here, changing visibility changes focus */ - child_has_focus = priv->child_has_focus; - if (priv->cur_page) - gtk_widget_unset_state_flags (priv->cur_page->tab_widget, GTK_STATE_FLAG_CHECKED); + { + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (notebook)); + GtkWidget *focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); + if (focus) + child_has_focus = gtk_widget_is_ancestor (focus, priv->cur_page->child); + gtk_widget_unset_state_flags (priv->cur_page->tab_widget, GTK_STATE_FLAG_CHECKED); + } priv->cur_page = page; gtk_widget_set_state_flags (page->tab_widget, GTK_STATE_FLAG_CHECKED, FALSE); @@ -5446,12 +5381,11 @@ gtk_notebook_real_switch_page (GtkNotebook *notebook, */ if (child_has_focus) { - if (priv->cur_page->last_focus_child && - gtk_widget_is_ancestor (priv->cur_page->last_focus_child, priv->cur_page->child)) - gtk_widget_grab_focus (priv->cur_page->last_focus_child); - else - if (!gtk_widget_child_focus (priv->cur_page->child, GTK_DIR_TAB_FORWARD)) - gtk_widget_grab_focus (GTK_WIDGET (notebook)); + GtkWidget *last_focus = find_last_focus (priv->cur_page->child); + if (last_focus) + gtk_widget_grab_focus (last_focus); + else if (!gtk_widget_child_focus (priv->cur_page->child, GTK_DIR_TAB_FORWARD)) + gtk_widget_grab_focus (GTK_WIDGET (notebook)); } update_arrow_state (notebook); -- GitLab From 9e859a196457d246b0155adf6177eee919b75562 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 01:47:24 -0500 Subject: [PATCH 03/70] paned: Stop using set_focus_child We can instead use focus_child to find the last focus location inside a child. --- gtk/gtkpaned.c | 130 +++++++++---------------------------------------- 1 file changed, 23 insertions(+), 107 deletions(-) diff --git a/gtk/gtkpaned.c b/gtk/gtkpaned.c index 9fc18c9667d..9250233c1f8 100644 --- a/gtk/gtkpaned.c +++ b/gtk/gtkpaned.c @@ -132,8 +132,6 @@ typedef struct GtkPaned *first_paned; GtkWidget *child1; GtkWidget *child2; - GtkWidget *last_child1_focus; - GtkWidget *last_child2_focus; GtkWidget *saved_focus; GtkOrientation orientation; @@ -231,16 +229,10 @@ static void gtk_paned_calc_position (GtkPaned *paned, gint allocation, gint child1_req, gint child2_req); -static void gtk_paned_set_focus_child (GtkContainer *container, - GtkWidget *child); static void gtk_paned_set_saved_focus (GtkPaned *paned, GtkWidget *widget); static void gtk_paned_set_first_paned (GtkPaned *paned, GtkPaned *first_paned); -static void gtk_paned_set_last_child1_focus (GtkPaned *paned, - GtkWidget *widget); -static void gtk_paned_set_last_child2_focus (GtkPaned *paned, - GtkWidget *widget); static gboolean gtk_paned_cycle_child_focus (GtkPaned *paned, gboolean reverse); static gboolean gtk_paned_cycle_handle_focus (GtkPaned *paned, @@ -372,7 +364,6 @@ gtk_paned_class_init (GtkPanedClass *class) container_class->remove = gtk_paned_remove; container_class->forall = gtk_paned_forall; container_class->child_type = gtk_paned_child_type; - container_class->set_focus_child = gtk_paned_set_focus_child; container_class->set_child_property = gtk_paned_set_child_property; container_class->get_child_property = gtk_paned_get_child_property; @@ -1353,8 +1344,6 @@ gtk_paned_unrealize (GtkWidget *widget) { GtkPaned *paned = GTK_PANED (widget); - gtk_paned_set_last_child1_focus (paned, NULL); - gtk_paned_set_last_child2_focus (paned, NULL); gtk_paned_set_saved_focus (paned, NULL); gtk_paned_set_first_paned (paned, NULL); @@ -1410,8 +1399,6 @@ gtk_paned_init (GtkPaned *paned) priv->position_set = FALSE; priv->last_allocation = -1; - priv->last_child1_focus = NULL; - priv->last_child2_focus = NULL; priv->in_recursion = FALSE; priv->original_position = -1; priv->max_position = G_MAXINT; @@ -1860,86 +1847,23 @@ gtk_paned_set_first_paned (GtkPaned *paned, GtkPaned *first_paned) (gpointer *)&(priv->first_paned)); } -static void -gtk_paned_set_last_child1_focus (GtkPaned *paned, GtkWidget *widget) -{ - GtkPanedPrivate *priv = gtk_paned_get_instance_private (paned); - - if (priv->last_child1_focus) - g_object_remove_weak_pointer (G_OBJECT (priv->last_child1_focus), - (gpointer *)&(priv->last_child1_focus)); - - priv->last_child1_focus = widget; - - if (priv->last_child1_focus) - g_object_add_weak_pointer (G_OBJECT (priv->last_child1_focus), - (gpointer *)&(priv->last_child1_focus)); -} - -static void -gtk_paned_set_last_child2_focus (GtkPaned *paned, GtkWidget *widget) -{ - GtkPanedPrivate *priv = gtk_paned_get_instance_private (paned); - - if (priv->last_child2_focus) - g_object_remove_weak_pointer (G_OBJECT (priv->last_child2_focus), - (gpointer *)&(priv->last_child2_focus)); - - priv->last_child2_focus = widget; - - if (priv->last_child2_focus) - g_object_add_weak_pointer (G_OBJECT (priv->last_child2_focus), - (gpointer *)&(priv->last_child2_focus)); -} - static GtkWidget * -paned_get_focus_widget (GtkPaned *paned) -{ - GtkWidget *toplevel; - - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned)); - if (gtk_widget_is_toplevel (toplevel)) - return gtk_window_get_focus (GTK_WINDOW (toplevel)); - - return NULL; -} - -static void -gtk_paned_set_focus_child (GtkContainer *container, - GtkWidget *focus_child) +find_last_focus (GtkWidget *widget) { - GtkPaned *paned = GTK_PANED (container); - GtkPanedPrivate *priv = gtk_paned_get_instance_private (paned); - GtkWidget *container_focus_child; + GtkWidget *f = widget; - g_return_if_fail (GTK_IS_PANED (container)); - - if (focus_child == NULL) + while (f) { - GtkWidget *last_focus; - GtkWidget *w; - - last_focus = paned_get_focus_widget (paned); - - if (last_focus) - { - /* If there is one or more paned widgets between us and the - * focus widget, we want the topmost of those as last_focus - */ - for (w = last_focus; w != GTK_WIDGET (paned); w = gtk_widget_get_parent (w)) - if (GTK_IS_PANED (w)) - last_focus = w; - - container_focus_child = gtk_widget_get_focus_child (GTK_WIDGET (container)); - if (container_focus_child == priv->child1) - gtk_paned_set_last_child1_focus (paned, last_focus); - else if (container_focus_child == priv->child2) - gtk_paned_set_last_child2_focus (paned, last_focus); - } + GtkWidget *focus_child = gtk_widget_get_focus_child (f); + if (focus_child == NULL) + break; + f = focus_child; } - if (GTK_CONTAINER_CLASS (gtk_paned_parent_class)->set_focus_child) - GTK_CONTAINER_CLASS (gtk_paned_parent_class)->set_focus_child (container, focus_child); + if (f != widget) + return f; + + return NULL; } static void @@ -1954,24 +1878,14 @@ gtk_paned_get_cycle_chain (GtkPaned *paned, GtkWidget *widget = GTK_WIDGET (paned); GList *temp_list = NULL; GList *list; + GtkWidget *last_child1_focus; + GtkWidget *last_child2_focus; if (priv->in_recursion) return; g_assert (widgets != NULL); - if (priv->last_child1_focus && - !gtk_widget_is_ancestor (priv->last_child1_focus, widget)) - { - gtk_paned_set_last_child1_focus (paned, NULL); - } - - if (priv->last_child2_focus && - !gtk_widget_is_ancestor (priv->last_child2_focus, widget)) - { - gtk_paned_set_last_child2_focus (paned, NULL); - } - parent = gtk_widget_get_parent (widget); if (parent) ancestor = gtk_widget_get_ancestor (parent, GTK_TYPE_PANED); @@ -1984,26 +1898,28 @@ gtk_paned_get_cycle_chain (GtkPaned *paned, * priv->last_child?_focus before priv->child?, both when we * are going forward and backward. */ + last_child1_focus = find_last_focus (priv->child1); + last_child2_focus = find_last_focus (priv->child2); focus_child = gtk_widget_get_focus_child (GTK_WIDGET (paned)); if (direction == GTK_DIR_TAB_FORWARD) { if (focus_child == priv->child1) { - temp_list = g_list_append (temp_list, priv->last_child2_focus); + temp_list = g_list_append (temp_list, last_child2_focus); temp_list = g_list_append (temp_list, priv->child2); temp_list = g_list_append (temp_list, ancestor); } else if (focus_child == priv->child2) { temp_list = g_list_append (temp_list, ancestor); - temp_list = g_list_append (temp_list, priv->last_child1_focus); + temp_list = g_list_append (temp_list, last_child1_focus); temp_list = g_list_append (temp_list, priv->child1); } else { - temp_list = g_list_append (temp_list, priv->last_child1_focus); + temp_list = g_list_append (temp_list, last_child1_focus); temp_list = g_list_append (temp_list, priv->child1); - temp_list = g_list_append (temp_list, priv->last_child2_focus); + temp_list = g_list_append (temp_list, last_child2_focus); temp_list = g_list_append (temp_list, priv->child2); temp_list = g_list_append (temp_list, ancestor); } @@ -2013,20 +1929,20 @@ gtk_paned_get_cycle_chain (GtkPaned *paned, if (focus_child == priv->child1) { temp_list = g_list_append (temp_list, ancestor); - temp_list = g_list_append (temp_list, priv->last_child2_focus); + temp_list = g_list_append (temp_list, last_child2_focus); temp_list = g_list_append (temp_list, priv->child2); } else if (focus_child == priv->child2) { - temp_list = g_list_append (temp_list, priv->last_child1_focus); + temp_list = g_list_append (temp_list, last_child1_focus); temp_list = g_list_append (temp_list, priv->child1); temp_list = g_list_append (temp_list, ancestor); } else { - temp_list = g_list_append (temp_list, priv->last_child2_focus); + temp_list = g_list_append (temp_list, last_child2_focus); temp_list = g_list_append (temp_list, priv->child2); - temp_list = g_list_append (temp_list, priv->last_child1_focus); + temp_list = g_list_append (temp_list, last_child1_focus); temp_list = g_list_append (temp_list, priv->child1); temp_list = g_list_append (temp_list, ancestor); } -- GitLab From 9b3206a2caa93d6a2bb6e891e4b781d14aeb1e94 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 08:45:13 -0500 Subject: [PATCH 04/70] root: Add focus Add a getter and a setter for the focus widget. The default implementations do nothing. --- gtk/gtkroot.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ gtk/gtkroot.h | 10 +++++++++ 2 files changed, 69 insertions(+) diff --git a/gtk/gtkroot.c b/gtk/gtkroot.c index ad93f8791a1..3552a85474e 100644 --- a/gtk/gtkroot.c +++ b/gtk/gtkroot.c @@ -59,12 +59,26 @@ gtk_root_default_get_surface_transform (GtkRoot *self, *y = 0; } +static void +gtk_root_default_set_focus (GtkRoot *self, + GtkWidget *focus) +{ +} + +static GtkWidget * +gtk_root_default_get_focus (GtkRoot *self) +{ + return NULL; +} + static void gtk_root_default_init (GtkRootInterface *iface) { iface->get_display = gtk_root_default_get_display; iface->get_renderer = gtk_root_default_get_renderer; iface->get_surface_transform = gtk_root_default_get_surface_transform; + iface->set_focus = gtk_root_default_set_focus; + iface->get_focus = gtk_root_default_get_focus; } GdkDisplay * @@ -124,3 +138,48 @@ gtk_root_get_for_surface (GdkSurface *surface) return NULL; } + +/** + * gtk_root_set_focus: + * @self: a #GtkRoot + * @focus: (allow-none): widget to be the new focus widget, or %NULL + * to unset the focus widget + * + * If @focus is not the current focus widget, and is focusable, sets + * it as the focus widget for the root. If @focus is %NULL, unsets + * the focus widget for the root. + * + * To set the focus to a particular widget in the root, it is usually + * more convenient to use gtk_widget_grab_focus() instead of this function. + */ +void +gtk_root_set_focus (GtkRoot *self, + GtkWidget *focus) +{ + g_return_if_fail (GTK_IS_ROOT (self)); + g_return_if_fail (focus == NULL || GTK_IS_WIDGET (focus)); + + GTK_ROOT_GET_IFACE (self)->set_focus (self, focus); +} + +/** + * gtk_root_get_focus: + * @self: a #GtkRoot + * + * Retrieves the current focused widget within the root. + * + * Note that this is the widget that would have the focus + * if the root is active; if the root is not focused then + * `gtk_widget_has_focus (widget)` will be %FALSE for the + * widget. + * + * Returns: (nullable) (transfer none): the currently focused widget, + * or %NULL if there is none. + */ +GtkWidget * +gtk_root_get_focus (GtkRoot *self) +{ + g_return_val_if_fail (GTK_IS_ROOT (self), NULL); + + return GTK_ROOT_GET_IFACE (self)->get_focus (self); +} diff --git a/gtk/gtkroot.h b/gtk/gtkroot.h index 45e777ba684..fe4d980d2e3 100644 --- a/gtk/gtkroot.h +++ b/gtk/gtkroot.h @@ -51,11 +51,21 @@ struct _GtkRootInterface void (* get_surface_transform) (GtkRoot *root, int *x, int *y); + + void (* set_focus) (GtkRoot *self, + GtkWidget *focus); + GtkWidget * (* get_focus) (GtkRoot *self); }; GDK_AVAILABLE_IN_ALL GtkWidget * gtk_root_get_for_surface (GdkSurface *surface); +GDK_AVAILABLE_IN_ALL +void gtk_root_set_focus (GtkRoot *self, + GtkWidget *focus); +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_root_get_focus (GtkRoot *self); + G_END_DECLS #endif /* __GTK_ROOT_H__ */ -- GitLab From 37cc26b7942ac15b5ea0a86a033d4326c29a920c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 08:46:21 -0500 Subject: [PATCH 05/70] window: Implement the root focus api This just uses the existing get/set_focus functions. We keep them public for now. --- gtk/gtkwindow.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 10e80d195db..87c3aa69276 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -2533,12 +2533,31 @@ gtk_window_root_get_surface_transform (GtkRoot *root, *y = margin.top + border.top + padding.top; } +static void +gtk_window_root_set_focus (GtkRoot *root, + GtkWidget *focus) +{ + GtkWindow *self = GTK_WINDOW (root); + + gtk_window_set_focus (self, focus); +} + +static GtkWidget * +gtk_window_root_get_focus (GtkRoot *root) +{ + GtkWindow *self = GTK_WINDOW (root); + + return gtk_window_get_focus (self); +} + static void gtk_window_root_interface_init (GtkRootInterface *iface) { iface->get_display = gtk_window_root_get_display; iface->get_renderer = gtk_window_root_get_renderer; iface->get_surface_transform = gtk_window_root_get_surface_transform; + iface->set_focus = gtk_window_root_set_focus; + iface->get_focus = gtk_window_root_get_focus; } /** -- GitLab From a8a5e65d0fdf1753d0aba037120a59011d162593 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 08:49:00 -0500 Subject: [PATCH 06/70] Port widgets to the root focus API --- gtk/gtkcoloreditor.c | 3 ++- gtk/gtkfilechooserwidget.c | 13 +++++++------ gtk/gtklabel.c | 2 +- gtk/gtkmain.c | 4 ++-- gtk/gtknotebook.c | 4 ++-- gtk/gtkpaned.c | 12 +++--------- gtk/gtkplacesview.c | 2 +- gtk/gtkstack.c | 34 ++++++++++++++++------------------ gtk/gtktext.c | 2 +- gtk/gtktreeviewcolumn.c | 7 ++----- gtk/gtkwidget.c | 25 +++++++------------------ gtk/gtkwidgetfocus.c | 10 +++------- gtk/inspector/misc-info.c | 4 ++-- tests/testtoolbar.c | 2 +- 14 files changed, 50 insertions(+), 74 deletions(-) diff --git a/gtk/gtkcoloreditor.c b/gtk/gtkcoloreditor.c index 34f5991ca2b..8670027631a 100644 --- a/gtk/gtkcoloreditor.c +++ b/gtk/gtkcoloreditor.c @@ -36,6 +36,7 @@ #include "gtkspinbutton.h" #include "gtkstylecontext.h" #include "gtkeventcontrollerkey.h" +#include "gtkroot.h" #include @@ -224,7 +225,7 @@ popup_edit (GtkWidget *widget, { dismiss_current_popup (editor); toplevel = gtk_widget_get_toplevel (GTK_WIDGET (editor)); - g_set_object (&editor->priv->popdown_focus, gtk_window_get_focus (GTK_WINDOW (toplevel))); + g_set_object (&editor->priv->popdown_focus, gtk_root_get_focus (GTK_ROOT (toplevel))); editor->priv->current_popup = popup; editor->priv->popup_position = position; gtk_widget_show (popup); diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index 4d9ab00e051..7c7b32d1194 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -82,6 +82,7 @@ #include "gtkdebug.h" #include "gtkfilechoosererrorstackprivate.h" #include "gtkentryprivate.h" +#include "gtkroot.h" #include @@ -1361,7 +1362,7 @@ key_press_cb (GtkEventController *controller, GtkWidget *default_widget, *focus_widget; default_widget = gtk_window_get_default_widget (window); - focus_widget = gtk_window_get_focus (window); + focus_widget = gtk_root_get_focus (GTK_ROOT (window)); if (widget != default_widget && !(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget)))) @@ -2709,7 +2710,7 @@ location_mode_set (GtkFileChooserWidget *impl, switch_to_file_list = FALSE; if (toplevel) { - current_focus = gtk_window_get_focus (toplevel); + current_focus = gtk_root_get_focus (GTK_ROOT (toplevel)); if (!current_focus || current_focus == priv->location_entry) switch_to_file_list = TRUE; } @@ -3566,7 +3567,7 @@ toplevel_set_focus_cb (GtkWindow *window, { GtkFileChooserWidgetPrivate *priv = impl->priv; - priv->toplevel_last_focus_widget = gtk_window_get_focus (window); + priv->toplevel_last_focus_widget = gtk_root_get_focus (GTK_ROOT (window)); } /* We monitor the focus widget on our toplevel to be able to know which widget @@ -3586,7 +3587,7 @@ gtk_file_chooser_widget_root (GtkWidget *widget) g_assert (priv->toplevel_set_focus_id == 0); priv->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus", G_CALLBACK (toplevel_set_focus_cb), impl); - priv->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel)); + priv->toplevel_last_focus_widget = gtk_root_get_focus (GTK_ROOT (toplevel)); } static void @@ -5806,7 +5807,7 @@ gtk_file_chooser_widget_get_files (GtkFileChooser *chooser) toplevel = get_toplevel (GTK_WIDGET (impl)); if (toplevel) - current_focus = gtk_window_get_focus (toplevel); + current_focus = gtk_root_get_focus (GTK_ROOT (toplevel)); else current_focus = NULL; @@ -6655,7 +6656,7 @@ gtk_file_chooser_widget_should_respond (GtkFileChooserEmbed *chooser_embed) retval = FALSE; - current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); + current_focus = gtk_root_get_focus (GTK_ROOT (toplevel)); if (current_focus == priv->browse_files_tree_view) { diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index 86442d16a1d..e3be68e02e1 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -6222,7 +6222,7 @@ gtk_label_activate_current_link (GtkLabel *label) if (window) { default_widget = gtk_window_get_default_widget (window); - focus_widget = gtk_window_get_focus (window); + focus_widget = gtk_root_get_focus (GTK_ROOT (window)); if (default_widget != widget && !(widget == focus_widget && (!default_widget || !gtk_widget_is_sensitive (default_widget)))) diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 8aa278e9eb4..be19d7389cd 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1761,7 +1761,7 @@ gtk_main_do_event (GdkEvent *event) if (is_pointing_event (event)) target_widget = handle_pointing_event (event); - else if (GTK_IS_WINDOW (target_widget) && + else if (GTK_IS_ROOT (target_widget) && (event->any.type == GDK_KEY_PRESS || event->any.type == GDK_KEY_RELEASE)) { @@ -1771,7 +1771,7 @@ gtk_main_do_event (GdkEvent *event) gtk_window_activate_key (GTK_WINDOW (target_widget), (GdkEventKey *) event)) goto cleanup; - focus_widget = gtk_window_get_focus (GTK_WINDOW (target_widget)); + focus_widget = gtk_root_get_focus (GTK_ROOT (target_widget)); if (focus_widget) target_widget = focus_widget; } diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index 7677682b822..eb0ec8a859f 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -5356,8 +5356,8 @@ gtk_notebook_real_switch_page (GtkNotebook *notebook, if (priv->cur_page) { - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (notebook)); - GtkWidget *focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); + GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (notebook)); + GtkWidget *focus = gtk_root_get_focus (root); if (focus) child_has_focus = gtk_widget_is_ancestor (focus, priv->cur_page->child); gtk_widget_unset_state_flags (priv->cur_page->tab_widget, GTK_STATE_FLAG_CHECKED); diff --git a/gtk/gtkpaned.c b/gtk/gtkpaned.c index 9250233c1f8..abaa822d6be 100644 --- a/gtk/gtkpaned.c +++ b/gtk/gtkpaned.c @@ -2172,10 +2172,8 @@ gtk_paned_restore_focus (GtkPaned *paned) if (!gtk_widget_child_focus (GTK_WIDGET (paned), GTK_DIR_TAB_FORWARD)) { - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned)); - - if (GTK_IS_WINDOW (toplevel)) - gtk_window_set_focus (GTK_WINDOW (toplevel), NULL); + GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (paned)); + gtk_root_set_focus (root, NULL); } } @@ -2280,7 +2278,6 @@ gtk_paned_cycle_handle_focus (GtkPaned *paned, { GtkPaned *focus; GtkPaned *first; - GtkWidget *toplevel; GtkWidget *focus_child; gtk_paned_find_neighbours (paned, &next, &prev); @@ -2326,10 +2323,7 @@ gtk_paned_cycle_handle_focus (GtkPaned *paned, first = next; } - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned)); - - if (GTK_IS_WINDOW (toplevel)) - gtk_paned_set_saved_focus (focus, gtk_window_get_focus (GTK_WINDOW (toplevel))); + gtk_paned_set_saved_focus (focus, gtk_root_get_focus (gtk_widget_get_root (GTK_WIDGET (paned)))); gtk_paned_set_first_paned (focus, first); priv->original_position = gtk_paned_get_position (focus); diff --git a/gtk/gtkplacesview.c b/gtk/gtkplacesview.c index bb03ef9d01d..dabb4860464 100644 --- a/gtk/gtkplacesview.c +++ b/gtk/gtkplacesview.c @@ -1766,7 +1766,7 @@ on_key_press_event (GtkEventController *controller, if (!toplevel) return FALSE; - focus_widget = gtk_window_get_focus (toplevel); + focus_widget = gtk_root_get_focus (GTK_ROOT (toplevel)); if (!GTK_IS_PLACES_VIEW_ROW (focus_widget)) return FALSE; diff --git a/gtk/gtkstack.c b/gtk/gtkstack.c index f9a7723448f..d7222f9e5e6 100644 --- a/gtk/gtkstack.c +++ b/gtk/gtkstack.c @@ -1123,7 +1123,6 @@ set_visible_child (GtkStack *stack, GtkStackPage *info; GtkWidget *widget = GTK_WIDGET (stack); GList *l; - GtkWidget *toplevel; GtkWidget *focus; gboolean contains_focus = FALSE; guint old_pos = GTK_INVALID_LIST_POSITION; @@ -1165,24 +1164,23 @@ set_visible_child (GtkStack *stack, } } - toplevel = gtk_widget_get_toplevel (widget); - if (GTK_IS_WINDOW (toplevel)) + if (gtk_widget_get_root (widget)) + focus = gtk_root_get_focus (gtk_widget_get_root (widget)); + else + focus = NULL; + if (focus && + priv->visible_child && + priv->visible_child->widget && + gtk_widget_is_ancestor (focus, priv->visible_child->widget)) { - focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); - if (focus && - priv->visible_child && - priv->visible_child->widget && - gtk_widget_is_ancestor (focus, priv->visible_child->widget)) - { - contains_focus = TRUE; - - if (priv->visible_child->last_focus) - g_object_remove_weak_pointer (G_OBJECT (priv->visible_child->last_focus), - (gpointer *)&priv->visible_child->last_focus); - priv->visible_child->last_focus = focus; - g_object_add_weak_pointer (G_OBJECT (priv->visible_child->last_focus), - (gpointer *)&priv->visible_child->last_focus); - } + contains_focus = TRUE; + + if (priv->visible_child->last_focus) + g_object_remove_weak_pointer (G_OBJECT (priv->visible_child->last_focus), + (gpointer *)&priv->visible_child->last_focus); + priv->visible_child->last_focus = focus; + g_object_add_weak_pointer (G_OBJECT (priv->visible_child->last_focus), + (gpointer *)&priv->visible_child->last_focus); } if (priv->last_visible_child) diff --git a/gtk/gtktext.c b/gtk/gtktext.c index 2ee20ffce77..ed721eca6bf 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -3827,7 +3827,7 @@ gtk_text_real_activate (GtkText *self) if (window) { default_widget = gtk_window_get_default_widget (window); - focus_widget = gtk_window_get_focus (window); + focus_widget = gtk_root_get_focus (GTK_ROOT (window)); if (widget != default_widget && !(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget)))) gtk_window_activate_default (window); diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c index 5817a5f2f86..8c113ad3c4c 100644 --- a/gtk/gtktreeviewcolumn.c +++ b/gtk/gtktreeviewcolumn.c @@ -1016,11 +1016,8 @@ gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column) gtk_widget_set_can_focus (priv->button, FALSE); if (gtk_widget_has_focus (priv->button)) { - GtkWidget *toplevel = gtk_widget_get_toplevel (priv->tree_view); - if (gtk_widget_is_toplevel (toplevel)) - { - gtk_window_set_focus (GTK_WINDOW (toplevel), NULL); - } + GtkRoot *root = gtk_widget_get_root (priv->tree_view); + gtk_root_set_focus (root, NULL); } } /* Queue a resize on the assumption that we always want to catch all changes diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index c35a9d12fd9..235471e3a17 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -5346,10 +5346,8 @@ gtk_widget_grab_focus (GtkWidget *widget) static void gtk_widget_real_grab_focus (GtkWidget *focus_widget) { - GtkWidget *toplevel; - - toplevel = gtk_widget_get_toplevel (focus_widget); - gtk_window_set_focus (GTK_WINDOW (toplevel), focus_widget); + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (focus_widget); + gtk_root_set_focus (priv->root, focus_widget); } static gboolean @@ -5634,16 +5632,14 @@ gtk_widget_has_visible_focus (GtkWidget *widget) gboolean gtk_widget_is_focus (GtkWidget *widget) { - GtkWidget *toplevel; + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - toplevel = _gtk_widget_get_toplevel (widget); + if (priv->root) + return widget == gtk_root_get_focus (priv->root); - if (GTK_IS_WINDOW (toplevel)) - return widget == gtk_window_get_focus (GTK_WINDOW (toplevel)); - else - return FALSE; + return FALSE; } /** @@ -8849,14 +8845,7 @@ gtk_widget_propagate_state (GtkWidget *widget, priv->state_flags |= GTK_STATE_FLAG_INSENSITIVE; if (gtk_widget_is_focus (widget) && !gtk_widget_is_sensitive (widget)) - { - GtkWidget *window; - - window = _gtk_widget_get_toplevel (widget); - - if (window && _gtk_widget_is_toplevel (window)) - gtk_window_set_focus (GTK_WINDOW (window), NULL); - } + gtk_root_set_focus (priv->root, NULL); new_flags = priv->state_flags; diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c index af68bd7a563..b1abae3894d 100644 --- a/gtk/gtkwidgetfocus.c +++ b/gtk/gtkwidgetfocus.c @@ -155,15 +155,11 @@ static gboolean old_focus_coords (GtkWidget *widget, graphene_rect_t *old_focus_bounds) { - GtkWidget *toplevel = _gtk_widget_get_toplevel (widget); GtkWidget *old_focus; - if (GTK_IS_WINDOW (toplevel)) - { - old_focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); - if (old_focus) - return gtk_widget_compute_bounds (old_focus, widget, old_focus_bounds); - } + old_focus = gtk_root_get_focus (gtk_widget_get_root (widget)); + if (old_focus) + return gtk_widget_compute_bounds (old_focus, widget, old_focus_bounds); return FALSE; } diff --git a/gtk/inspector/misc-info.c b/gtk/inspector/misc-info.c index 9c3b3ec05f9..f352894daf1 100644 --- a/gtk/inspector/misc-info.c +++ b/gtk/inspector/misc-info.c @@ -222,7 +222,7 @@ update_focus_widget (GtkInspectorMiscInfo *sl) { GtkWidget *widget; - widget = gtk_window_get_focus (GTK_WINDOW (sl->priv->object)); + widget = gtk_root_get_focus (GTK_ROOT (sl->priv->object)); if (widget) { gchar *tmp; @@ -249,7 +249,7 @@ show_focus_widget (GtkWidget *button, GtkInspectorMiscInfo *sl) { GtkWidget *widget; - widget = gtk_window_get_focus (GTK_WINDOW (sl->priv->object)); + widget = gtk_root_get_focus (GTK_ROOT (sl->priv->object)); if (widget) show_object (sl, G_OBJECT (widget), "properties"); } diff --git a/tests/testtoolbar.c b/tests/testtoolbar.c index 57c7ceb1b3f..12160c8115f 100644 --- a/tests/testtoolbar.c +++ b/tests/testtoolbar.c @@ -344,7 +344,7 @@ popup_context_menu (GtkToolbar *toolbar, gint x, gint y, gint button_number) GtkWidget *widget; window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (toolbar))); - widget = gtk_window_get_focus (window); + widget = gtk_root_get_focus (GTK_ROOT (window)); if (!widget) widget = GTK_WIDGET (toolbar); -- GitLab From 5af98031e5c5067dc3a97e1b83022bfc93e7e76e Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 11:40:09 -0500 Subject: [PATCH 07/70] Move a check to gtk_window_set_focus gtk_widget_grab_focus is just a wrapper for gtk_window_set_focus. We should do all the enforcement there. --- gtk/gtkwidget.c | 3 --- gtk/gtkwindow.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 235471e3a17..ca7ec817ef6 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -5337,9 +5337,6 @@ gtk_widget_grab_focus (GtkWidget *widget) { g_return_if_fail (GTK_IS_WIDGET (widget)); - if (!gtk_widget_is_sensitive (widget)) - return; - GTK_WIDGET_GET_CLASS (widget)->grab_focus (widget); } diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 87c3aa69276..575bc80f89d 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -7343,6 +7343,9 @@ static void gtk_window_real_set_focus (GtkWindow *window, GtkWidget *focus) { + if (focus && !gtk_widget_is_sensitive (focus)) + return; + unset_focus_widget (window); set_focus_widget (window, focus); } -- GitLab From 3bb19eb91b92f4acb37d383b48c7b0984d52c9c5 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 12:52:56 -0500 Subject: [PATCH 08/70] Simplify the move_focus api No need to pass the array in from the outside. --- gtk/gtkwidget.c | 13 ++----------- gtk/gtkwidgetfocus.c | 21 ++++++++++++--------- gtk/gtkwidgetprivate.h | 3 +-- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index ca7ec817ef6..90d3d3088e8 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -5459,17 +5459,8 @@ gtk_widget_real_focus (GtkWidget *widget, } else { - GPtrArray *focus_order = g_ptr_array_new (); - gboolean ret = FALSE; - - /* Try focusing any of the child widgets, depending on the given @direction */ - - gtk_widget_focus_sort (widget, direction, focus_order); - ret = gtk_widget_focus_move (widget, direction, focus_order); - - g_ptr_array_unref (focus_order); - - if (ret) + /* Try focusing any of the child widgets, depending on the given direction */ + if (gtk_widget_focus_move (widget, direction)) return TRUE; } diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c index b1abae3894d..c29adfdbe05 100644 --- a/gtk/gtkwidgetfocus.c +++ b/gtk/gtkwidgetfocus.c @@ -450,13 +450,17 @@ gtk_widget_focus_sort (GtkWidget *widget, gboolean gtk_widget_focus_move (GtkWidget *widget, - GtkDirectionType direction, - GPtrArray *focus_order) + GtkDirectionType direction) { + GPtrArray *focus_order; GtkWidget *focus_child = gtk_widget_get_focus_child (widget); int i; + gboolean ret = FALSE; + + focus_order = g_ptr_array_new (); + gtk_widget_focus_sort (widget, direction, focus_order); - for (i = 0; i < focus_order->len; i ++) + for (i = 0; i < focus_order->len && !ret; i++) { GtkWidget *child = g_ptr_array_index (focus_order, i); @@ -465,18 +469,17 @@ gtk_widget_focus_move (GtkWidget *widget, if (focus_child == child) { focus_child = NULL; - - if (gtk_widget_child_focus (child, direction)) - return TRUE; + ret = gtk_widget_child_focus (child, direction); } } else if (_gtk_widget_is_drawable (child) && gtk_widget_is_ancestor (child, widget)) { - if (gtk_widget_child_focus (child, direction)) - return TRUE; + ret = gtk_widget_child_focus (child, direction); } } - return FALSE; + g_ptr_array_unref (focus_order); + + return ret; } diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index ae8595e4dd4..314d793090f 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -317,8 +317,7 @@ void gtk_widget_focus_sort (GtkWidget *wi GtkDirectionType direction, GPtrArray *focus_order); gboolean gtk_widget_focus_move (GtkWidget *widget, - GtkDirectionType direction, - GPtrArray *focus_order); + GtkDirectionType direction); void gtk_widget_get_surface_allocation (GtkWidget *widget, GtkAllocation *allocation); -- GitLab From d7d05fe06ec278ecfee6fd5d82cb9b6d2120ff0f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 13:22:43 -0500 Subject: [PATCH 09/70] widget: Drop the ::focus signal The focus vfunc is an implementation detail of GTK focus handling, and having external signal handlers interfere with it is not a good idea. --- gtk/gtkwidget.c | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 90d3d3088e8..fe3061e6b42 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -484,7 +484,6 @@ enum { GRAB_NOTIFY, CHILD_NOTIFY, MNEMONIC_ACTIVATE, - FOCUS, MOVE_FOCUS, KEYNAV_FAILED, DRAG_BEGIN, @@ -1670,23 +1669,6 @@ gtk_widget_class_init (GtkWidgetClass *klass) G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); - /** - * GtkWidget::focus: - * @widget: the object which received the signal. - * @direction: - * - * Returns: %TRUE to stop other handlers from being invoked for the event. %FALSE to propagate the event further. - */ - widget_signals[FOCUS] = - g_signal_new (I_("focus"), - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkWidgetClass, focus), - _gtk_boolean_handled_accumulator, NULL, - _gtk_marshal_BOOLEAN__ENUM, - G_TYPE_BOOLEAN, 1, - GTK_TYPE_DIRECTION_TYPE); - /** * GtkWidget::move-focus: * @widget: the object which received the signal. @@ -7489,8 +7471,6 @@ gboolean gtk_widget_child_focus (GtkWidget *widget, GtkDirectionType direction) { - gboolean return_val; - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); if (!_gtk_widget_get_visible (widget) || @@ -7502,12 +7482,7 @@ gtk_widget_child_focus (GtkWidget *widget, * focus */ - g_signal_emit (widget, - widget_signals[FOCUS], - 0, - direction, &return_val); - - return return_val; + return GTK_WIDGET_GET_CLASS (widget)->focus (widget, direction); } /** -- GitLab From 9e0c4ed24bccf5b556811aa50c00b0af5af55bbc Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 14:35:14 -0500 Subject: [PATCH 10/70] widget: Add a next-focus-child API Move things around to make the focus chain introspectable. Use the next-focus-child api in the move-focus and focus implementations. We add a next_focus_child vfunc that containers can override to tweak the behavior. --- gtk/gtkwidget.c | 52 +++++++++++++++------ gtk/gtkwidget.h | 6 +++ gtk/gtkwidgetfocus.c | 75 ++++++++++++++++++++++++++++++- gtk/gtkwidgetprivate.h | 3 ++ gtk/gtkwindow.c | 100 +---------------------------------------- 5 files changed, 122 insertions(+), 114 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index fe3061e6b42..c581cb1766e 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -949,6 +949,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) klass->mnemonic_activate = gtk_widget_real_mnemonic_activate; klass->grab_focus = gtk_widget_real_grab_focus; klass->focus = gtk_widget_real_focus; + klass->next_focus_child = gtk_widget_next_focus_child; klass->move_focus = gtk_widget_real_move_focus; klass->keynav_failed = gtk_widget_real_keynav_failed; klass->drag_begin = NULL; @@ -5442,8 +5443,19 @@ gtk_widget_real_focus (GtkWidget *widget, else { /* Try focusing any of the child widgets, depending on the given direction */ - if (gtk_widget_focus_move (widget, direction)) - return TRUE; + GtkWidget *focus_child = gtk_widget_get_focus_child (widget); + GtkWidget *next_focus; + + if (focus_child) + next_focus = gtk_widget_get_next_focus (focus_child, direction); + else + next_focus = gtk_widget_get_next_focus (widget, direction); + + if (next_focus && gtk_widget_is_ancestor (next_focus, widget)) + { + gtk_widget_grab_focus (next_focus); + return TRUE; + } } return FALSE; @@ -5453,10 +5465,17 @@ static void gtk_widget_real_move_focus (GtkWidget *widget, GtkDirectionType direction) { - GtkWidget *toplevel = _gtk_widget_get_toplevel (widget); + GtkWidget *focus_child; + GtkWidget *next_focus; + + focus_child = gtk_root_get_focus (gtk_widget_get_root (widget)); + if (focus_child) + next_focus = gtk_widget_get_next_focus (focus_child, direction); + else + next_focus = gtk_widget_get_next_focus (widget, direction); - if (widget != toplevel && GTK_IS_WINDOW (toplevel)) - g_signal_emit (toplevel, widget_signals[MOVE_FOCUS], 0, direction); + if (next_focus) + gtk_widget_grab_focus (next_focus); } static gboolean @@ -7471,18 +7490,23 @@ gboolean gtk_widget_child_focus (GtkWidget *widget, GtkDirectionType direction) { - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + GtkWidget *focus_child; + GtkWidget *next_focus; - if (!_gtk_widget_get_visible (widget) || - !gtk_widget_is_sensitive (widget)) - return FALSE; + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - /* Emit ::focus in any case, even if can-focus is FALSE, - * since any widget might have child widgets that will take - * focus - */ + focus_child = gtk_root_get_focus (gtk_widget_get_root (widget)); + if (focus_child) + next_focus = gtk_widget_get_next_focus (focus_child, direction); + else + next_focus = gtk_widget_get_next_focus (widget, direction); + if (next_focus && gtk_widget_is_ancestor (next_focus, widget)) + { + gtk_widget_grab_focus (next_focus); + return TRUE; + } - return GTK_WIDGET_GET_CLASS (widget)->focus (widget, direction); + return FALSE; } /** diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index f3012b07a4c..11ba57036a6 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -280,6 +280,9 @@ struct _GtkWidgetClass void (* grab_focus) (GtkWidget *widget); gboolean (* focus) (GtkWidget *widget, GtkDirectionType direction); + GtkWidget * (* next_focus_child) (GtkWidget *widget, + GtkWidget *child, + GtkDirectionType direction); /* keyboard navigation */ void (* move_focus) (GtkWidget *widget, @@ -480,6 +483,9 @@ gboolean gtk_widget_has_visible_focus (GtkWidget *widget); GDK_AVAILABLE_IN_ALL void gtk_widget_grab_focus (GtkWidget *widget); GDK_AVAILABLE_IN_ALL +GtkWidget *gtk_widget_get_next_focus (GtkWidget *widget, + GtkDirectionType direction); +GDK_AVAILABLE_IN_ALL void gtk_widget_set_focus_on_click (GtkWidget *widget, gboolean focus_on_click); GDK_AVAILABLE_IN_ALL diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c index c29adfdbe05..61c02395085 100644 --- a/gtk/gtkwidgetfocus.c +++ b/gtk/gtkwidgetfocus.c @@ -422,7 +422,8 @@ gtk_widget_focus_sort (GtkWidget *widget, child != NULL; child = _gtk_widget_get_next_sibling (child)) { - if (_gtk_widget_get_realized (child)) + if (_gtk_widget_get_realized (child) && + gtk_widget_get_sensitive (child)) g_ptr_array_add (focus_order, child); } } @@ -483,3 +484,75 @@ gtk_widget_focus_move (GtkWidget *widget, return ret; } + +/** + * gtk_widget_get_next_focus: + * @widget: a #GtkWidget + * @direction: diretion to move in + * + * Finds the widget that would get focused if @widget was + * the focus widget, and focus was moved in @direcion. + * + * Returns: (transfer none): the next focus widget + */ +GtkWidget * +gtk_widget_get_next_focus (GtkWidget *widget, + GtkDirectionType dir) +{ + GtkWidget *prev; + GtkWidget *next; + + prev = NULL; + do { + next = GTK_WIDGET_GET_CLASS (widget)->next_focus_child (widget, prev, dir); + if (next == NULL) + { + prev = widget; + widget = gtk_widget_get_parent (widget); + } + else if (gtk_widget_get_can_focus (next)) + { + return next; + } + else + { + widget = next; + prev = NULL; + } + } while (widget); + + return NULL; +} + +GtkWidget * +gtk_widget_next_focus_child (GtkWidget *widget, + GtkWidget *focus_child, + GtkDirectionType direction) +{ + GPtrArray *focus_order; + int i; + GtkWidget *next_child = NULL; + + focus_order = g_ptr_array_new (); + gtk_widget_focus_sort (widget, direction, focus_order); + + for (i = 0; i < focus_order->len; i++) + { + GtkWidget *child = g_ptr_array_index (focus_order, i); + + if (focus_child) + { + if (focus_child == child) + focus_child = NULL; + } + else + { + next_child = child; + break; + } + } + + g_ptr_array_unref (focus_order); + + return next_child; +} diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index 314d793090f..3638e40356d 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -318,6 +318,9 @@ void gtk_widget_focus_sort (GtkWidget *wi GPtrArray *focus_order); gboolean gtk_widget_focus_move (GtkWidget *widget, GtkDirectionType direction); +GtkWidget * gtk_widget_next_focus_child (GtkWidget *widget, + GtkWidget *child, + GtkDirectionType direction); void gtk_widget_get_surface_allocation (GtkWidget *widget, GtkAllocation *allocation); diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 575bc80f89d..36ac49ff956 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -436,10 +436,6 @@ static void gtk_window_remove (GtkContainer *container, static void gtk_window_forall (GtkContainer *container, GtkCallback callback, gpointer callback_data); -static gint gtk_window_focus (GtkWidget *widget, - GtkDirectionType direction); -static void gtk_window_move_focus (GtkWidget *widget, - GtkDirectionType dir); static void gtk_window_real_set_focus (GtkWindow *window, GtkWidget *focus); @@ -804,8 +800,6 @@ gtk_window_class_init (GtkWindowClass *klass) widget_class->realize = gtk_window_realize; widget_class->unrealize = gtk_window_unrealize; widget_class->size_allocate = gtk_window_size_allocate; - widget_class->focus = gtk_window_focus; - widget_class->move_focus = gtk_window_move_focus; widget_class->measure = gtk_window_measure; widget_class->state_flags_changed = gtk_window_state_flags_changed; widget_class->style_updated = gtk_window_style_updated; @@ -5750,7 +5744,7 @@ gtk_window_show (GtkWidget *widget) if (priv->initial_focus) gtk_window_set_focus (window, priv->initial_focus); else - gtk_window_move_focus (widget, GTK_DIR_TAB_FORWARD); + gtk_widget_child_focus (widget, GTK_DIR_TAB_FORWARD); } if (priv->modal) @@ -7209,98 +7203,6 @@ gtk_window_forall (GtkContainer *container, (* callback) (priv->title_box, callback_data); } -static gboolean -gtk_window_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkWindow *window = GTK_WINDOW (widget); - GtkWindowPrivate *priv = gtk_window_get_instance_private (window); - GtkBin *bin; - GtkContainer *container; - GtkWidget *child; - GtkWidget *old_focus_child; - GtkWidget *parent; - - if (!_gtk_widget_is_toplevel (widget)) - return GTK_WIDGET_CLASS (gtk_window_parent_class)->focus (widget, direction); - - container = GTK_CONTAINER (widget); - bin = GTK_BIN (widget); - - old_focus_child = gtk_widget_get_focus_child (widget); - - /* We need a special implementation here to deal properly with wrapping - * around in the tab chain without the danger of going into an - * infinite loop. - */ - if (old_focus_child) - { - if (gtk_widget_child_focus (old_focus_child, direction)) - return TRUE; - } - - if (priv->focus_widget) - { - if (direction == GTK_DIR_LEFT || - direction == GTK_DIR_RIGHT || - direction == GTK_DIR_UP || - direction == GTK_DIR_DOWN) - { - return FALSE; - } - - /* Wrapped off the end, clear the focus setting for the toplpevel */ - parent = _gtk_widget_get_parent (priv->focus_widget); - while (parent) - { - gtk_widget_set_focus_child (parent, NULL); - parent = _gtk_widget_get_parent (parent); - } - - gtk_window_set_focus (GTK_WINDOW (container), NULL); - } - - /* Now try to focus the first widget in the window, - * taking care to hook titlebar widgets into the - * focus chain. - */ - if (priv->title_box != NULL && - old_focus_child != NULL && - priv->title_box != old_focus_child) - child = priv->title_box; - else - child = gtk_bin_get_child (bin); - - if (child) - { - if (gtk_widget_child_focus (child, direction)) - return TRUE; - else if (priv->title_box != NULL && - priv->title_box != child && - gtk_widget_child_focus (priv->title_box, direction)) - return TRUE; - - } - - return FALSE; -} - -static void -gtk_window_move_focus (GtkWidget *widget, - GtkDirectionType dir) -{ - if (!_gtk_widget_is_toplevel (widget)) - { - GTK_WIDGET_CLASS (gtk_window_parent_class)->move_focus (widget, dir); - return; - } - - gtk_widget_child_focus (widget, dir); - - if (!gtk_widget_get_focus_child (widget)) - gtk_window_set_focus (GTK_WINDOW (widget), NULL); -} - static void unset_focus_widget (GtkWindow *window) { -- GitLab From 4588950a43193b7f625f8d684a416949013b5fba Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 14:34:28 -0500 Subject: [PATCH 11/70] stack: Implement next_focus_child We only return the visible page here, to avoid focus moving into other pages. --- gtk/gtkstack.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/gtk/gtkstack.c b/gtk/gtkstack.c index d7222f9e5e6..04b4b0b4225 100644 --- a/gtk/gtkstack.c +++ b/gtk/gtkstack.c @@ -533,6 +533,9 @@ static void gtk_stack_forall (GtkContainer *contain static void gtk_stack_compute_expand (GtkWidget *widget, gboolean *hexpand, gboolean *vexpand); +static GtkWidget *gtk_stack_next_focus_child (GtkWidget *widget, + GtkWidget *child, + GtkDirectionType direction); static void gtk_stack_size_allocate (GtkWidget *widget, int width, int height, @@ -724,6 +727,7 @@ gtk_stack_class_init (GtkStackClass *klass) widget_class->snapshot = gtk_stack_snapshot; widget_class->measure = gtk_stack_measure; widget_class->compute_expand = gtk_stack_compute_expand; + widget_class->next_focus_child = gtk_stack_next_focus_child; container_class->add = gtk_stack_add; container_class->remove = gtk_stack_remove; @@ -2434,6 +2438,19 @@ gtk_stack_measure (GtkWidget *widget, } } +static GtkWidget * +gtk_stack_next_focus_child (GtkWidget *widget, + GtkWidget *child, + GtkDirectionType direction) +{ + GtkStackPrivate *priv = gtk_stack_get_instance_private (GTK_STACK (widget)); + + if (child == NULL) + return priv->visible_child->widget; + + return NULL; +} + static void gtk_stack_init (GtkStack *stack) { -- GitLab From fdc7231bfd02cd69876c0edc4723119950c88ef6 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 15:40:30 -0500 Subject: [PATCH 12/70] expander: remove the ::focus implementation --- gtk/gtkexpander.c | 174 ---------------------------------------------- 1 file changed, 174 deletions(-) diff --git a/gtk/gtkexpander.c b/gtk/gtkexpander.c index cba7916c121..c03c0257c35 100644 --- a/gtk/gtkexpander.c +++ b/gtk/gtkexpander.c @@ -177,8 +177,6 @@ static void gtk_expander_size_allocate (GtkWidget *widget, int width, int height, int baseline); -static gboolean gtk_expander_focus (GtkWidget *widget, - GtkDirectionType direction); static gboolean gtk_expander_drag_motion (GtkWidget *widget, GdkDrop *drop, gint x, @@ -253,7 +251,6 @@ gtk_expander_class_init (GtkExpanderClass *klass) widget_class->destroy = gtk_expander_destroy; widget_class->size_allocate = gtk_expander_size_allocate; - widget_class->focus = gtk_expander_focus; widget_class->drag_motion = gtk_expander_drag_motion; widget_class->drag_leave = gtk_expander_drag_leave; widget_class->measure = gtk_expander_measure; @@ -562,142 +559,6 @@ gtk_expander_drag_leave (GtkWidget *widget, } } -typedef enum -{ - FOCUS_NONE, - FOCUS_WIDGET, - FOCUS_LABEL, - FOCUS_CHILD -} FocusSite; - -static gboolean -focus_current_site (GtkExpander *expander, - GtkDirectionType direction) -{ - GtkWidget *current_focus; - - current_focus = gtk_widget_get_focus_child (GTK_WIDGET (expander)); - - if (!current_focus) - return FALSE; - - return gtk_widget_child_focus (current_focus, direction); -} - -static gboolean -focus_in_site (GtkExpander *expander, - FocusSite site, - GtkDirectionType direction) -{ - GtkExpanderPrivate *priv = gtk_expander_get_instance_private (expander); - - switch (site) - { - case FOCUS_WIDGET: - gtk_widget_grab_focus (GTK_WIDGET (expander)); - return TRUE; - case FOCUS_LABEL: - if (priv->label_widget) - return gtk_widget_child_focus (priv->label_widget, direction); - else - return FALSE; - case FOCUS_CHILD: - { - GtkWidget *child = priv->child; - - if (child && gtk_widget_get_child_visible (child)) - return gtk_widget_child_focus (child, direction); - else - return FALSE; - } - case FOCUS_NONE: - default: - break; - } - - g_assert_not_reached (); - return FALSE; -} - -static FocusSite -get_next_site (GtkExpander *expander, - FocusSite site, - GtkDirectionType direction) -{ - gboolean ltr; - - ltr = gtk_widget_get_direction (GTK_WIDGET (expander)) != GTK_TEXT_DIR_RTL; - - switch (site) - { - case FOCUS_NONE: - switch (direction) - { - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_LEFT: - case GTK_DIR_UP: - return FOCUS_CHILD; - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_DOWN: - case GTK_DIR_RIGHT: - default: - return FOCUS_WIDGET; - } - break; - case FOCUS_WIDGET: - switch (direction) - { - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_UP: - return FOCUS_NONE; - case GTK_DIR_LEFT: - return ltr ? FOCUS_NONE : FOCUS_LABEL; - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_DOWN: - default: - return FOCUS_LABEL; - case GTK_DIR_RIGHT: - return ltr ? FOCUS_LABEL : FOCUS_NONE; - } - break; - case FOCUS_LABEL: - switch (direction) - { - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_UP: - return FOCUS_WIDGET; - case GTK_DIR_LEFT: - return ltr ? FOCUS_WIDGET : FOCUS_CHILD; - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_DOWN: - default: - return FOCUS_CHILD; - case GTK_DIR_RIGHT: - return ltr ? FOCUS_CHILD : FOCUS_WIDGET; - } - break; - case FOCUS_CHILD: - switch (direction) - { - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_LEFT: - case GTK_DIR_UP: - return FOCUS_LABEL; - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_DOWN: - case GTK_DIR_RIGHT: - default: - return FOCUS_NONE; - } - break; - default: - g_assert_not_reached (); - break; - } - - return FOCUS_NONE; -} - static void gtk_expander_resize_toplevel (GtkExpander *expander) { @@ -731,41 +592,6 @@ gtk_expander_resize_toplevel (GtkExpander *expander) } } -static gboolean -gtk_expander_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkExpander *expander = GTK_EXPANDER (widget); - GtkExpanderPrivate *priv = gtk_expander_get_instance_private (expander); - - if (!focus_current_site (expander, direction)) - { - GtkWidget *old_focus_child; - gboolean widget_is_focus; - FocusSite site = FOCUS_NONE; - - widget_is_focus = gtk_widget_is_focus (widget); - old_focus_child = gtk_widget_get_focus_child (GTK_WIDGET (widget)); - - if (old_focus_child && old_focus_child == priv->label_widget) - site = FOCUS_LABEL; - else if (old_focus_child) - site = FOCUS_CHILD; - else if (widget_is_focus) - site = FOCUS_WIDGET; - - while ((site = get_next_site (expander, site, direction)) != FOCUS_NONE) - { - if (focus_in_site (expander, site, direction)) - return TRUE; - } - - return FALSE; - } - - return TRUE; -} - static void gtk_expander_add (GtkContainer *container, GtkWidget *widget) -- GitLab From 974af6773fcc550d8b2a764b115b00b348527b68 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 20:03:34 -0500 Subject: [PATCH 13/70] inspector: Add a focus overlay Add an overlay that shows a segment of the focus chain. --- gtk/inspector/focusoverlay.c | 135 +++++++++++++++++++++++++++++++++++ gtk/inspector/focusoverlay.h | 19 +++++ gtk/inspector/meson.build | 1 + gtk/inspector/visual.c | 38 ++++++++++ gtk/inspector/visual.ui | 27 +++++++ 5 files changed, 220 insertions(+) create mode 100644 gtk/inspector/focusoverlay.c create mode 100644 gtk/inspector/focusoverlay.h diff --git a/gtk/inspector/focusoverlay.c b/gtk/inspector/focusoverlay.c new file mode 100644 index 00000000000..a46d29ec50e --- /dev/null +++ b/gtk/inspector/focusoverlay.c @@ -0,0 +1,135 @@ +#include "config.h" +#include "focusoverlay.h" +#include "gtkwidgetprivate.h" + +struct _GtkFocusOverlay +{ + GtkInspectorOverlay parent_instance; +}; + +struct _GtkFocusOverlayClass +{ + GtkInspectorOverlayClass parent_class; +}; + +G_DEFINE_TYPE (GtkFocusOverlay, gtk_focus_overlay, GTK_TYPE_INSPECTOR_OVERLAY) + +static void +draw_focus_location (GtkWidget *widget, + GtkWidget *child, + GtkSnapshot *snapshot, + const char *color, + const char *text) +{ + GtkAllocation allocation; + graphene_rect_t bounds; + PangoLayout *layout; + PangoRectangle rect; + GskRoundedRect rrect; + int x, y; + GdkRGBA rgba; + + if (child == NULL) + return; + + gdk_rgba_parse (&rgba, color); + + gtk_widget_get_allocation (child, &allocation); + + gtk_snapshot_save (snapshot); + + gtk_widget_translate_coordinates (child, widget, 0, 0, &x, &y); + gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x, y)); + + gtk_snapshot_push_debug (snapshot, "Widget focus debugging"); + + layout = gtk_widget_create_pango_layout (widget, NULL); + pango_layout_set_markup (layout, text, -1); + pango_layout_get_extents (layout, NULL, &rect); + graphene_rect_init (&bounds, + 0, 0, + rect.width / PANGO_SCALE + 20, + rect.height / PANGO_SCALE + 20); + gsk_rounded_rect_init_from_rect (&rrect, &bounds, 10.0f); + + gtk_snapshot_push_rounded_clip (snapshot, &rrect); + + gtk_snapshot_append_color (snapshot, &rgba, &bounds); + + gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (10, 10)); + gtk_snapshot_append_layout (snapshot, layout, &(GdkRGBA) { 1, 1, 1, 1 }); + g_object_unref (layout); + + gtk_snapshot_pop (snapshot); + gtk_snapshot_pop (snapshot); + + gtk_snapshot_restore (snapshot); +} + +static void +draw_focus_locations (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + GtkWidget *focus; + GtkWidget *next; + int i; + char text[20]; + + if (!gtk_widget_get_mapped (widget)) + return; + + focus = gtk_root_get_focus (GTK_ROOT (widget)); + if (focus == NULL) + return; + + draw_focus_location (widget, focus, snapshot, "rgba(0,0,255,0.4)", "Focus"); + + next = focus; + for (i = 1; i < 4; i++) + { + g_snprintf (text, 20, "⇥ %d", i); + next = gtk_widget_get_next_focus (next, GTK_DIR_TAB_FORWARD); + if (!next) + break; + draw_focus_location (widget, next, snapshot, "rgba(255,0,255,0.4)", text); + } + + next = focus; + for (i = 1; i < 4; i++) + { + g_snprintf (text, 20, "⇤ %d", i); + next = gtk_widget_get_next_focus (next, GTK_DIR_TAB_BACKWARD); + if (!next) + break; + draw_focus_location (widget, next, snapshot, "rgba(255,0,255,0.4)", text); + } +} + +static void +gtk_focus_overlay_snapshot (GtkInspectorOverlay *overlay, + GtkSnapshot *snapshot, + GskRenderNode *node, + GtkWidget *widget) +{ + draw_focus_locations (widget, snapshot); +} + +static void +gtk_focus_overlay_init (GtkFocusOverlay *self) +{ + +} + +static void +gtk_focus_overlay_class_init (GtkFocusOverlayClass *klass) +{ + GtkInspectorOverlayClass *overlay_class = (GtkInspectorOverlayClass *)klass; + + overlay_class->snapshot = gtk_focus_overlay_snapshot; +} + +GtkInspectorOverlay * +gtk_focus_overlay_new (void) +{ + return g_object_new (GTK_TYPE_FOCUS_OVERLAY, NULL); +} diff --git a/gtk/inspector/focusoverlay.h b/gtk/inspector/focusoverlay.h new file mode 100644 index 00000000000..429a3ef94d0 --- /dev/null +++ b/gtk/inspector/focusoverlay.h @@ -0,0 +1,19 @@ + + +#ifndef __GTK_FOCUS_OVERLAY_H__ +#define __GTK_FOCUS_OVERLAY_H__ + +#include "inspectoroverlay.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_FOCUS_OVERLAY (gtk_focus_overlay_get_type ()) +G_DECLARE_FINAL_TYPE (GtkFocusOverlay, gtk_focus_overlay, GTK, FOCUS_OVERLAY, GtkInspectorOverlay) + +GtkInspectorOverlay * gtk_focus_overlay_new (void); + +G_END_DECLS + + + +#endif diff --git a/gtk/inspector/meson.build b/gtk/inspector/meson.build index a134f8f278d..92bd8020d1d 100644 --- a/gtk/inspector/meson.build +++ b/gtk/inspector/meson.build @@ -6,6 +6,7 @@ inspector_sources = files( 'css-editor.c', 'css-node-tree.c', 'data-list.c', + 'focusoverlay.c', 'fpsoverlay.c', 'general.c', 'graphdata.c', diff --git a/gtk/inspector/visual.c b/gtk/inspector/visual.c index 5a21d1d316e..930c51659fd 100644 --- a/gtk/inspector/visual.c +++ b/gtk/inspector/visual.c @@ -20,6 +20,7 @@ #include "visual.h" +#include "focusoverlay.h" #include "fpsoverlay.h" #include "updatesoverlay.h" #include "layoutoverlay.h" @@ -83,6 +84,7 @@ struct _GtkInspectorVisualPrivate GtkInspectorOverlay *fps_overlay; GtkInspectorOverlay *updates_overlay; GtkInspectorOverlay *layout_overlay; + GtkInspectorOverlay *focus_overlay; }; G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorVisual, gtk_inspector_visual, GTK_TYPE_SCROLLED_WINDOW) @@ -298,6 +300,41 @@ updates_activate (GtkSwitch *sw, redraw_everything (); } +static void +focus_activate (GtkSwitch *sw, + GParamSpec *pspec, + GtkInspectorVisual *vis) +{ + GtkInspectorVisualPrivate *priv = vis->priv; + GtkInspectorWindow *iw; + gboolean focus; + + focus = gtk_switch_get_active (sw); + iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (vis))); + if (iw == NULL) + return; + + if (focus) + { + if (priv->focus_overlay == NULL) + { + priv->focus_overlay = gtk_focus_overlay_new (); + gtk_inspector_window_add_overlay (iw, priv->focus_overlay); + g_object_unref (priv->focus_overlay); + } + } + else + { + if (priv->focus_overlay != NULL) + { + gtk_inspector_window_remove_overlay (iw, priv->focus_overlay); + priv->focus_overlay = NULL; + } + } + + redraw_everything (); +} + static void baselines_activate (GtkSwitch *sw) { @@ -952,6 +989,7 @@ gtk_inspector_visual_class_init (GtkInspectorVisualClass *klass) gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_entry); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_adjustment); + gtk_widget_class_bind_template_callback (widget_class, focus_activate); gtk_widget_class_bind_template_callback (widget_class, fps_activate); gtk_widget_class_bind_template_callback (widget_class, updates_activate); gtk_widget_class_bind_template_callback (widget_class, direction_changed); diff --git a/gtk/inspector/visual.ui b/gtk/inspector/visual.ui index 1b143fd291d..a9948e7d61b 100644 --- a/gtk/inspector/visual.ui +++ b/gtk/inspector/visual.ui @@ -371,6 +371,33 @@ none + + + 0 + + + 10 + 40 + + + Show focus overlay + start + baseline + 0.0 + + + + + end + baseline + 1 + + + + + + + 0 -- GitLab From 71b9821bcf565a8dd82ff7603bc59568aa7e8a68 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 20:18:33 -0500 Subject: [PATCH 14/70] window: Make focus wrap again This is the traditional GTK behavior: wrap around when the focus reaches the end of the toplevel's focus chain. --- gtk/gtkwindow.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 36ac49ff956..00838023610 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -767,6 +767,27 @@ gtk_window_pick (GtkWidget *widget, return GTK_WIDGET_CLASS (gtk_window_parent_class)->pick (widget, x, y); } +static GtkWidget * +gtk_window_next_focus_child (GtkWidget *widget, + GtkWidget *child, + GtkDirectionType direction) +{ + GtkWidget *next; + + next = GTK_WIDGET_CLASS (gtk_window_parent_class)->next_focus_child (widget, child, direction); + if (next) + return next; + + switch ((int)direction) + { + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_TAB_BACKWARD: + return GTK_WIDGET_CLASS (gtk_window_parent_class)->next_focus_child (widget, NULL, direction); + default: + return NULL; + } +} + static void gtk_window_class_init (GtkWindowClass *klass) { @@ -805,6 +826,7 @@ gtk_window_class_init (GtkWindowClass *klass) widget_class->style_updated = gtk_window_style_updated; widget_class->snapshot = gtk_window_snapshot; widget_class->pick = gtk_window_pick; + widget_class->next_focus_child = gtk_window_next_focus_child; container_class->add = gtk_window_add; container_class->remove = gtk_window_remove; -- GitLab From 4a56b1883b3333d707be8b660db4551e52d5f6df Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 20:32:11 -0500 Subject: [PATCH 15/70] Exclude non-drawable widgets from focus This was showing up as confusion between palette and editor in the color chooser. --- gtk/gtkwidgetfocus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c index 61c02395085..81dea61e0f7 100644 --- a/gtk/gtkwidgetfocus.c +++ b/gtk/gtkwidgetfocus.c @@ -423,6 +423,7 @@ gtk_widget_focus_sort (GtkWidget *widget, child = _gtk_widget_get_next_sibling (child)) { if (_gtk_widget_get_realized (child) && + _gtk_widget_is_drawable (child) && gtk_widget_get_sensitive (child)) g_ptr_array_add (focus_order, child); } -- GitLab From 8e3f3cfa07c528b9205a19c16a89f17a91c81709 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 20:53:44 -0500 Subject: [PATCH 16/70] Remove gtk_widget_focus_move This is no longer used. --- gtk/gtkwidgetfocus.c | 37 ------------------------------------- gtk/gtkwidgetprivate.h | 2 -- 2 files changed, 39 deletions(-) diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c index 81dea61e0f7..c835b0f64d7 100644 --- a/gtk/gtkwidgetfocus.c +++ b/gtk/gtkwidgetfocus.c @@ -449,43 +449,6 @@ gtk_widget_focus_sort (GtkWidget *widget, } } - -gboolean -gtk_widget_focus_move (GtkWidget *widget, - GtkDirectionType direction) -{ - GPtrArray *focus_order; - GtkWidget *focus_child = gtk_widget_get_focus_child (widget); - int i; - gboolean ret = FALSE; - - focus_order = g_ptr_array_new (); - gtk_widget_focus_sort (widget, direction, focus_order); - - for (i = 0; i < focus_order->len && !ret; i++) - { - GtkWidget *child = g_ptr_array_index (focus_order, i); - - if (focus_child) - { - if (focus_child == child) - { - focus_child = NULL; - ret = gtk_widget_child_focus (child, direction); - } - } - else if (_gtk_widget_is_drawable (child) && - gtk_widget_is_ancestor (child, widget)) - { - ret = gtk_widget_child_focus (child, direction); - } - } - - g_ptr_array_unref (focus_order); - - return ret; -} - /** * gtk_widget_get_next_focus: * @widget: a #GtkWidget diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index 3638e40356d..cb30b774382 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -316,8 +316,6 @@ GtkWidget *gtk_widget_get_focus_child (GtkWidget void gtk_widget_focus_sort (GtkWidget *widget, GtkDirectionType direction, GPtrArray *focus_order); -gboolean gtk_widget_focus_move (GtkWidget *widget, - GtkDirectionType direction); GtkWidget * gtk_widget_next_focus_child (GtkWidget *widget, GtkWidget *child, GtkDirectionType direction); -- GitLab From 39946239691acd86b62b1b063670a39454415b14 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 2 Mar 2019 21:03:38 -0500 Subject: [PATCH 17/70] Remove gtk_widget_send_focus_change from public API It should be dropped altogether. Until that happens, keep it private. --- gtk/gtkwidget.h | 3 --- gtk/gtkwidgetprivate.h | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 11ba57036a6..1d41c2f6171 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -454,9 +454,6 @@ gboolean gtk_widget_mnemonic_activate (GtkWidget *widget, GDK_AVAILABLE_IN_ALL gboolean gtk_widget_event (GtkWidget *widget, const GdkEvent *event); -GDK_AVAILABLE_IN_ALL -gboolean gtk_widget_send_focus_change (GtkWidget *widget, - GdkEvent *event); GDK_AVAILABLE_IN_ALL gboolean gtk_widget_activate (GtkWidget *widget); diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index cb30b774382..9da9ec81636 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -335,6 +335,10 @@ gboolean gtk_widget_run_controllers (GtkWidget const GdkEvent *event, GtkPropagationPhase phase); +gboolean gtk_widget_send_focus_change (GtkWidget *widget, + GdkEvent *event); + + /* inline getters */ static inline GtkWidget * -- GitLab From 0b78524ccda2b0181eec7510346c11a9cbcf38f8 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 00:04:54 -0500 Subject: [PATCH 18/70] Remove ::focus implementations We no longer call the ::focus vfunc, so remove all existing implementations. This needs careful review to determine how to bring the functionality in these handlers back. --- gtk/gtkflowbox.c | 157 ---------------------------- gtk/gtklabel.c | 146 -------------------------- gtk/gtklistbox.c | 198 ----------------------------------- gtk/gtkmenu.c | 16 +-- gtk/gtknotebook.c | 224 ---------------------------------------- gtk/gtkpaned.c | 21 ---- gtk/gtkpopover.c | 34 ------ gtk/gtkradiobutton.c | 96 ----------------- gtk/gtkscrolledwindow.c | 42 -------- gtk/gtktextview.c | 36 ------- gtk/gtktoolbar.c | 40 ------- gtk/gtktreeview.c | 220 --------------------------------------- gtk/gtkwidget.c | 47 +-------- gtk/gtkwidget.h | 4 +- 14 files changed, 14 insertions(+), 1267 deletions(-) diff --git a/gtk/gtkflowbox.c b/gtk/gtkflowbox.c index 65c491fe4b9..b40ffca25ac 100644 --- a/gtk/gtkflowbox.c +++ b/gtk/gtkflowbox.c @@ -303,92 +303,8 @@ gtk_flow_box_child_get_box (GtkFlowBoxChild *child) return NULL; } -static void -gtk_flow_box_child_set_focus (GtkFlowBoxChild *child) -{ - GtkFlowBox *box = gtk_flow_box_child_get_box (child); - gboolean modify; - gboolean extend; - - get_current_selection_modifiers (GTK_WIDGET (box), &modify, &extend); - - if (modify) - gtk_flow_box_update_cursor (box, child); - else - gtk_flow_box_update_selection (box, child, FALSE, FALSE); -} - /* GtkWidget implementation {{{2 */ -static gboolean -gtk_flow_box_child_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - gboolean had_focus = FALSE; - GtkWidget *child; - - child = gtk_bin_get_child (GTK_BIN (widget)); - - /* Without "can-focus" flag try to pass the focus to the child immediately */ - if (!gtk_widget_get_can_focus (widget)) - { - if (child) - { - if (gtk_widget_child_focus (child, direction)) - { - GtkFlowBox *box; - box = gtk_flow_box_child_get_box (GTK_FLOW_BOX_CHILD (widget)); - if (box) - gtk_flow_box_update_cursor (box, GTK_FLOW_BOX_CHILD (widget)); - return TRUE; - } - } - return FALSE; - } - - g_object_get (widget, "has-focus", &had_focus, NULL); - if (had_focus) - { - /* If on row, going right, enter into possible container */ - if (child && - (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD)) - { - if (gtk_widget_child_focus (GTK_WIDGET (child), direction)) - return TRUE; - } - - return FALSE; - } - else if (gtk_widget_get_focus_child (widget) != NULL) - { - /* Child has focus, always navigate inside it first */ - if (gtk_widget_child_focus (child, direction)) - return TRUE; - - /* If exiting child container to the left, select child */ - if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD) - { - gtk_flow_box_child_set_focus (GTK_FLOW_BOX_CHILD (widget)); - return TRUE; - } - - return FALSE; - } - else - { - /* If coming from the left, enter into possible container */ - if (child && - (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)) - { - if (gtk_widget_child_focus (child, direction)) - return TRUE; - } - - gtk_flow_box_child_set_focus (GTK_FLOW_BOX_CHILD (widget)); - return TRUE; - } -} - static void gtk_flow_box_child_activate (GtkFlowBoxChild *child) { @@ -422,7 +338,6 @@ gtk_flow_box_child_class_init (GtkFlowBoxChildClass *class) GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); widget_class->get_request_mode = gtk_flow_box_child_get_request_mode; - widget_class->focus = gtk_flow_box_child_focus; class->activate = gtk_flow_box_child_activate; @@ -2893,77 +2808,6 @@ gtk_flow_box_child_type (GtkContainer *container) /* Keynav {{{2 */ -static gboolean -gtk_flow_box_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkFlowBox *box = GTK_FLOW_BOX (widget); - GtkWidget *focus_child; - GSequenceIter *iter; - GtkFlowBoxChild *next_focus_child; - - /* Without "can-focus" flag fall back to the default behavior immediately */ - if (!gtk_widget_get_can_focus (widget)) - { - return GTK_WIDGET_CLASS (gtk_flow_box_parent_class)->focus (widget, direction); - } - - focus_child = gtk_widget_get_focus_child (widget); - next_focus_child = NULL; - - if (focus_child != NULL) - { - if (gtk_widget_child_focus (focus_child, direction)) - return TRUE; - - iter = CHILD_PRIV (focus_child)->iter; - - if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD) - iter = gtk_flow_box_get_previous_focusable (box, iter); - else if (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD) - iter = gtk_flow_box_get_next_focusable (box, iter); - else if (direction == GTK_DIR_UP) - iter = gtk_flow_box_get_above_focusable (box, iter); - else if (direction == GTK_DIR_DOWN) - iter = gtk_flow_box_get_below_focusable (box, iter); - - if (iter != NULL) - next_focus_child = g_sequence_get (iter); - } - else - { - if (BOX_PRIV (box)->selected_child) - next_focus_child = BOX_PRIV (box)->selected_child; - else - { - if (direction == GTK_DIR_UP || direction == GTK_DIR_TAB_BACKWARD) - iter = gtk_flow_box_get_last_focusable (box); - else - iter = gtk_flow_box_get_first_focusable (box); - - if (iter != NULL) - next_focus_child = g_sequence_get (iter); - } - } - - if (next_focus_child == NULL) - { - if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN || - direction == GTK_DIR_LEFT || direction == GTK_DIR_RIGHT) - { - if (gtk_widget_keynav_failed (GTK_WIDGET (box), direction)) - return TRUE; - } - - return FALSE; - } - - if (gtk_widget_child_focus (GTK_WIDGET (next_focus_child), direction)) - return TRUE; - - return TRUE; -} - static void gtk_flow_box_add_move_binding (GtkBindingSet *binding_set, guint keyval, @@ -3373,7 +3217,6 @@ gtk_flow_box_class_init (GtkFlowBoxClass *class) widget_class->size_allocate = gtk_flow_box_size_allocate; widget_class->unmap = gtk_flow_box_unmap; - widget_class->focus = gtk_flow_box_focus; widget_class->snapshot = gtk_flow_box_snapshot; widget_class->get_request_mode = gtk_flow_box_get_request_mode; widget_class->measure = gtk_flow_box_measure; diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index e3be68e02e1..e8b3355c922 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -413,8 +413,6 @@ static void gtk_label_state_flags_changed (GtkWidget *widget, static void gtk_label_style_updated (GtkWidget *widget); static void gtk_label_snapshot (GtkWidget *widget, GtkSnapshot *snapshot); -static gboolean gtk_label_focus (GtkWidget *widget, - GtkDirectionType direction); static void gtk_label_realize (GtkWidget *widget); static void gtk_label_unrealize (GtkWidget *widget); @@ -612,7 +610,6 @@ gtk_label_class_init (GtkLabelClass *class) widget_class->drag_data_get = gtk_label_drag_data_get; widget_class->grab_focus = gtk_label_grab_focus; widget_class->popup_menu = gtk_label_popup_menu; - widget_class->focus = gtk_label_focus; widget_class->get_request_mode = gtk_label_get_request_mode; widget_class->measure = gtk_label_measure; @@ -4298,149 +4295,6 @@ gtk_label_grab_focus (GtkWidget *widget) } } -static gboolean -gtk_label_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkLabel *label = GTK_LABEL (widget); - GtkLabelPrivate *priv = gtk_label_get_instance_private (label); - GtkLabelSelectionInfo *info = priv->select_info; - GtkLabelLink *focus_link; - GList *l; - - if (!gtk_widget_is_focus (widget)) - { - gtk_widget_grab_focus (widget); - if (info) - { - focus_link = gtk_label_get_focus_link (label); - if (focus_link && direction == GTK_DIR_TAB_BACKWARD) - { - for (l = g_list_last (info->links); l; l = l->prev) - { - focus_link = l->data; - if (!range_is_in_ellipsis (label, focus_link->start, focus_link->end)) - { - info->selection_anchor = focus_link->start; - info->selection_end = focus_link->start; - _gtk_label_accessible_focus_link_changed (label); - } - } - } - - return TRUE; - } - - return FALSE; - } - - if (!info) - return FALSE; - - if (info->selectable) - { - gint index; - - if (info->selection_anchor != info->selection_end) - goto out; - - index = info->selection_anchor; - - if (direction == GTK_DIR_TAB_FORWARD) - for (l = info->links; l; l = l->next) - { - GtkLabelLink *link = l->data; - - if (link->start > index) - { - if (!range_is_in_ellipsis (label, link->start, link->end)) - { - gtk_label_select_region_index (label, link->start, link->start); - _gtk_label_accessible_focus_link_changed (label); - return TRUE; - } - } - } - else if (direction == GTK_DIR_TAB_BACKWARD) - for (l = g_list_last (info->links); l; l = l->prev) - { - GtkLabelLink *link = l->data; - - if (link->end < index) - { - if (!range_is_in_ellipsis (label, link->start, link->end)) - { - gtk_label_select_region_index (label, link->start, link->start); - _gtk_label_accessible_focus_link_changed (label); - return TRUE; - } - } - } - - goto out; - } - else - { - focus_link = gtk_label_get_focus_link (label); - switch (direction) - { - case GTK_DIR_TAB_FORWARD: - if (focus_link) - { - l = g_list_find (info->links, focus_link); - l = l->next; - } - else - l = info->links; - for (; l; l = l->next) - { - GtkLabelLink *link = l->data; - if (!range_is_in_ellipsis (label, link->start, link->end)) - break; - } - break; - - case GTK_DIR_TAB_BACKWARD: - if (focus_link) - { - l = g_list_find (info->links, focus_link); - l = l->prev; - } - else - l = g_list_last (info->links); - for (; l; l = l->prev) - { - GtkLabelLink *link = l->data; - if (!range_is_in_ellipsis (label, link->start, link->end)) - break; - } - break; - - default: - case GTK_DIR_UP: - case GTK_DIR_DOWN: - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - goto out; - } - - if (l) - { - focus_link = l->data; - info->selection_anchor = focus_link->start; - info->selection_end = focus_link->start; - _gtk_label_accessible_focus_link_changed (label); - gtk_widget_queue_draw (widget); - - return TRUE; - } - } - -out: - - return FALSE; -} - static void gtk_label_multipress_gesture_pressed (GtkGestureMultiPress *gesture, gint n_press, diff --git a/gtk/gtklistbox.c b/gtk/gtklistbox.c index f6ccfb1931b..b3edb45fec1 100644 --- a/gtk/gtklistbox.c +++ b/gtk/gtklistbox.c @@ -208,8 +208,6 @@ static void gtk_list_box_update_cursor (GtkListBo GtkListBoxRow *row, gboolean grab_focus); static void gtk_list_box_show (GtkWidget *widget); -static gboolean gtk_list_box_focus (GtkWidget *widget, - GtkDirectionType direction); static GSequenceIter* gtk_list_box_get_previous_visible (GtkListBox *box, GSequenceIter *iter); static GtkListBoxRow *gtk_list_box_get_first_focusable (GtkListBox *box); @@ -407,7 +405,6 @@ gtk_list_box_class_init (GtkListBoxClass *klass) object_class->set_property = gtk_list_box_set_property; object_class->finalize = gtk_list_box_finalize; widget_class->show = gtk_list_box_show; - widget_class->focus = gtk_list_box_focus; widget_class->compute_expand = gtk_list_box_compute_expand; widget_class->get_request_mode = gtk_list_box_get_request_mode; widget_class->measure = gtk_list_box_measure; @@ -1870,129 +1867,6 @@ gtk_list_box_show (GtkWidget *widget) GTK_WIDGET_CLASS (gtk_list_box_parent_class)->show (widget); } -static gboolean -gtk_list_box_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkListBox *box = GTK_LIST_BOX (widget); - GtkListBoxPrivate *priv = BOX_PRIV (box); - GtkWidget *focus_child; - GtkListBoxRow *next_focus_row; - GtkWidget *row; - GtkWidget *header; - - focus_child = gtk_widget_get_focus_child (widget); - - next_focus_row = NULL; - if (focus_child != NULL) - { - GSequenceIter *i; - - if (gtk_widget_child_focus (focus_child, direction)) - return TRUE; - - if (direction == GTK_DIR_UP || direction == GTK_DIR_TAB_BACKWARD) - { - if (GTK_IS_LIST_BOX_ROW (focus_child)) - { - header = ROW_PRIV (GTK_LIST_BOX_ROW (focus_child))->header; - if (header && gtk_widget_child_focus (header, direction)) - return TRUE; - } - - if (GTK_IS_LIST_BOX_ROW (focus_child)) - row = focus_child; - else - row = g_hash_table_lookup (priv->header_hash, focus_child); - - if (GTK_IS_LIST_BOX_ROW (row)) - i = gtk_list_box_get_previous_visible (box, ROW_PRIV (GTK_LIST_BOX_ROW (row))->iter); - else - i = NULL; - - while (i != NULL) - { - if (gtk_widget_get_sensitive (g_sequence_get (i))) - { - next_focus_row = g_sequence_get (i); - break; - } - - i = gtk_list_box_get_previous_visible (box, i); - } - } - else if (direction == GTK_DIR_DOWN || direction == GTK_DIR_TAB_FORWARD) - { - if (GTK_IS_LIST_BOX_ROW (focus_child)) - i = gtk_list_box_get_next_visible (box, ROW_PRIV (GTK_LIST_BOX_ROW (focus_child))->iter); - else - { - row = g_hash_table_lookup (priv->header_hash, focus_child); - if (GTK_IS_LIST_BOX_ROW (row)) - i = ROW_PRIV (GTK_LIST_BOX_ROW (row))->iter; - else - i = NULL; - } - - while (!g_sequence_iter_is_end (i)) - { - if (gtk_widget_get_sensitive (g_sequence_get (i))) - { - next_focus_row = g_sequence_get (i); - break; - } - - i = gtk_list_box_get_next_visible (box, i); - } - } - } - else - { - /* No current focus row */ - switch (direction) - { - case GTK_DIR_UP: - case GTK_DIR_TAB_BACKWARD: - next_focus_row = priv->selected_row; - if (next_focus_row == NULL) - next_focus_row = gtk_list_box_get_last_focusable (box); - break; - case GTK_DIR_DOWN: - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - default: - next_focus_row = priv->selected_row; - if (next_focus_row == NULL) - next_focus_row = gtk_list_box_get_first_focusable (box); - break; - } - } - - if (next_focus_row == NULL) - { - if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN) - { - if (gtk_widget_keynav_failed (GTK_WIDGET (box), direction)) - return TRUE; - } - - return FALSE; - } - - if (direction == GTK_DIR_DOWN || direction == GTK_DIR_TAB_FORWARD) - { - header = ROW_PRIV (next_focus_row)->header; - if (header && gtk_widget_child_focus (header, direction)) - return TRUE; - } - - if (gtk_widget_child_focus (GTK_WIDGET (next_focus_row), direction)) - return TRUE; - - return FALSE; -} - static void list_box_add_visible_rows (GtkListBox *box, gint n) @@ -2882,77 +2756,6 @@ gtk_list_box_row_new (void) return g_object_new (GTK_TYPE_LIST_BOX_ROW, NULL); } -static void -gtk_list_box_row_set_focus (GtkListBoxRow *row) -{ - GtkListBox *box = gtk_list_box_row_get_box (row); - gboolean modify; - gboolean extend; - - if (!box) - return; - - get_current_selection_modifiers (GTK_WIDGET (row), &modify, &extend); - - if (modify) - gtk_list_box_update_cursor (box, row, TRUE); - else - gtk_list_box_update_selection (box, row, FALSE, FALSE); -} - -static gboolean -gtk_list_box_row_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget); - gboolean had_focus = FALSE; - GtkWidget *child; - - child = gtk_bin_get_child (GTK_BIN (widget)); - - g_object_get (widget, "has-focus", &had_focus, NULL); - if (had_focus) - { - /* If on row, going right, enter into possible container */ - if (child && - (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD)) - { - if (gtk_widget_child_focus (GTK_WIDGET (child), direction)) - return TRUE; - } - - return FALSE; - } - else if (gtk_widget_get_focus_child (widget) != NULL) - { - /* Child has focus, always navigate inside it first */ - if (gtk_widget_child_focus (gtk_widget_get_focus_child (widget), direction)) - return TRUE; - - /* If exiting child container to the left, select row */ - if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD) - { - gtk_list_box_row_set_focus (row); - return TRUE; - } - - return FALSE; - } - else - { - /* If coming from the left, enter into possible container */ - if (child && - (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)) - { - if (gtk_widget_child_focus (child, direction)) - return TRUE; - } - - gtk_list_box_row_set_focus (row); - return TRUE; - } -} - static void gtk_list_box_row_activate (GtkListBoxRow *row) { @@ -3384,7 +3187,6 @@ gtk_list_box_row_class_init (GtkListBoxRowClass *klass) widget_class->show = gtk_list_box_row_show; widget_class->hide = gtk_list_box_row_hide; - widget_class->focus = gtk_list_box_row_focus; widget_class->grab_focus = gtk_list_box_row_grab_focus; klass->activate = gtk_list_box_row_activate; diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index 247dd4f12ee..a3a22cbcb5c 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -251,8 +251,9 @@ static void gtk_menu_handle_scrolling (GtkMenu *menu, gint event_y, gboolean enter, gboolean motion); -static gboolean gtk_menu_focus (GtkWidget *widget, - GtkDirectionType direction); +static GtkWidget *gtk_menu_next_focus_child (GtkWidget *widget, + GtkWidget *child, + GtkDirectionType direction); static gint gtk_menu_get_popup_delay (GtkMenuShell *menu_shell); static void gtk_menu_move_current (GtkMenuShell *menu_shell, GtkMenuDirectionType direction); @@ -492,7 +493,7 @@ gtk_menu_class_init (GtkMenuClass *class) widget_class->size_allocate = gtk_menu_size_allocate; widget_class->show = gtk_menu_show; widget_class->snapshot = gtk_menu_snapshot; - widget_class->focus = gtk_menu_focus; + widget_class->next_focus_child = gtk_menu_next_focus_child; widget_class->can_activate_accel = gtk_menu_real_can_activate_accel; widget_class->grab_notify = gtk_menu_grab_notify; widget_class->measure = gtk_menu_measure; @@ -2254,12 +2255,13 @@ gtk_menu_realize (GtkWidget *widget) GTK_MENU_SHELL (widget)->priv->active_menu_item); } -static gboolean -gtk_menu_focus (GtkWidget *widget, - GtkDirectionType direction) +static GtkWidget * +gtk_menu_next_focus_child (GtkWidget *widget, + GtkWidget *child, + GtkDirectionType direction) { /* A menu or its menu items cannot have focus */ - return FALSE; + return NULL; } static GdkSurface * diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index eb0ec8a859f..c7fa88bc322 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -643,8 +643,6 @@ static void gtk_notebook_grab_notify (GtkWidget *widget, gboolean was_grabbed); static void gtk_notebook_state_flags_changed (GtkWidget *widget, GtkStateFlags previous_state); -static gboolean gtk_notebook_focus (GtkWidget *widget, - GtkDirectionType direction); /*** Drag and drop Methods ***/ static void gtk_notebook_drag_begin (GtkWidget *widget, @@ -919,7 +917,6 @@ gtk_notebook_class_init (GtkNotebookClass *class) widget_class->popup_menu = gtk_notebook_popup_menu; widget_class->grab_notify = gtk_notebook_grab_notify; widget_class->state_flags_changed = gtk_notebook_state_flags_changed; - widget_class->focus = gtk_notebook_focus; widget_class->drag_begin = gtk_notebook_drag_begin; widget_class->drag_end = gtk_notebook_drag_end; widget_class->drag_motion = gtk_notebook_drag_motion; @@ -3468,7 +3465,6 @@ gtk_notebook_drag_data_received (GtkWidget *widget, * * gtk_notebook_add * gtk_notebook_remove - * gtk_notebook_focus * gtk_notebook_child_type * gtk_notebook_forall */ @@ -3543,30 +3539,6 @@ focus_tabs_in (GtkNotebook *notebook) return FALSE; } -static gboolean -focus_tabs_move (GtkNotebook *notebook, - GtkDirectionType direction, - gint search_direction) -{ - GtkNotebookPrivate *priv = notebook->priv; - GList *new_page; - - new_page = gtk_notebook_search_page (notebook, priv->focus_tab, - search_direction, TRUE); - if (!new_page) - { - new_page = gtk_notebook_search_page (notebook, NULL, - search_direction, TRUE); - } - - if (new_page) - gtk_notebook_switch_focus_tab (notebook, new_page); - else - gtk_widget_error_bell (GTK_WIDGET (notebook)); - - return TRUE; -} - static gboolean focus_child_in (GtkNotebook *notebook, GtkDirectionType direction) @@ -3579,202 +3551,6 @@ focus_child_in (GtkNotebook *notebook, return FALSE; } -static gboolean -focus_action_in (GtkNotebook *notebook, - gint action, - GtkDirectionType direction) -{ - GtkNotebookPrivate *priv = notebook->priv; - - if (priv->action_widget[action] && - gtk_widget_get_visible (priv->action_widget[action])) - return gtk_widget_child_focus (priv->action_widget[action], direction); - else - return FALSE; -} - -/* Focus in the notebook can either be on the pages, or on - * the tabs or on the action_widgets. - */ -static gboolean -gtk_notebook_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkNotebook *notebook = GTK_NOTEBOOK (widget); - GtkNotebookPrivate *priv = notebook->priv; - GtkWidget *old_focus_child; - GtkDirectionType effective_direction; - gint first_action; - gint last_action; - - gboolean widget_is_focus; - - if (priv->tab_pos == GTK_POS_TOP || - priv->tab_pos == GTK_POS_LEFT) - { - first_action = ACTION_WIDGET_START; - last_action = ACTION_WIDGET_END; - } - else - { - first_action = ACTION_WIDGET_END; - last_action = ACTION_WIDGET_START; - } - - if (priv->focus_out) - { - priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */ - return FALSE; - } - - widget_is_focus = gtk_widget_is_focus (widget); - old_focus_child = gtk_widget_get_focus_child (widget); - - effective_direction = get_effective_direction (notebook, direction); - - if (old_focus_child) /* Focus on page child or action widget */ - { - if (gtk_widget_child_focus (old_focus_child, direction)) - return TRUE; - - if (old_focus_child == priv->action_widget[ACTION_WIDGET_START]) - { - switch ((guint) effective_direction) - { - case GTK_DIR_DOWN: - return focus_child_in (notebook, GTK_DIR_TAB_FORWARD); - case GTK_DIR_RIGHT: - return focus_tabs_in (notebook); - case GTK_DIR_LEFT: - return FALSE; - case GTK_DIR_UP: - return FALSE; - default: - switch ((guint) direction) - { - case GTK_DIR_TAB_FORWARD: - if ((priv->tab_pos == GTK_POS_RIGHT || priv->tab_pos == GTK_POS_BOTTOM) && - focus_child_in (notebook, direction)) - return TRUE; - return focus_tabs_in (notebook); - case GTK_DIR_TAB_BACKWARD: - return FALSE; - default: - g_assert_not_reached (); - break; - } - } - } - else if (old_focus_child == priv->action_widget[ACTION_WIDGET_END]) - { - switch ((guint) effective_direction) - { - case GTK_DIR_DOWN: - return focus_child_in (notebook, GTK_DIR_TAB_FORWARD); - case GTK_DIR_RIGHT: - return FALSE; - case GTK_DIR_LEFT: - return focus_tabs_in (notebook); - case GTK_DIR_UP: - return FALSE; - default: - switch ((guint) direction) - { - case GTK_DIR_TAB_FORWARD: - return FALSE; - case GTK_DIR_TAB_BACKWARD: - if ((priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_LEFT) && - focus_child_in (notebook, direction)) - return TRUE; - return focus_tabs_in (notebook); - default: - g_assert_not_reached (); - break; - } - } - } - else - { - switch ((guint) effective_direction) - { - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_UP: - /* Focus onto the tabs */ - return focus_tabs_in (notebook); - case GTK_DIR_DOWN: - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - return FALSE; - case GTK_DIR_TAB_FORWARD: - return focus_action_in (notebook, last_action, direction); - default: - break; - } - } - } - else if (widget_is_focus) /* Focus was on tabs */ - { - switch ((guint) effective_direction) - { - case GTK_DIR_TAB_BACKWARD: - return focus_action_in (notebook, first_action, direction); - case GTK_DIR_UP: - return FALSE; - case GTK_DIR_TAB_FORWARD: - if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD)) - return TRUE; - return focus_action_in (notebook, last_action, direction); - case GTK_DIR_DOWN: - /* We use TAB_FORWARD rather than direction so that we focus a more - * predictable widget for the user; users may be using arrow focusing - * in this situation even if they don't usually use arrow focusing. - */ - return focus_child_in (notebook, GTK_DIR_TAB_FORWARD); - case GTK_DIR_LEFT: - return focus_tabs_move (notebook, direction, STEP_PREV); - case GTK_DIR_RIGHT: - return focus_tabs_move (notebook, direction, STEP_NEXT); - default: - break; - } - } - else /* Focus was not on widget */ - { - switch ((guint) effective_direction) - { - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_DOWN: - if (focus_action_in (notebook, first_action, direction)) - return TRUE; - if (focus_tabs_in (notebook)) - return TRUE; - if (focus_action_in (notebook, last_action, direction)) - return TRUE; - if (focus_child_in (notebook, direction)) - return TRUE; - return FALSE; - case GTK_DIR_TAB_BACKWARD: - if (focus_action_in (notebook, last_action, direction)) - return TRUE; - if (focus_child_in (notebook, direction)) - return TRUE; - if (focus_tabs_in (notebook)) - return TRUE; - if (focus_action_in (notebook, first_action, direction)) - return TRUE; - case GTK_DIR_UP: - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - return focus_child_in (notebook, direction); - default: - break; - } - } - - g_assert_not_reached (); - return FALSE; -} - static void gtk_notebook_forall (GtkContainer *container, GtkCallback callback, diff --git a/gtk/gtkpaned.c b/gtk/gtkpaned.c index abaa822d6be..24508b2c7e4 100644 --- a/gtk/gtkpaned.c +++ b/gtk/gtkpaned.c @@ -216,8 +216,6 @@ static void gtk_paned_size_allocate (GtkWidget *widget, int height, int baseline); static void gtk_paned_unrealize (GtkWidget *widget); -static gboolean gtk_paned_focus (GtkWidget *widget, - GtkDirectionType direction); static void gtk_paned_add (GtkContainer *container, GtkWidget *widget); static void gtk_paned_remove (GtkContainer *container, @@ -357,7 +355,6 @@ gtk_paned_class_init (GtkPanedClass *class) widget_class->measure = gtk_paned_measure; widget_class->size_allocate = gtk_paned_size_allocate; widget_class->unrealize = gtk_paned_unrealize; - widget_class->focus = gtk_paned_focus; widget_class->pick = gtk_paned_pick; container_class->add = gtk_paned_add; @@ -1485,24 +1482,6 @@ update_drag (GtkPaned *paned, gtk_paned_set_position (paned, size); } -static gboolean -gtk_paned_focus (GtkWidget *widget, - GtkDirectionType direction) - -{ - gboolean retval; - - /* This is a hack, but how can this be done without - * excessive cut-and-paste from gtkcontainer.c? - */ - - gtk_widget_set_can_focus (widget, FALSE); - retval = GTK_WIDGET_CLASS (gtk_paned_parent_class)->focus (widget, direction); - gtk_widget_set_can_focus (widget, TRUE); - - return retval; -} - /** * gtk_paned_new: * @orientation: the paned’s orientation. diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c index cfd697a234f..6f305ed0aa4 100644 --- a/gtk/gtkpopover.c +++ b/gtk/gtkpopover.c @@ -1455,39 +1455,6 @@ gtk_popover_grab_focus (GtkWidget *widget) gtk_widget_child_focus (child, GTK_DIR_TAB_FORWARD); } -static gboolean -gtk_popover_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkPopover *popover = GTK_POPOVER (widget); - GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); - - if (!priv->visible) - return FALSE; - - if (!GTK_WIDGET_CLASS (gtk_popover_parent_class)->focus (widget, direction)) - { - GtkWidget *focus; - - focus = gtk_window_get_focus (priv->window); - focus = gtk_widget_get_parent (focus); - - /* 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); - } - - return gtk_widget_child_focus (gtk_bin_get_child (GTK_BIN (widget)), - direction); - } - - return TRUE; -} - static void gtk_popover_show (GtkWidget *widget) { @@ -1582,7 +1549,6 @@ gtk_popover_class_init (GtkPopoverClass *klass) 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; diff --git a/gtk/gtkradiobutton.c b/gtk/gtkradiobutton.c index e87c0d7d496..64bda0def91 100644 --- a/gtk/gtkradiobutton.c +++ b/gtk/gtkradiobutton.c @@ -149,8 +149,6 @@ static GParamSpec *radio_button_props[LAST_PROP] = { NULL, }; static guint signals[N_SIGNALS] = { 0 }; static void gtk_radio_button_destroy (GtkWidget *widget); -static gboolean gtk_radio_button_focus (GtkWidget *widget, - GtkDirectionType direction); static void gtk_radio_button_clicked (GtkButton *button); static void gtk_radio_button_set_property (GObject *object, guint prop_id, @@ -192,7 +190,6 @@ gtk_radio_button_class_init (GtkRadioButtonClass *class) g_object_class_install_properties (gobject_class, LAST_PROP, radio_button_props); widget_class->destroy = gtk_radio_button_destroy; - widget_class->focus = gtk_radio_button_focus; button_class->clicked = gtk_radio_button_clicked; @@ -615,99 +612,6 @@ gtk_radio_button_destroy (GtkWidget *widget) GTK_WIDGET_CLASS (gtk_radio_button_parent_class)->destroy (widget); } -static gboolean -gtk_radio_button_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkRadioButton *radio_button = GTK_RADIO_BUTTON (widget); - GtkRadioButtonPrivate *priv = gtk_radio_button_get_instance_private (radio_button); - GSList *tmp_slist; - - /* Radio buttons with draw_indicator unset focus "normally", since - * they look like buttons to the user. - */ - if (!gtk_check_button_get_draw_indicator (GTK_CHECK_BUTTON (widget))) - return GTK_WIDGET_CLASS (gtk_radio_button_parent_class)->focus (widget, direction); - - if (gtk_widget_is_focus (widget)) - { - GPtrArray *child_array; - GtkWidget *new_focus = NULL; - GSList *l; - guint index; - gboolean found; - guint i; - - if (direction == GTK_DIR_TAB_FORWARD || - direction == GTK_DIR_TAB_BACKWARD) - return FALSE; - - child_array = g_ptr_array_sized_new (g_slist_length (priv->group)); - for (l = priv->group; l; l = l->next) - g_ptr_array_add (child_array, l->data); - - gtk_widget_focus_sort (widget, direction, child_array); - found = g_ptr_array_find (child_array, widget, &index); - - if (found) - { - /* Start at the *next* widget in the list */ - if (index < child_array->len - 1) - index ++; - } - else - { - /* Search from the start of the list */ - index = 0; - } - - for (i = index; i < child_array->len; i ++) - { - GtkWidget *child = g_ptr_array_index (child_array, i); - - if (gtk_widget_get_mapped (child) && gtk_widget_is_sensitive (child)) - { - new_focus = child; - break; - } - } - - - if (new_focus) - { - gtk_widget_grab_focus (new_focus); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (new_focus), TRUE); - } - - g_ptr_array_free (child_array, TRUE); - - return TRUE; - } - else - { - GtkRadioButton *selected_button = NULL; - - /* We accept the focus if, we don't have the focus and - * - we are the currently active button in the group - * - there is no currently active radio button. - */ - tmp_slist = priv->group; - while (tmp_slist) - { - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (tmp_slist->data)) && - gtk_widget_get_visible (tmp_slist->data)) - selected_button = tmp_slist->data; - tmp_slist = tmp_slist->next; - } - - if (selected_button && selected_button != radio_button) - return FALSE; - - gtk_widget_grab_focus (widget); - return TRUE; - } -} - static void gtk_radio_button_clicked (GtkButton *button) { diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index ea1b402c4b5..b6cc8c3179e 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -322,8 +322,6 @@ static void gtk_scrolled_window_size_allocate (GtkWidget *widge int width, int height, int baseline); -static gboolean gtk_scrolled_window_focus (GtkWidget *widget, - GtkDirectionType direction); static void gtk_scrolled_window_add (GtkContainer *container, GtkWidget *widget); static void gtk_scrolled_window_remove (GtkContainer *container, @@ -507,7 +505,6 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class) widget_class->destroy = gtk_scrolled_window_destroy; widget_class->snapshot = gtk_scrolled_window_snapshot; widget_class->size_allocate = gtk_scrolled_window_size_allocate; - widget_class->focus = gtk_scrolled_window_focus; widget_class->measure = gtk_scrolled_window_measure; widget_class->map = gtk_scrolled_window_map; widget_class->unmap = gtk_scrolled_window_unmap; @@ -3330,45 +3327,6 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window) (GDestroyNotify) kinetic_scroll_data_free); } -static gboolean -gtk_scrolled_window_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget); - GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window); - GtkWidget *child; - gboolean had_focus_child; - - had_focus_child = gtk_widget_get_focus_child (widget) != NULL; - - if (priv->focus_out) - { - priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */ - return FALSE; - } - - if (gtk_widget_is_focus (widget)) - return FALSE; - - /* We only put the scrolled window itself in the focus chain if it - * isn't possible to focus any children. - */ - child = gtk_bin_get_child (GTK_BIN (widget)); - if (child) - { - if (gtk_widget_child_focus (child, direction)) - return TRUE; - } - - if (!had_focus_child && gtk_widget_get_can_focus (widget)) - { - gtk_widget_grab_focus (widget); - return TRUE; - } - else - return FALSE; -} - static void gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment, gpointer data) diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index 6da0b597e92..040e122f570 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -407,8 +407,6 @@ static void gtk_text_view_motion (GtkEventController *controller, gpointer user_data); static void gtk_text_view_snapshot (GtkWidget *widget, GtkSnapshot *snapshot); -static gboolean gtk_text_view_focus (GtkWidget *widget, - GtkDirectionType direction); static void gtk_text_view_select_all (GtkWidget *widget, gboolean select); static gboolean get_middle_click_paste (GtkTextView *text_view); @@ -694,7 +692,6 @@ gtk_text_view_class_init (GtkTextViewClass *klass) widget_class->measure = gtk_text_view_measure; widget_class->size_allocate = gtk_text_view_size_allocate; widget_class->snapshot = gtk_text_view_snapshot; - widget_class->focus = gtk_text_view_focus; widget_class->drag_begin = gtk_text_view_drag_begin; widget_class->drag_end = gtk_text_view_drag_end; widget_class->drag_data_get = gtk_text_view_drag_data_get; @@ -5447,39 +5444,6 @@ gtk_text_view_snapshot (GtkWidget *widget, } } -static gboolean -gtk_text_view_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - gboolean result; - - if (!gtk_widget_is_focus (widget) && - gtk_widget_get_focus_child (widget) == NULL) - { - if (gtk_widget_get_can_focus (widget)) - { - gtk_widget_grab_focus (widget); - return TRUE; - } - - return FALSE; - } - else - { - gboolean can_focus; - /* - * Unset CAN_FOCUS flag so that gtk_container_focus() allows - * children to get the focus - */ - can_focus = gtk_widget_get_can_focus (widget); - gtk_widget_set_can_focus (widget, FALSE); - result = GTK_WIDGET_CLASS (gtk_text_view_parent_class)->focus (widget, direction); - gtk_widget_set_can_focus (widget, can_focus); - - return result; - } -} - /* * Container */ diff --git a/gtk/gtktoolbar.c b/gtk/gtktoolbar.c index 6c455cb3d4a..f7c41b87030 100644 --- a/gtk/gtktoolbar.c +++ b/gtk/gtktoolbar.c @@ -189,8 +189,6 @@ static void gtk_toolbar_size_allocate (GtkWidget *widget, int height, int baseline); static void gtk_toolbar_style_updated (GtkWidget *widget); -static gboolean gtk_toolbar_focus (GtkWidget *widget, - GtkDirectionType dir); static void gtk_toolbar_move_focus (GtkWidget *widget, GtkDirectionType dir); static void gtk_toolbar_display_changed (GtkWidget *widget, @@ -368,7 +366,6 @@ gtk_toolbar_class_init (GtkToolbarClass *klass) widget_class->measure = gtk_toolbar_measure; widget_class->size_allocate = gtk_toolbar_size_allocate; widget_class->style_updated = gtk_toolbar_style_updated; - widget_class->focus = gtk_toolbar_focus; gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_TOOL_BAR); @@ -1611,43 +1608,6 @@ gtk_toolbar_move_focus (GtkWidget *widget, g_list_free (children); } -/* The focus handler for the toolbar. It called when the user presses - * TAB or otherwise tries to focus the toolbar. - */ -static gboolean -gtk_toolbar_focus (GtkWidget *widget, - GtkDirectionType dir) -{ - GtkToolbar *toolbar = GTK_TOOLBAR (widget); - GList *children, *list; - gboolean result = FALSE; - - /* if focus is already somewhere inside the toolbar then return FALSE. - * The only way focus can stay inside the toolbar is when the user presses - * arrow keys or Ctrl TAB (both of which are handled by the - * gtk_toolbar_move_focus() keybinding function. - */ - if (gtk_widget_get_focus_child (widget)) - return FALSE; - - children = gtk_toolbar_list_children_in_focus_order (toolbar, dir); - - for (list = children; list != NULL; list = list->next) - { - GtkWidget *child = list->data; - - if (gtk_widget_get_mapped (child) && gtk_widget_child_focus (child, dir)) - { - result = TRUE; - break; - } - } - - g_list_free (children); - - return result; -} - static GtkSettings * toolbar_get_settings (GtkToolbar *toolbar) { diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index 1a4ed0488a0..c7dc3b1d3d3 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -616,8 +616,6 @@ static void gtk_tree_view_key_controller_key_released (GtkEventControllerKey static void gtk_tree_view_key_controller_focus_out (GtkEventControllerKey *key, GtkTreeView *tree_view); -static gint gtk_tree_view_focus (GtkWidget *widget, - GtkDirectionType direction); static void gtk_tree_view_grab_focus (GtkWidget *widget); static void gtk_tree_view_style_updated (GtkWidget *widget); @@ -985,7 +983,6 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) widget_class->drag_motion = gtk_tree_view_drag_motion; widget_class->drag_drop = gtk_tree_view_drag_drop; widget_class->drag_data_received = gtk_tree_view_drag_data_received; - widget_class->focus = gtk_tree_view_focus; widget_class->grab_focus = gtk_tree_view_grab_focus; widget_class->style_updated = gtk_tree_view_style_updated; @@ -7709,170 +7706,6 @@ gtk_tree_view_get_fixed_height_mode (GtkTreeView *tree_view) return tree_view->priv->fixed_height_mode; } -/* Returns TRUE if the focus is within the headers, after the focus operation is - * done - */ -static gboolean -gtk_tree_view_header_focus (GtkTreeView *tree_view, - GtkDirectionType dir, - gboolean clamp_column_visible) -{ - GtkTreeViewColumn *column; - GtkWidget *button; - GtkWidget *focus_child; - GList *last_column, *first_column; - GList *tmp_list; - gboolean rtl; - - if (! tree_view->priv->headers_visible) - return FALSE; - - focus_child = gtk_widget_get_focus_child (GTK_WIDGET (tree_view)); - - first_column = tree_view->priv->columns; - while (first_column) - { - column = GTK_TREE_VIEW_COLUMN (first_column->data); - button = gtk_tree_view_column_get_button (column); - - if (gtk_widget_get_can_focus (button) && - gtk_tree_view_column_get_visible (column) && - (gtk_tree_view_column_get_clickable (column) || - gtk_tree_view_column_get_reorderable (column))) - break; - first_column = first_column->next; - } - - /* No headers are visible, or are focusable. We can't focus in or out. - */ - if (first_column == NULL) - return FALSE; - - last_column = g_list_last (tree_view->priv->columns); - while (last_column) - { - column = GTK_TREE_VIEW_COLUMN (last_column->data); - button = gtk_tree_view_column_get_button (column); - - if (gtk_widget_get_can_focus (button) && - gtk_tree_view_column_get_visible (column) && - (gtk_tree_view_column_get_clickable (column) || - gtk_tree_view_column_get_reorderable (column))) - break; - last_column = last_column->prev; - } - - - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - - switch (dir) - { - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_UP: - case GTK_DIR_DOWN: - if (focus_child == NULL) - { - if (tree_view->priv->focus_column != NULL) - button = gtk_tree_view_column_get_button (tree_view->priv->focus_column); - else - button = NULL; - - if (button && gtk_widget_get_can_focus (button)) - focus_child = button; - else - focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data)); - - gtk_widget_grab_focus (focus_child); - break; - } - return FALSE; - - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - if (focus_child == NULL) - { - if (tree_view->priv->focus_column != NULL) - focus_child = gtk_tree_view_column_get_button (tree_view->priv->focus_column); - else if (dir == GTK_DIR_LEFT) - focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (last_column->data)); - else - focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data)); - - gtk_widget_grab_focus (focus_child); - break; - } - - if (gtk_widget_child_focus (focus_child, dir)) - { - /* The focus moves inside the button. */ - /* This is probably a great example of bad UI */ - break; - } - - /* We need to move the focus among the row of buttons. */ - for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) - if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child) - break; - - if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT)) - || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))) - { - gtk_widget_error_bell (GTK_WIDGET (tree_view)); - break; - } - - while (tmp_list) - { - if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)) - tmp_list = tmp_list->next; - else - tmp_list = tmp_list->prev; - - if (tmp_list == NULL) - { - g_warning ("Internal button not found"); - break; - } - column = tmp_list->data; - button = gtk_tree_view_column_get_button (column); - if (button && - gtk_tree_view_column_get_visible (column) && - gtk_widget_get_can_focus (button)) - { - focus_child = button; - gtk_widget_grab_focus (button); - break; - } - } - break; - default: - g_assert_not_reached (); - break; - } - - /* if focus child is non-null, we assume it's been set to the current focus child - */ - if (focus_child) - { - for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) - if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child) - { - _gtk_tree_view_set_focus_column (tree_view, GTK_TREE_VIEW_COLUMN (tmp_list->data)); - break; - } - - if (clamp_column_visible) - { - gtk_tree_view_clamp_column_visible (tree_view, - tree_view->priv->focus_column, - FALSE); - } - } - - return (focus_child != NULL); -} - /* This function returns in 'path' the first focusable path, if the given path * is already focusable, it’s the returned one. */ @@ -7919,59 +7752,6 @@ search_first_focusable_path (GtkTreeView *tree_view, return (*path != NULL); } -static gint -gtk_tree_view_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); - GtkWidget *focus_child; - - if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget)) - return FALSE; - - focus_child = gtk_widget_get_focus_child (widget); - - gtk_tree_view_stop_editing (GTK_TREE_VIEW (widget), FALSE); - /* Case 1. Headers currently have focus. */ - if (focus_child) - { - switch (direction) - { - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - gtk_tree_view_header_focus (tree_view, direction, TRUE); - return TRUE; - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_UP: - return FALSE; - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_DOWN: - gtk_widget_grab_focus (widget); - return TRUE; - default: - g_assert_not_reached (); - return FALSE; - } - } - - /* Case 2. We don't have focus at all. */ - if (!gtk_widget_has_focus (widget)) - { - gtk_widget_grab_focus (widget); - return TRUE; - } - - /* Case 3. We have focus already. */ - if (direction == GTK_DIR_TAB_BACKWARD) - return (gtk_tree_view_header_focus (tree_view, direction, FALSE)); - else if (direction == GTK_DIR_TAB_FORWARD) - return FALSE; - - /* Other directions caught by the keybindings */ - gtk_widget_grab_focus (widget); - return TRUE; -} - static void gtk_tree_view_grab_focus (GtkWidget *widget) { diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index c581cb1766e..f6322b805ec 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -599,8 +599,6 @@ static void gtk_widget_real_style_updated (GtkWidget *widget); static void gtk_widget_dispatch_child_properties_changed (GtkWidget *object, guint n_pspecs, GParamSpec **pspecs); -static gboolean gtk_widget_real_focus (GtkWidget *widget, - GtkDirectionType direction); static void gtk_widget_real_move_focus (GtkWidget *widget, GtkDirectionType direction); static gboolean gtk_widget_real_keynav_failed (GtkWidget *widget, @@ -948,7 +946,6 @@ gtk_widget_class_init (GtkWidgetClass *klass) klass->snapshot = gtk_widget_real_snapshot; klass->mnemonic_activate = gtk_widget_real_mnemonic_activate; klass->grab_focus = gtk_widget_real_grab_focus; - klass->focus = gtk_widget_real_focus; klass->next_focus_child = gtk_widget_next_focus_child; klass->move_focus = gtk_widget_real_move_focus; klass->keynav_failed = gtk_widget_real_keynav_failed; @@ -5423,48 +5420,11 @@ gtk_widget_real_style_updated (GtkWidget *widget) } } -static gboolean -gtk_widget_real_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - if (gtk_widget_get_can_focus (widget)) - { - if (!gtk_widget_is_focus (widget)) - { - gtk_widget_grab_focus (widget); - return TRUE; - } - } - else if (_gtk_widget_get_first_child (widget) == NULL) - { - /* No children, no possibility to focus anything */ - return FALSE; - } - else - { - /* Try focusing any of the child widgets, depending on the given direction */ - GtkWidget *focus_child = gtk_widget_get_focus_child (widget); - GtkWidget *next_focus; - - if (focus_child) - next_focus = gtk_widget_get_next_focus (focus_child, direction); - else - next_focus = gtk_widget_get_next_focus (widget, direction); - - if (next_focus && gtk_widget_is_ancestor (next_focus, widget)) - { - gtk_widget_grab_focus (next_focus); - return TRUE; - } - } - - return FALSE; -} - static void gtk_widget_real_move_focus (GtkWidget *widget, GtkDirectionType direction) { + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); GtkWidget *focus_child; GtkWidget *next_focus; @@ -5475,7 +5435,7 @@ gtk_widget_real_move_focus (GtkWidget *widget, next_focus = gtk_widget_get_next_focus (widget, direction); if (next_focus) - gtk_widget_grab_focus (next_focus); + gtk_root_set_focus (priv->root, next_focus); } static gboolean @@ -7490,6 +7450,7 @@ gboolean gtk_widget_child_focus (GtkWidget *widget, GtkDirectionType direction) { + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); GtkWidget *focus_child; GtkWidget *next_focus; @@ -7502,7 +7463,7 @@ gtk_widget_child_focus (GtkWidget *widget, next_focus = gtk_widget_get_next_focus (widget, direction); if (next_focus && gtk_widget_is_ancestor (next_focus, widget)) { - gtk_widget_grab_focus (next_focus); + gtk_root_set_focus (priv->root, next_focus); return TRUE; } diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 1d41c2f6171..4b8fe1e4713 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -185,7 +185,7 @@ struct _GtkWidget * %FALSE, and just grabs the focus if @group_cycling is %TRUE. * @grab_focus: Causes @widget to have the keyboard focus for the * #GtkWindow it’s inside. - * @focus: + * @next_focus_child: Returns the next child that is a candidate for receiving focus * @move_focus: Signal emitted when a change of focus is requested * @keynav_failed: Signal emitted if keyboard navigation fails. * @drag_begin: Signal emitted on the drag source when a drag is @@ -278,8 +278,6 @@ struct _GtkWidgetClass /* explicit focus */ void (* grab_focus) (GtkWidget *widget); - gboolean (* focus) (GtkWidget *widget, - GtkDirectionType direction); GtkWidget * (* next_focus_child) (GtkWidget *widget, GtkWidget *child, GtkDirectionType direction); -- GitLab From 07ce97d5007007cfa079260a2d3aeb5b2c69a324 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 00:35:02 -0500 Subject: [PATCH 19/70] Add a child-focusable flag This gives a convenient way to exclude children from the focus chain. --- gtk/gtkwidget.c | 69 ++++++++++++++++++++++++++++++++++++++++-- gtk/gtkwidget.h | 6 ++++ gtk/gtkwidgetfocus.c | 2 +- gtk/gtkwidgetprivate.h | 1 + 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index f6322b805ec..289f864b46b 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -2823,6 +2823,7 @@ gtk_widget_init (GTypeInstance *instance, gpointer g_class) priv->visible = gtk_widget_class_get_visible_by_default (g_class); priv->child_visible = TRUE; + priv->child_focusable = TRUE; priv->name = NULL; priv->user_alpha = 255; priv->alpha = 255; @@ -3110,10 +3111,11 @@ gtk_widget_unparent (GtkWidget *widget) toplevel = NULL; /* Removing a widget from a container restores the child visible - * flag to the default state, so it doesn't affect the child - * in the next parent. + * and focusable flags to the default state, so they don't affect + * the child in the next parent. */ priv->child_visible = TRUE; + priv->child_focusable = TRUE; old_parent = priv->parent; if (old_parent) @@ -13514,3 +13516,66 @@ gtk_widget_get_height (GtkWidget *widget) return priv->height; } + +/** + * gtk_widget_get_child_focusable: + * @widget: a #GtkWidget + * + * Gets the value set with gtk_widget_set_child_focusable(). + * If you feel a need to use this function, your code probably + * needs reorganization. + * + * This function is only useful for widget implementations and + * never should be called by an application. + * + * Returns: %TRUE if the widget is focusable + **/ +gboolean +gtk_widget_get_child_focusable (GtkWidget *widget) +{ + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return priv->child_focusable; +} + +/** + * gtk_widget_set_child_focusable: + * @widget: a #GtkWidget + * @focusable: if %TRUE, @widget should be focusable + * + * Sets whether @widget should be able to receive focus + * + * Child focusability will be reset to its default state of %TRUE + * when the widget is removed from its parent. + * + * This function is only useful for widget implementations and + * never should be called by an application. + **/ +void +gtk_widget_set_child_focusable (GtkWidget *widget, + gboolean focusable) +{ + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + focusable = !!focusable; + + if (priv->child_focusable == focusable) + return; + + if (focusable) + priv->child_focusable = TRUE; + else + { + GtkWidget *toplevel; + + priv->child_focusable = FALSE; + + toplevel = _gtk_widget_get_toplevel (widget); + if (toplevel != widget && _gtk_widget_is_toplevel (toplevel)) + _gtk_window_unset_focus_and_default (GTK_WINDOW (toplevel), widget); + } +} diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 4b8fe1e4713..d7f906d52a4 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -1063,6 +1063,12 @@ void gtk_widget_insert_before (GtkWidget *widget, GDK_AVAILABLE_IN_ALL void gtk_widget_set_focus_child (GtkWidget *widget, GtkWidget *child); +GDK_AVAILABLE_IN_ALL +void gtk_widget_set_child_focusable (GtkWidget *widget, + gboolean focusable); +GDK_AVAILABLE_IN_ALL +gboolean gtk_widget_get_child_focusable (GtkWidget *widget); + GDK_AVAILABLE_IN_ALL void gtk_widget_snapshot_child (GtkWidget *widget, GtkWidget *child, diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c index c835b0f64d7..2be4f75369e 100644 --- a/gtk/gtkwidgetfocus.c +++ b/gtk/gtkwidgetfocus.c @@ -423,7 +423,7 @@ gtk_widget_focus_sort (GtkWidget *widget, child = _gtk_widget_get_next_sibling (child)) { if (_gtk_widget_get_realized (child) && - _gtk_widget_is_drawable (child) && + gtk_widget_get_child_focusable (child) && gtk_widget_get_sensitive (child)) g_ptr_array_add (focus_order, child); } diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index 9da9ec81636..b4089566d40 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -72,6 +72,7 @@ struct _GtkWidgetPrivate guint has_grab : 1; guint shadowed : 1; guint child_visible : 1; + guint child_focusable : 1; guint multidevice : 1; guint can_pick : 1; -- GitLab From 1ce4fee9cde847bb1e197627214fc5a8e3aeef41 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 00:49:45 -0500 Subject: [PATCH 20/70] window: Check for child-focusable Don't allow gtk_window_set_focus to focus a widget that is !can-focus or has an ancestor that is !child-focusable. --- gtk/gtkwindow.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 00838023610..fa97dcf03ef 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -7263,11 +7263,30 @@ set_focus_widget (GtkWindow *window, do_focus_change (priv->focus_widget, TRUE); } +static gboolean +can_focus_widget (GtkWidget *widget) +{ + GtkWidget *w; + + if (!gtk_widget_is_sensitive (widget) || + !gtk_widget_get_can_focus (widget)) + return FALSE; + + w = widget; + do { + if (!gtk_widget_get_child_focusable (w)) + return FALSE; + w = gtk_widget_get_parent (w); + } while (w != NULL); + + return TRUE; +} + static void gtk_window_real_set_focus (GtkWindow *window, GtkWidget *focus) { - if (focus && !gtk_widget_is_sensitive (focus)) + if (focus && !can_focus_widget (focus)) return; unset_focus_widget (window); -- GitLab From e15c4703bc9c71b6de04884302c3257837f297ef Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 00:50:51 -0500 Subject: [PATCH 21/70] color chooser: Prevent focus leaks The color chooser switches from the palette to the editor by just showing and hiding. Use child-focusable to ensure that focus does not wander off into the hidden parts. --- gtk/gtkcolorchooserwidget.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/gtk/gtkcolorchooserwidget.c b/gtk/gtkcolorchooserwidget.c index 9f2d286017d..3d2ce5a71d4 100644 --- a/gtk/gtkcolorchooserwidget.c +++ b/gtk/gtkcolorchooserwidget.c @@ -129,6 +129,22 @@ swatch_activate (GtkColorSwatch *swatch, _gtk_color_chooser_color_activated (GTK_COLOR_CHOOSER (cc), &color); } +static void +show_editor (GtkColorChooserWidget *cc, + gboolean show) +{ + gtk_widget_set_visible (cc->priv->palette, !show); + gtk_widget_set_visible (cc->priv->editor, show); + gtk_widget_set_child_focusable (cc->priv->palette, !show); + gtk_widget_set_child_focusable (cc->priv->editor, show); + if (show) + gtk_widget_child_focus (cc->priv->editor, GTK_DIR_TAB_FORWARD); + else + gtk_widget_child_focus (cc->priv->palette, GTK_DIR_TAB_FORWARD); + + g_object_notify (G_OBJECT (cc), "show-editor"); +} + static void swatch_customize (GtkColorSwatch *swatch, GtkColorChooserWidget *cc) @@ -138,9 +154,7 @@ swatch_customize (GtkColorSwatch *swatch, gtk_color_swatch_get_rgba (swatch, &color); gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->priv->editor), &color); - gtk_widget_hide (cc->priv->palette); - gtk_widget_show (cc->priv->editor); - g_object_notify (G_OBJECT (cc), "show-editor"); + show_editor (cc, TRUE); } static void @@ -174,9 +188,7 @@ button_activate (GtkColorSwatch *swatch, gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->priv->editor), &color); - gtk_widget_hide (cc->priv->palette); - gtk_widget_show (cc->priv->editor); - g_object_notify (G_OBJECT (cc), "show-editor"); + show_editor (cc, TRUE); } static void @@ -260,9 +272,9 @@ gtk_color_chooser_widget_set_use_alpha (GtkColorChooserWidget *cc, static void gtk_color_chooser_widget_set_show_editor (GtkColorChooserWidget *cc, - gboolean show_editor) + gboolean show) { - if (show_editor) + if (show) { GdkRGBA color = { 0.75, 0.25, 0.25, 1.0 }; @@ -271,8 +283,7 @@ gtk_color_chooser_widget_set_show_editor (GtkColorChooserWidget *cc, gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->priv->editor), &color); } - gtk_widget_set_visible (cc->priv->editor, show_editor); - gtk_widget_set_visible (cc->priv->palette, !show_editor); + show_editor (cc, show); } static void @@ -592,6 +603,7 @@ gtk_color_chooser_widget_init (GtkColorChooserWidget *cc) gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc), &color); gtk_widget_hide (GTK_WIDGET (cc->priv->editor)); + gtk_widget_set_child_focusable (GTK_WIDGET (cc->priv->editor), FALSE); cc->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); gtk_size_group_add_widget (cc->priv->size_group, cc->priv->palette); -- GitLab From e015ee0c009ae0bbdc11e5b76840541c90a83667 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 01:34:32 -0500 Subject: [PATCH 22/70] root: Add a ::focus-widget property --- gtk/gtkroot.c | 42 ++++++++++++++++++++++++++---------------- gtk/gtkroot.h | 4 ---- gtk/gtkrootprivate.h | 8 ++++++++ gtk/gtkwindow.c | 28 +++++++++------------------- 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/gtk/gtkroot.c b/gtk/gtkroot.c index 3552a85474e..249d7405b1b 100644 --- a/gtk/gtkroot.c +++ b/gtk/gtkroot.c @@ -21,6 +21,8 @@ #include "gtkrootprivate.h" #include "gdk/gdk-private.h" +#include "gtkprivate.h" +#include "gtkintl.h" /** * SECTION:gtkroot @@ -59,26 +61,19 @@ gtk_root_default_get_surface_transform (GtkRoot *self, *y = 0; } -static void -gtk_root_default_set_focus (GtkRoot *self, - GtkWidget *focus) -{ -} - -static GtkWidget * -gtk_root_default_get_focus (GtkRoot *self) -{ - return NULL; -} - static void gtk_root_default_init (GtkRootInterface *iface) { iface->get_display = gtk_root_default_get_display; iface->get_renderer = gtk_root_default_get_renderer; iface->get_surface_transform = gtk_root_default_get_surface_transform; - iface->set_focus = gtk_root_default_set_focus; - iface->get_focus = gtk_root_default_get_focus; + + g_object_interface_install_property (iface, + g_param_spec_object ("focus-widget", + P_("Focus widget"), + P_("The focus widget"), + GTK_TYPE_WIDGET, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); } GdkDisplay * @@ -159,7 +154,7 @@ gtk_root_set_focus (GtkRoot *self, g_return_if_fail (GTK_IS_ROOT (self)); g_return_if_fail (focus == NULL || GTK_IS_WIDGET (focus)); - GTK_ROOT_GET_IFACE (self)->set_focus (self, focus); + g_object_set (self, "focus-widget", focus, NULL); } /** @@ -179,7 +174,22 @@ gtk_root_set_focus (GtkRoot *self, GtkWidget * gtk_root_get_focus (GtkRoot *self) { + GtkWidget *focus; + g_return_val_if_fail (GTK_IS_ROOT (self), NULL); - return GTK_ROOT_GET_IFACE (self)->get_focus (self); + g_object_get (self, "focus-widget", &focus, NULL); + + if (focus) + g_object_unref (focus); + + return focus; +} + +guint +gtk_root_install_properties (GObjectClass *object_class, + guint first_prop) +{ + g_object_class_override_property (object_class, first_prop + GTK_ROOT_PROP_FOCUS_WIDGET, "focus-widget"); + return GTK_ROOT_NUM_PROPERTIES; } diff --git a/gtk/gtkroot.h b/gtk/gtkroot.h index fe4d980d2e3..4c7ae89e377 100644 --- a/gtk/gtkroot.h +++ b/gtk/gtkroot.h @@ -51,10 +51,6 @@ struct _GtkRootInterface void (* get_surface_transform) (GtkRoot *root, int *x, int *y); - - void (* set_focus) (GtkRoot *self, - GtkWidget *focus); - GtkWidget * (* get_focus) (GtkRoot *self); }; GDK_AVAILABLE_IN_ALL diff --git a/gtk/gtkrootprivate.h b/gtk/gtkrootprivate.h index 07ddc380e54..357bc6441f4 100644 --- a/gtk/gtkrootprivate.h +++ b/gtk/gtkrootprivate.h @@ -11,6 +11,14 @@ GskRenderer * gtk_root_get_renderer (GtkRoot void gtk_root_get_surface_transform (GtkRoot *self, int *x, int *y); +enum { + GTK_ROOT_PROP_FOCUS_WIDGET, + GTK_ROOT_NUM_PROPERTIES +} GtkRootProperties; + +guint gtk_root_install_properties (GObjectClass *object_class, + guint first_prop); + G_END_DECLS #endif /* __GTK_ROOT_PRIVATE_H__ */ diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index fa97dcf03ef..a4d3bfd4ea2 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -1134,6 +1134,7 @@ gtk_window_class_init (GtkWindowClass *klass) GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY); g_object_class_install_properties (gobject_class, LAST_ARG, window_props); + gtk_root_install_properties (gobject_class, LAST_ARG); /** * GtkWindow:set-focus: @@ -2111,6 +2112,9 @@ gtk_window_set_property (GObject *object, case PROP_FOCUS_VISIBLE: gtk_window_set_focus_visible (window, g_value_get_boolean (value)); break; + case LAST_ARG + GTK_ROOT_PROP_FOCUS_WIDGET: + gtk_window_set_focus (window, g_value_get_object (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2226,6 +2230,9 @@ gtk_window_get_property (GObject *object, case PROP_IS_MAXIMIZED: g_value_set_boolean (value, gtk_window_is_maximized (window)); break; + case LAST_ARG + GTK_ROOT_PROP_FOCUS_WIDGET: + g_value_set_object (value, gtk_window_get_focus (window)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2549,31 +2556,12 @@ gtk_window_root_get_surface_transform (GtkRoot *root, *y = margin.top + border.top + padding.top; } -static void -gtk_window_root_set_focus (GtkRoot *root, - GtkWidget *focus) -{ - GtkWindow *self = GTK_WINDOW (root); - - gtk_window_set_focus (self, focus); -} - -static GtkWidget * -gtk_window_root_get_focus (GtkRoot *root) -{ - GtkWindow *self = GTK_WINDOW (root); - - return gtk_window_get_focus (self); -} - static void gtk_window_root_interface_init (GtkRootInterface *iface) { iface->get_display = gtk_window_root_get_display; iface->get_renderer = gtk_window_root_get_renderer; iface->get_surface_transform = gtk_window_root_get_surface_transform; - iface->set_focus = gtk_window_root_set_focus; - iface->get_focus = gtk_window_root_get_focus; } /** @@ -7291,6 +7279,8 @@ gtk_window_real_set_focus (GtkWindow *window, unset_focus_widget (window); set_focus_widget (window, focus); + + g_object_notify (G_OBJECT (window), "focus-widget"); } static void -- GitLab From a7f564f04fc0ec77d8d74ebbebd165c06246302e Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 02:52:57 -0500 Subject: [PATCH 23/70] Protect against focus cycles Now that next_focus_child wraps around for windows, GtkMenus cause an infinite lookp in next_Focus_child. Prevent that. --- gtk/gtkwidgetfocus.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c index 2be4f75369e..691275d7018 100644 --- a/gtk/gtkwidgetfocus.c +++ b/gtk/gtkwidgetfocus.c @@ -465,6 +465,9 @@ gtk_widget_get_next_focus (GtkWidget *widget, { GtkWidget *prev; GtkWidget *next; + GHashTable *seen; + + seen = g_hash_table_new (g_direct_hash, g_direct_equal); prev = NULL; do { @@ -476,16 +479,23 @@ gtk_widget_get_next_focus (GtkWidget *widget, } else if (gtk_widget_get_can_focus (next)) { - return next; + break; + } + else if (g_hash_table_contains (seen, next)) + { + next = NULL; + break; } else { + g_hash_table_add (seen, next); widget = next; prev = NULL; } } while (widget); - return NULL; + g_hash_table_unref (seen); + return next; } GtkWidget * -- GitLab From 9991f1a0e821e4f9b5e781047292df17c55b4310 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 07:55:14 -0500 Subject: [PATCH 24/70] inspector: Stop using ::set-focus The focus-widget is just a regular property now that shows up on the property page. No need to special-case it on the misc page anymore. --- gtk/inspector/misc-info.c | 50 -------------------------------------- gtk/inspector/misc-info.ui | 37 ---------------------------- 2 files changed, 87 deletions(-) diff --git a/gtk/inspector/misc-info.c b/gtk/inspector/misc-info.c index f352894daf1..1d9d053d7db 100644 --- a/gtk/inspector/misc-info.c +++ b/gtk/inspector/misc-info.c @@ -46,9 +46,6 @@ struct _GtkInspectorMiscInfoPrivate { GtkWidget *default_widget_row; GtkWidget *default_widget; GtkWidget *default_widget_button; - GtkWidget *focus_widget_row; - GtkWidget *focus_widget; - GtkWidget *focus_widget_button; GtkWidget *mnemonic_label_row; GtkWidget *mnemonic_label; GtkWidget *request_mode_row; @@ -217,43 +214,6 @@ show_default_widget (GtkWidget *button, GtkInspectorMiscInfo *sl) show_object (sl, G_OBJECT (widget), "properties"); } -static void -update_focus_widget (GtkInspectorMiscInfo *sl) -{ - GtkWidget *widget; - - widget = gtk_root_get_focus (GTK_ROOT (sl->priv->object)); - if (widget) - { - gchar *tmp; - tmp = g_strdup_printf ("%p", widget); - gtk_label_set_label (GTK_LABEL (sl->priv->focus_widget), tmp); - g_free (tmp); - gtk_widget_set_sensitive (sl->priv->focus_widget_button, TRUE); - } - else - { - gtk_label_set_label (GTK_LABEL (sl->priv->focus_widget), "NULL"); - gtk_widget_set_sensitive (sl->priv->focus_widget_button, FALSE); - } -} - -static void -set_focus_cb (GtkWindow *window, GtkWidget *focus, GtkInspectorMiscInfo *sl) -{ - update_focus_widget (sl); -} - -static void -show_focus_widget (GtkWidget *button, GtkInspectorMiscInfo *sl) -{ - GtkWidget *widget; - - widget = gtk_root_get_focus (GTK_ROOT (sl->priv->object)); - if (widget) - show_object (sl, G_OBJECT (widget), "properties"); -} - static void show_mnemonic_label (GtkWidget *button, GtkInspectorMiscInfo *sl) { @@ -358,7 +318,6 @@ update_info (gpointer data) if (GTK_IS_WINDOW (sl->priv->object)) { update_default_widget (sl); - update_focus_widget (sl); } if (GDK_IS_FRAME_CLOCK (sl->priv->object)) @@ -408,7 +367,6 @@ gtk_inspector_misc_info_set_object (GtkInspectorMiscInfo *sl, if (sl->priv->object) { g_signal_handlers_disconnect_by_func (sl->priv->object, state_flags_changed, sl); - g_signal_handlers_disconnect_by_func (sl->priv->object, set_focus_cb, sl); g_signal_handlers_disconnect_by_func (sl->priv->object, allocation_changed, sl); disconnect_each_other (sl->priv->object, G_OBJECT (sl)); disconnect_each_other (sl, sl->priv->object); @@ -475,14 +433,10 @@ gtk_inspector_misc_info_set_object (GtkInspectorMiscInfo *sl, if (GTK_IS_WINDOW (object)) { gtk_widget_show (sl->priv->default_widget_row); - gtk_widget_show (sl->priv->focus_widget_row); - - g_signal_connect_object (object, "set-focus", G_CALLBACK (set_focus_cb), sl, G_CONNECT_AFTER); } else { gtk_widget_hide (sl->priv->default_widget_row); - gtk_widget_hide (sl->priv->focus_widget_row); } if (GDK_IS_FRAME_CLOCK (object)) @@ -595,9 +549,6 @@ gtk_inspector_misc_info_class_init (GtkInspectorMiscInfoClass *klass) gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, default_widget_row); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, default_widget); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, default_widget_button); - gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, focus_widget_row); - gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, focus_widget); - gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, focus_widget_button); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, mnemonic_label_row); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, mnemonic_label); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, request_mode_row); @@ -631,7 +582,6 @@ gtk_inspector_misc_info_class_init (GtkInspectorMiscInfoClass *klass) gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, child_visible); gtk_widget_class_bind_template_callback (widget_class, show_default_widget); - gtk_widget_class_bind_template_callback (widget_class, show_focus_widget); gtk_widget_class_bind_template_callback (widget_class, show_frame_clock); } diff --git a/gtk/inspector/misc-info.ui b/gtk/inspector/misc-info.ui index 0ee5082e240..cbb3ef65b43 100644 --- a/gtk/inspector/misc-info.ui +++ b/gtk/inspector/misc-info.ui @@ -157,42 +157,6 @@ - - - 0 - - - 10 - 40 - - - Focus Widget - start - baseline - 0.0 - 1 - - - - - 1 - end - baseline - end - - - - - end - baseline - Properties - - - - - - - 0 @@ -616,7 +580,6 @@ - -- GitLab From 3b5a6b656a3cc20f167168370209b221eed4dc7b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 08:06:53 -0500 Subject: [PATCH 25/70] filechooser: Stop using ::set-focus We can achieve the same with the focus-widget property. --- gtk/gtkfilechooserwidget.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index 7c7b32d1194..3227a84e1f6 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -341,6 +341,7 @@ struct _GtkFileChooserWidgetPrivate { GSource *focus_entry_idle; gulong toplevel_set_focus_id; + GtkWidget *toplevel_current_focus_widget; GtkWidget *toplevel_last_focus_widget; gint sort_column; @@ -3561,13 +3562,14 @@ gtk_file_chooser_widget_dispose (GObject *object) * widget on our toplevel. See gtk_file_chooser_widget_hierarchy_changed() */ static void -toplevel_set_focus_cb (GtkWindow *window, - GtkWidget *focus, +toplevel_set_focus_cb (GtkWindow *window, + GParamSpec *pspec, GtkFileChooserWidget *impl) { GtkFileChooserWidgetPrivate *priv = impl->priv; - priv->toplevel_last_focus_widget = gtk_root_get_focus (GTK_ROOT (window)); + priv->toplevel_last_focus_widget = priv->toplevel_current_focus_widget; + priv->toplevel_current_focus_widget = gtk_root_get_focus (GTK_ROOT (window)); } /* We monitor the focus widget on our toplevel to be able to know which widget @@ -3585,9 +3587,10 @@ gtk_file_chooser_widget_root (GtkWidget *widget) toplevel = gtk_widget_get_toplevel (widget); g_assert (priv->toplevel_set_focus_id == 0); - priv->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus", + priv->toplevel_set_focus_id = g_signal_connect (toplevel, "notify::focus-widget", G_CALLBACK (toplevel_set_focus_cb), impl); - priv->toplevel_last_focus_widget = gtk_root_get_focus (GTK_ROOT (toplevel)); + priv->toplevel_last_focus_widget = NULL; + priv->toplevel_current_focus_widget = gtk_root_get_focus (GTK_ROOT (toplevel)); } static void @@ -3603,6 +3606,7 @@ gtk_file_chooser_widget_unroot (GtkWidget *widget) g_signal_handler_disconnect (toplevel, priv->toplevel_set_focus_id); priv->toplevel_set_focus_id = 0; priv->toplevel_last_focus_widget = NULL; + priv->toplevel_current_focus_widget = NULL; } GTK_WIDGET_CLASS (gtk_file_chooser_widget_parent_class)->unroot (widget); -- GitLab From 53598e5e44a5ce99a03304c7f824921c86e0e641 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 08:10:14 -0500 Subject: [PATCH 26/70] popover: Stop using ::set-focus This will change completely when GtkPopover becomes a root. For now, stop using ::set-focus and just use the focus-widget property. --- gtk/gtkpopover.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c index 6f305ed0aa4..7370b4951f5 100644 --- a/gtk/gtkpopover.c +++ b/gtk/gtkpopover.c @@ -614,10 +614,11 @@ window_active_changed (GtkWindow *window, static void window_set_focus (GtkWindow *window, - GtkWidget *widget, + 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; @@ -673,7 +674,7 @@ gtk_popover_apply_modality (GtkPopover *popover, g_signal_connect (priv->window, "notify::is-active", G_CALLBACK (window_active_changed), popover); - g_signal_connect (priv->window, "set-focus", + g_signal_connect (priv->window, "notify::focus-widget", G_CALLBACK (window_set_focus), popover); } else -- GitLab From 73cad163ad2450cdb071b3d641e1b4745b8d94a1 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 08:25:42 -0500 Subject: [PATCH 27/70] window: Remove ::set-focus The focus-widget is now a property, so we don't need this signal anymore. --- gtk/gtkwindow.c | 64 +++++++++++++------------------------------------ gtk/gtkwindow.h | 5 ---- 2 files changed, 17 insertions(+), 52 deletions(-) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index a4d3bfd4ea2..78f5b427b75 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -299,7 +299,6 @@ static const char *dnd_dest_targets [] = { #endif enum { - SET_FOCUS, ACTIVATE_FOCUS, ACTIVATE_DEFAULT, KEYS_CHANGED, @@ -436,8 +435,6 @@ static void gtk_window_remove (GtkContainer *container, static void gtk_window_forall (GtkContainer *container, GtkCallback callback, gpointer callback_data); -static void gtk_window_real_set_focus (GtkWindow *window, - GtkWidget *focus); static void gtk_window_real_activate_default (GtkWindow *window); static void gtk_window_real_activate_focus (GtkWindow *window); @@ -832,8 +829,6 @@ gtk_window_class_init (GtkWindowClass *klass) container_class->remove = gtk_window_remove; container_class->forall = gtk_window_forall; - klass->set_focus = gtk_window_real_set_focus; - klass->activate_default = gtk_window_real_activate_default; klass->activate_focus = gtk_window_real_activate_focus; klass->keys_changed = gtk_window_keys_changed; @@ -1136,24 +1131,6 @@ gtk_window_class_init (GtkWindowClass *klass) g_object_class_install_properties (gobject_class, LAST_ARG, window_props); gtk_root_install_properties (gobject_class, LAST_ARG); - /** - * GtkWindow:set-focus: - * @window: the window which received the signal - * @widget: (nullable): the newly focused widget (or %NULL for no focus) - * - * This signal is emitted whenever the currently focused widget in - * this window changes. - */ - window_signals[SET_FOCUS] = - g_signal_new (I_("set-focus"), - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkWindowClass, set_focus), - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - GTK_TYPE_WIDGET); - /** * GtkWindow::activate-focus: * @window: the window which received the signal @@ -2794,27 +2771,6 @@ gtk_window_get_role (GtkWindow *window) return priv->wm_role; } -/** - * gtk_window_set_focus: - * @window: a #GtkWindow - * @focus: (allow-none): widget to be the new focus widget, or %NULL to unset - * any focus widget for the toplevel window. - * - * If @focus is not the current focus widget, and is focusable, sets - * it as the focus widget for the window. If @focus is %NULL, unsets - * the focus widget for this window. To set the focus to a particular - * widget in the toplevel, it is usually more convenient to use - * gtk_widget_grab_focus() instead of this function. - **/ -void -gtk_window_set_focus (GtkWindow *window, - GtkWidget *focus) -{ - g_return_if_fail (GTK_IS_WINDOW (window)); - - g_signal_emit (window, window_signals[SET_FOCUS], 0, focus); -} - /** * gtk_window_set_default: * @window: a #GtkWindow @@ -7270,10 +7226,24 @@ can_focus_widget (GtkWidget *widget) return TRUE; } -static void -gtk_window_real_set_focus (GtkWindow *window, - GtkWidget *focus) +/** + * gtk_window_set_focus: + * @window: a #GtkWindow + * @focus: (allow-none): widget to be the new focus widget, or %NULL to unset + * any focus widget for the toplevel window. + * + * If @focus is not the current focus widget, and is focusable, sets + * it as the focus widget for the window. If @focus is %NULL, unsets + * the focus widget for this window. To set the focus to a particular + * widget in the toplevel, it is usually more convenient to use + * gtk_widget_grab_focus() instead of this function. + **/ +void +gtk_window_set_focus (GtkWindow *window, + GtkWidget *focus) { + g_return_if_fail (GTK_IS_WINDOW (window)); + if (focus && !can_focus_widget (focus)) return; diff --git a/gtk/gtkwindow.h b/gtk/gtkwindow.h index fb30ab99b8e..280658b278c 100644 --- a/gtk/gtkwindow.h +++ b/gtk/gtkwindow.h @@ -70,11 +70,6 @@ struct _GtkWindowClass { GtkBinClass parent_class; - /*< public >*/ - - void (* set_focus) (GtkWindow *window, - GtkWidget *focus); - /* G_SIGNAL_ACTION signals for keybindings */ void (* activate_focus) (GtkWindow *window); -- GitLab From 4fa651a03fb516d4bed52b4a3680bde6210ff4e9 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 12:31:12 -0500 Subject: [PATCH 28/70] window: Remove initial-focus builder support With focus-widget now a property, this is no longer needed. --- gtk/gtkwindow.c | 85 ------------------------------------------------- tests/dialog.ui | 1 - 2 files changed, 86 deletions(-) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 78f5b427b75..af902229d50 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -122,16 +122,12 @@ * elements representing the #GtkAccelGroup objects you want to add to * your window (synonymous with gtk_window_add_accel_group(). * - * It also supports the element, whose name property names - * the widget to receive the focus when the window is mapped. - * * An example of a UI definition fragment with accel groups: * |[ * * * * - * * * * ... @@ -2365,55 +2361,6 @@ static const GMarkupParser window_parser = window_start_element }; -typedef struct { - GObject *object; - GtkBuilder *builder; - gchar *name; - gint line; - gint col; -} NameSubParserData; - -static void -focus_start_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **names, - const gchar **values, - gpointer user_data, - GError **error) -{ - NameSubParserData *data = (NameSubParserData*)user_data; - - if (strcmp (element_name, "initial-focus") == 0) - { - const gchar *name; - - if (!_gtk_builder_check_parent (data->builder, context, "object", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_STRING, "name", &name, - G_MARKUP_COLLECT_INVALID)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - data->name = g_strdup (name); - g_markup_parse_context_get_position (context, &data->line, &data->col); - } - else - { - _gtk_builder_error_unhandled_tag (data->builder, context, - "GtkWindow", element_name, - error); - } -} - -static const GMarkupParser focus_parser = -{ - focus_start_element -}; - static gboolean gtk_window_buildable_custom_tag_start (GtkBuildable *buildable, GtkBuilder *builder, @@ -2441,21 +2388,6 @@ gtk_window_buildable_custom_tag_start (GtkBuildable *buildable, return TRUE; } - if (strcmp (tagname, "initial-focus") == 0) - { - NameSubParserData *data; - - data = g_slice_new0 (NameSubParserData); - data->name = NULL; - data->object = G_OBJECT (buildable); - data->builder = builder; - - *parser = focus_parser; - *parser_data = data; - - return TRUE; - } - return FALSE; } @@ -2478,23 +2410,6 @@ gtk_window_buildable_custom_finished (GtkBuildable *buildable, g_slice_free (GSListSubParserData, data); } - - if (strcmp (tagname, "initial-focus") == 0) - { - NameSubParserData *data = (NameSubParserData*)user_data; - - if (data->name) - { - GObject *object; - - object = _gtk_builder_lookup_object (builder, data->name, data->line, data->col); - if (object) - gtk_window_set_focus (GTK_WINDOW (buildable), GTK_WIDGET (object)); - g_free (data->name); - } - - g_slice_free (NameSubParserData, data); - } } static GdkDisplay * diff --git a/tests/dialog.ui b/tests/dialog.ui index 93aa2015bc6..baa032ec48f 100644 --- a/tests/dialog.ui +++ b/tests/dialog.ui @@ -239,6 +239,5 @@ cancel_button confirm_button - -- GitLab From a844c9d6caff50fa427e1fdb7e6d4538ee3f8c76 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 12:06:56 -0500 Subject: [PATCH 29/70] entry: Redo select-on-focus handling GtkText was handling this just on grab_focus, which is not the only way anymore that focus can reach it. Instead, shift this to focus-in, and add a property that can be used to override the select-on-focus setting. Remove the gtk_text_grab_focus_without_selecting(), but keep the GtkEntry variant of the function, reimplemented to use the properties. GtkSearchEntry sets the select-on-focus property of its text to FALSE, to match expectations. --- docs/reference/gtk/gtk4-sections.txt | 1 - gtk/gtkentry.c | 17 +++- gtk/gtksearchentry.c | 3 +- gtk/gtktext.c | 112 +++++++++++++++++---------- gtk/gtktext.h | 3 - 5 files changed, 86 insertions(+), 50 deletions(-) diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 911135eb398..699cb2d56b9 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -904,7 +904,6 @@ gtk_text_set_attributes gtk_text_get_attributes gtk_text_set_tabs gtk_text_get_tabs -gtk_text_grab_focus_without_selecting gtk_text_get_type diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index ff52184a414..a57fb5e50f3 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -1652,10 +1652,23 @@ gtk_entry_snapshot (GtkWidget *widget, void gtk_entry_grab_focus_without_selecting (GtkEntry *entry) { - g_return_if_fail (GTK_IS_ENTRY (entry)); GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry); + gboolean select, set; + + g_return_if_fail (GTK_IS_ENTRY (entry)); + + g_object_get (priv->text, + "select-on-focus", &select, + "select-on-focus-set", &set, + NULL); + g_object_set (priv->text, "select-on-focus", FALSE, NULL); + + gtk_widget_grab_focus (priv->text); - gtk_text_grab_focus_without_selecting (GTK_TEXT (priv->text)); + g_object_set (priv->text, + "select-on-focus", select, + "select-on-focus-set", set, + NULL); } static void diff --git a/gtk/gtksearchentry.c b/gtk/gtksearchentry.c index fd6dfed05fb..b9495241a5f 100644 --- a/gtk/gtksearchentry.c +++ b/gtk/gtksearchentry.c @@ -255,7 +255,7 @@ gtk_search_entry_grab_focus (GtkWidget *widget) GtkSearchEntry *entry = GTK_SEARCH_ENTRY (widget); GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry); - gtk_text_grab_focus_without_selecting (GTK_TEXT (priv->entry)); + gtk_widget_grab_focus (priv->entry); } static gboolean @@ -523,6 +523,7 @@ gtk_search_entry_init (GtkSearchEntry *entry) gtk_widget_set_vexpand (priv->box, FALSE); priv->entry = gtk_text_new (); + g_object_set (priv->entry, "select-on-focus", FALSE, NULL); gtk_widget_set_hexpand (priv->entry, TRUE); gtk_widget_set_vexpand (priv->entry, TRUE); gtk_container_add (GTK_CONTAINER (priv->box), GTK_WIDGET (priv->entry)); diff --git a/gtk/gtktext.c b/gtk/gtktext.c index ed721eca6bf..b33889556eb 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -225,6 +225,8 @@ struct _GtkTextPrivate guint cursor_handle_dragged : 1; guint selection_handle_dragged : 1; guint populate_all : 1; + guint select_on_focus : 1; + guint select_on_focus_set : 1; }; struct _GtkTextPasswordHint @@ -268,6 +270,8 @@ enum { PROP_POPULATE_ALL, PROP_TABS, PROP_ENABLE_EMOJI_COMPLETION, + PROP_SELECT_ON_FOCUS, + PROP_SELECT_ON_FOCUS_SET, NUM_PROPERTIES }; @@ -321,7 +325,6 @@ static void gtk_text_snapshot (GtkWidget *widget, GtkSnapshot *snapshot); static void gtk_text_focus_in (GtkWidget *widget); static void gtk_text_focus_out (GtkWidget *widget); -static void gtk_text_grab_focus (GtkWidget *widget); static void gtk_text_style_updated (GtkWidget *widget); static void gtk_text_direction_changed (GtkWidget *widget, GtkTextDirection previous_dir); @@ -675,7 +678,6 @@ gtk_text_class_init (GtkTextClass *class) widget_class->measure = gtk_text_measure; widget_class->size_allocate = gtk_text_size_allocate; widget_class->snapshot = gtk_text_snapshot; - widget_class->grab_focus = gtk_text_grab_focus; widget_class->style_updated = gtk_text_style_updated; widget_class->drag_begin = gtk_text_drag_begin; widget_class->drag_end = gtk_text_drag_end; @@ -898,6 +900,26 @@ gtk_text_class_init (GtkTextClass *class) TRUE, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + /** + * GtkEntry::select-on-focus: + * + * Whether to select the contents of the text when focus enters it. + * When set, this property overrides the system-widget setting for + * this feature. See #GtkEntry::select-on-enter-set + */ + text_props[PROP_SELECT_ON_FOCUS] = + g_param_spec_boolean ("select-on-focus", + P_("Selet on focus"), + P_("Whether to select the text on focus"), + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + text_props[PROP_SELECT_ON_FOCUS_SET] = + g_param_spec_boolean ("select-on-focus-set", + P_("Select on focus set"), + P_("Whether the select-on-focus property has been set"), + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + g_object_class_install_properties (gobject_class, NUM_PROPERTIES, text_props); gtk_editable_install_properties (gobject_class, NUM_PROPERTIES); @@ -1515,6 +1537,27 @@ gtk_text_set_property (GObject *object, set_enable_emoji_completion (self, g_value_get_boolean (value)); break; + case PROP_SELECT_ON_FOCUS: + if (priv->select_on_focus != g_value_get_boolean (value)) + { + priv->select_on_focus = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + if (!priv->select_on_focus_set) + { + priv->select_on_focus_set = TRUE; + g_object_notify_by_pspec (object, text_props[PROP_SELECT_ON_FOCUS_SET]); + } + break; + + case PROP_SELECT_ON_FOCUS_SET: + if (priv->select_on_focus_set != g_value_get_boolean (value)) + { + priv->select_on_focus_set = g_value_get_boolean (value); + g_object_notify_by_pspec (object, pspec); + } + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1630,6 +1673,14 @@ gtk_text_get_property (GObject *object, g_value_set_boolean (value, priv->enable_emoji_completion); break; + case PROP_SELECT_ON_FOCUS: + g_value_set_boolean (value, priv->select_on_focus); + break; + + case PROP_SELECT_ON_FOCUS_SET: + g_value_set_boolean (value, priv->select_on_focus_set); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2896,6 +2947,22 @@ gtk_text_focus_in (GtkWidget *widget) GtkTextPrivate *priv = gtk_text_get_instance_private (self); GdkKeymap *keymap; + if (priv->editable && !priv->in_click) + { + gboolean select_on_focus; + + if (priv->select_on_focus_set) + select_on_focus = priv->select_on_focus; + else + g_object_get (gtk_widget_get_settings (widget), + "gtk-entry-select-on-focus", + &select_on_focus, + NULL); + + if (select_on_focus) + gtk_text_set_selection_bounds (self, 0, -1); + } + gtk_widget_queue_draw (widget); keymap = gdk_display_get_keymap (gtk_widget_get_display (widget)); @@ -2941,47 +3008,6 @@ gtk_text_focus_out (GtkWidget *widget) g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, self); } -static void -gtk_text_grab_focus (GtkWidget *widget) -{ - GtkText *self = GTK_TEXT (widget); - GtkTextPrivate *priv = gtk_text_get_instance_private (self); - gboolean select_on_focus; - - GTK_WIDGET_CLASS (gtk_text_parent_class)->grab_focus (GTK_WIDGET (self)); - - if (priv->editable && !priv->in_click) - { - g_object_get (gtk_widget_get_settings (widget), - "gtk-entry-select-on-focus", - &select_on_focus, - NULL); - - if (select_on_focus) - gtk_text_set_selection_bounds (self, 0, -1); - } -} - -/** - * gtk_text_grab_focus_without_selecting: - * @self: a #GtkText - * - * Causes @self to have keyboard focus. - * - * It behaves like gtk_widget_grab_focus(), - * except that it doesn't select the contents of the self. - * You only want to call this on some special entries - * which the user usually doesn't want to replace all text in, - * such as search-as-you-type entries. - */ -void -gtk_text_grab_focus_without_selecting (GtkText *self) -{ - g_return_if_fail (GTK_IS_TEXT (self)); - - GTK_WIDGET_CLASS (gtk_text_parent_class)->grab_focus (GTK_WIDGET (self)); -} - static void gtk_text_direction_changed (GtkWidget *widget, GtkTextDirection previous_dir) diff --git a/gtk/gtktext.h b/gtk/gtktext.h index 9907daee9d6..96488a1f331 100644 --- a/gtk/gtktext.h +++ b/gtk/gtktext.h @@ -131,9 +131,6 @@ void gtk_text_set_tabs (GtkText *self, GDK_AVAILABLE_IN_ALL PangoTabArray * gtk_text_get_tabs (GtkText *self); -GDK_AVAILABLE_IN_ALL -void gtk_text_grab_focus_without_selecting (GtkText *self); - G_END_DECLS #endif /* __GTK_TEXT_H__ */ -- GitLab From 6c5d4fbe328f9cb91b8e671a3b13964f55701946 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 13:09:37 -0500 Subject: [PATCH 30/70] label: Redo select-on-focus handling Similar to GtkEntry, move this from grab_focus to focus-in. --- gtk/gtklabel.c | 80 +++++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index e8b3355c922..c4c4db43d56 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -33,6 +33,7 @@ #include "gtkcssshadowsvalueprivate.h" #include "gtkcssstylepropertyprivate.h" #include "gtkdnd.h" +#include "gtkeventcontrollerkey.h" #include "gtkeventcontrollermotion.h" #include "gtkgesturedrag.h" #include "gtkgesturemultipress.h" @@ -523,6 +524,9 @@ static void gtk_label_activate_current_link (GtkLabel *label); static GtkLabelLink *gtk_label_get_current_link (GtkLabel *label); static void emit_activate_link (GtkLabel *label, GtkLabelLink *link); +static gboolean range_is_in_ellipsis (GtkLabel *label, + gint range_start, + gint range_end); /* Event controller callbacks */ static void gtk_label_multipress_gesture_pressed (GtkGestureMultiPress *gesture, @@ -1119,6 +1123,46 @@ gtk_label_class_init (GtkLabelClass *class) quark_link = g_quark_from_static_string ("link"); } +static void +focus_in_cb (GtkEventControllerKey *controller, + GtkWidget *widget) +{ + GtkLabel *label = GTK_LABEL (widget); + GtkLabelPrivate *priv = gtk_label_get_instance_private (label); + + if (priv->select_info->selectable) + { + gboolean select_on_focus; + + g_object_get (gtk_widget_get_settings (widget), + "gtk-label-select-on-focus", + &select_on_focus, + NULL); + + if (select_on_focus && !priv->in_click) + gtk_label_select_region (label, 0, -1); + } + else + { + if (priv->select_info->links && !priv->in_click) + { + GList *l; + + for (l = priv->select_info->links; l; l = l->next) + { + GtkLabelLink *link = l->data; + if (!range_is_in_ellipsis (label, link->start, link->end)) + { + priv->select_info->selection_anchor = link->start; + priv->select_info->selection_end = link->start; + _gtk_label_accessible_focus_link_changed (label); + break; + } + } + } + } +} + static void gtk_label_set_property (GObject *object, guint prop_id, @@ -1270,6 +1314,7 @@ static void gtk_label_init (GtkLabel *label) { GtkLabelPrivate *priv = gtk_label_get_instance_private (label); + GtkEventController *controller; gtk_widget_set_has_surface (GTK_WIDGET (label), FALSE); @@ -1300,6 +1345,10 @@ gtk_label_init (GtkLabel *label) priv->mnemonic_window = NULL; priv->mnemonics_visible = TRUE; + + controller = gtk_event_controller_key_new (); + g_signal_connect (controller, "focus-in", G_CALLBACK (focus_in_cb), label); + gtk_widget_add_controller (GTK_WIDGET (label), controller); } @@ -4257,42 +4306,11 @@ gtk_label_grab_focus (GtkWidget *widget) { GtkLabel *label = GTK_LABEL (widget); GtkLabelPrivate *priv = gtk_label_get_instance_private (label); - gboolean select_on_focus; - GtkLabelLink *link; - GList *l; if (priv->select_info == NULL) return; GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget); - - if (priv->select_info->selectable) - { - g_object_get (gtk_widget_get_settings (widget), - "gtk-label-select-on-focus", - &select_on_focus, - NULL); - - if (select_on_focus && !priv->in_click) - gtk_label_select_region (label, 0, -1); - } - else - { - if (priv->select_info->links && !priv->in_click) - { - for (l = priv->select_info->links; l; l = l->next) - { - link = l->data; - if (!range_is_in_ellipsis (label, link->start, link->end)) - { - priv->select_info->selection_anchor = link->start; - priv->select_info->selection_end = link->start; - _gtk_label_accessible_focus_link_changed (label); - break; - } - } - } - } } static void -- GitLab From 81783abd522dd268a192b404b52344d2f17cda66 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 14:06:15 -0500 Subject: [PATCH 31/70] label: Bring back link focus Handle key-pressed ourselves to maintain the intra-label focus location for links. This works almost as well as it did previously. One issue is that our ::focus-in handler does not know which direction the focus is coming from, so we always select the first link, whereas previously we would select the last link for Shift-Tab. --- gtk/gtklabel.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 5 deletions(-) diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index c4c4db43d56..9ca02781ad5 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -527,6 +527,7 @@ static void emit_activate_link (GtkLabel *label, static gboolean range_is_in_ellipsis (GtkLabel *label, gint range_start, gint range_end); +static GtkLabelLink *gtk_label_get_focus_link (GtkLabel *label); /* Event controller callbacks */ static void gtk_label_multipress_gesture_pressed (GtkGestureMultiPress *gesture, @@ -1129,8 +1130,12 @@ focus_in_cb (GtkEventControllerKey *controller, { GtkLabel *label = GTK_LABEL (widget); GtkLabelPrivate *priv = gtk_label_get_instance_private (label); + GtkLabelSelectionInfo *info = priv->select_info; - if (priv->select_info->selectable) + if (!info) + return; + + if (info->selectable) { gboolean select_on_focus; @@ -1144,18 +1149,20 @@ focus_in_cb (GtkEventControllerKey *controller, } else { - if (priv->select_info->links && !priv->in_click) + if (info->links && !priv->in_click) { GList *l; + int i; - for (l = priv->select_info->links; l; l = l->next) + for (l = info->links, i = 0; l; l = l->next, i++) { GtkLabelLink *link = l->data; if (!range_is_in_ellipsis (label, link->start, link->end)) { - priv->select_info->selection_anchor = link->start; - priv->select_info->selection_end = link->start; + info->selection_anchor = link->start; + info->selection_end = link->start; _gtk_label_accessible_focus_link_changed (label); + gtk_widget_queue_draw (widget); break; } } @@ -1163,6 +1170,129 @@ focus_in_cb (GtkEventControllerKey *controller, } } +static gboolean +key_press_cb (GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType modifiers, + GtkWidget *widget) +{ + GtkLabel *label = GTK_LABEL (widget); + GtkLabelPrivate *priv = gtk_label_get_instance_private (label); + GtkLabelSelectionInfo *info = priv->select_info; + GtkDirectionType direction; + GtkLabelLink *focus_link; + GList *l; + + if (keyval != GDK_KEY_Tab && keyval != GDK_KEY_KP_Tab) + return FALSE; + + if ((modifiers & GDK_SHIFT_MASK) != 0) + direction = GTK_DIR_TAB_BACKWARD; + else + direction = GTK_DIR_TAB_FORWARD; + + if (!info) + return FALSE; + + if (info->selectable) + { + int index; + int i; + + if (info->selection_anchor != info->selection_end) + return FALSE; + + index = info->selection_anchor; + + if (direction == GTK_DIR_TAB_FORWARD) + { + for (l = info->links, i = 0; l; l = l->next, i++) + { + GtkLabelLink *link = l->data; + + if (link->start > index) + { + if (!range_is_in_ellipsis (label, link->start, link->end)) + { + gtk_label_select_region_index (label, link->start, link->start); + _gtk_label_accessible_focus_link_changed (label); + gtk_widget_queue_draw (widget); + return TRUE; + } + } + } + } + else + { + for (l = g_list_last (info->links), i = g_list_length (info->links) - 1; l; l = l->prev, i--) + { + GtkLabelLink *link = l->data; + + if (link->end < index) + { + if (!range_is_in_ellipsis (label, link->start, link->end)) + { + gtk_label_select_region_index (label, link->start, link->start); + _gtk_label_accessible_focus_link_changed (label); + gtk_widget_queue_draw (widget); + return TRUE; + } + } + } + } + } + else + { + focus_link = gtk_label_get_focus_link (label); + if (direction == GTK_DIR_TAB_FORWARD) + { + if (focus_link) + { + l = g_list_find (info->links, focus_link); + l = l->next; + } + else + l = info->links; + for (; l; l = l->next) + { + GtkLabelLink *link = l->data; + if (!range_is_in_ellipsis (label, link->start, link->end)) + break; + } + } + else + { + if (focus_link) + { + l = g_list_find (info->links, focus_link); + l = l->prev; + } + else + l = g_list_last (info->links); + for (; l; l = l->prev) + { + GtkLabelLink *link = l->data; + if (!range_is_in_ellipsis (label, link->start, link->end)) + break; + } + } + + if (l) + { + focus_link = l->data; + info->selection_anchor = focus_link->start; + info->selection_end = focus_link->start; + _gtk_label_accessible_focus_link_changed (label); + gtk_widget_queue_draw (widget); + + return TRUE; + } + } + + return FALSE; +} + static void gtk_label_set_property (GObject *object, guint prop_id, @@ -1348,6 +1478,7 @@ gtk_label_init (GtkLabel *label) controller = gtk_event_controller_key_new (); g_signal_connect (controller, "focus-in", G_CALLBACK (focus_in_cb), label); + g_signal_connect (controller, "key-pressed", G_CALLBACK (key_press_cb), label); gtk_widget_add_controller (GTK_WIDGET (label), controller); } -- GitLab From 767a478919d3e90c083b76faaaaeccd64e77eb65 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 3 Mar 2019 14:18:25 -0500 Subject: [PATCH 32/70] Factor out a common function Use the same helper function to determine if a widget can be made the focus-widget in all the places where it is what we want to know. The helper function takes into account can-focus, sensitive and child-focusable. --- gtk/gtkwidgetfocus.c | 22 +++++++++++++++++++++- gtk/gtkwidgetprivate.h | 2 ++ gtk/gtkwindow.c | 21 +-------------------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c index 691275d7018..230f76d19e5 100644 --- a/gtk/gtkwidgetfocus.c +++ b/gtk/gtkwidgetfocus.c @@ -449,6 +449,25 @@ gtk_widget_focus_sort (GtkWidget *widget, } } +gboolean +gtk_widget_can_take_focus (GtkWidget *widget) +{ + GtkWidget *w; + + if (!gtk_widget_is_sensitive (widget) || + !gtk_widget_get_can_focus (widget)) + return FALSE; + + w = widget; + do { + if (!gtk_widget_get_child_focusable (w)) + return FALSE; + w = gtk_widget_get_parent (w); + } while (w != NULL); + + return TRUE; +} + /** * gtk_widget_get_next_focus: * @widget: a #GtkWidget @@ -477,7 +496,7 @@ gtk_widget_get_next_focus (GtkWidget *widget, prev = widget; widget = gtk_widget_get_parent (widget); } - else if (gtk_widget_get_can_focus (next)) + else if (gtk_widget_can_take_focus (next)) { break; } @@ -495,6 +514,7 @@ gtk_widget_get_next_focus (GtkWidget *widget, } while (widget); g_hash_table_unref (seen); + return next; } diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index b4089566d40..639ecafc7eb 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -339,6 +339,8 @@ gboolean gtk_widget_run_controllers (GtkWidget gboolean gtk_widget_send_focus_change (GtkWidget *widget, GdkEvent *event); +gboolean gtk_widget_can_take_focus (GtkWidget *widget); + /* inline getters */ diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index af902229d50..2b131641f72 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -7122,25 +7122,6 @@ set_focus_widget (GtkWindow *window, do_focus_change (priv->focus_widget, TRUE); } -static gboolean -can_focus_widget (GtkWidget *widget) -{ - GtkWidget *w; - - if (!gtk_widget_is_sensitive (widget) || - !gtk_widget_get_can_focus (widget)) - return FALSE; - - w = widget; - do { - if (!gtk_widget_get_child_focusable (w)) - return FALSE; - w = gtk_widget_get_parent (w); - } while (w != NULL); - - return TRUE; -} - /** * gtk_window_set_focus: * @window: a #GtkWindow @@ -7159,7 +7140,7 @@ gtk_window_set_focus (GtkWindow *window, { g_return_if_fail (GTK_IS_WINDOW (window)); - if (focus && !can_focus_widget (focus)) + if (focus && !gtk_widget_can_take_focus (focus)) return; unset_focus_widget (window); -- GitLab From ead299b12de45a868cfabf4af7fd4e2f7850f78b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 4 Mar 2019 19:00:30 -0500 Subject: [PATCH 33/70] Add a missing include If we don't include config.h, GDK_AVAILABLE_... does not have the desired effect, and your symbols are not exported. And you spend hours tearing out your hair. --- gtk/gtkwidgetfocus.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c index 230f76d19e5..a3802957988 100644 --- a/gtk/gtkwidgetfocus.c +++ b/gtk/gtkwidgetfocus.c @@ -15,6 +15,8 @@ * License along with this library. If not, see . */ +#include "config.h" + #include "gtkwidgetprivate.h" typedef struct _CompareInfo CompareInfo; -- GitLab From 865cd272bc3ae65e11316c97c9d79e3b36e72772 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 4 Mar 2019 20:31:20 -0500 Subject: [PATCH 34/70] root: Don't allow setting nonsense focus Don't allow setting focus-widget to something thats not inside the root. --- gtk/gtkroot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gtk/gtkroot.c b/gtk/gtkroot.c index 249d7405b1b..3ed7812856e 100644 --- a/gtk/gtkroot.c +++ b/gtk/gtkroot.c @@ -152,7 +152,8 @@ gtk_root_set_focus (GtkRoot *self, GtkWidget *focus) { g_return_if_fail (GTK_IS_ROOT (self)); - g_return_if_fail (focus == NULL || GTK_IS_WIDGET (focus)); + g_return_if_fail (focus == NULL || + (GTK_IS_WIDGET (focus) && gtk_widget_get_root (focus) == self)); g_object_set (self, "focus-widget", focus, NULL); } -- GitLab From ed6830a2f0e1fde7a410db4595785b0776dff251 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 4 Mar 2019 22:44:50 -0500 Subject: [PATCH 35/70] Force focus chain to be cyclic Move the enforcement of focus chain wraparound to gtk_widget_get_next_focus. This lets next_focus_child implementation be simpler. --- gtk/gtkwidgetfocus.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c index a3802957988..e6f4e3060fe 100644 --- a/gtk/gtkwidgetfocus.c +++ b/gtk/gtkwidgetfocus.c @@ -495,8 +495,15 @@ gtk_widget_get_next_focus (GtkWidget *widget, next = GTK_WIDGET_GET_CLASS (widget)->next_focus_child (widget, prev, dir); if (next == NULL) { - prev = widget; - widget = gtk_widget_get_parent (widget); + if (GTK_IS_ROOT (widget)) + { + prev = NULL; + } + else + { + prev = widget; + widget = gtk_widget_get_parent (widget); + } } else if (gtk_widget_can_take_focus (next)) { -- GitLab From 12456d71b17c648e1d4882baa1e12fd0c8d8f6a2 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 4 Mar 2019 22:48:24 -0500 Subject: [PATCH 36/70] window: Drop next_focus_child implementation Not needed anymore, now that the cycling is enforced. --- gtk/gtkwindow.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 2b131641f72..6b6b462e972 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -760,27 +760,6 @@ gtk_window_pick (GtkWidget *widget, return GTK_WIDGET_CLASS (gtk_window_parent_class)->pick (widget, x, y); } -static GtkWidget * -gtk_window_next_focus_child (GtkWidget *widget, - GtkWidget *child, - GtkDirectionType direction) -{ - GtkWidget *next; - - next = GTK_WIDGET_CLASS (gtk_window_parent_class)->next_focus_child (widget, child, direction); - if (next) - return next; - - switch ((int)direction) - { - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_TAB_BACKWARD: - return GTK_WIDGET_CLASS (gtk_window_parent_class)->next_focus_child (widget, NULL, direction); - default: - return NULL; - } -} - static void gtk_window_class_init (GtkWindowClass *klass) { @@ -819,7 +798,6 @@ gtk_window_class_init (GtkWindowClass *klass) widget_class->style_updated = gtk_window_style_updated; widget_class->snapshot = gtk_window_snapshot; widget_class->pick = gtk_window_pick; - widget_class->next_focus_child = gtk_window_next_focus_child; container_class->add = gtk_window_add; container_class->remove = gtk_window_remove; -- GitLab From 87d2aa74d38d8df9067706012535353920e86e33 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 4 Mar 2019 20:15:39 -0500 Subject: [PATCH 37/70] Clarify grab_focus docs --- gtk/gtkwidget.c | 25 +++++++++++++------------ gtk/gtkwidget.h | 5 ++++- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 289f864b46b..12161890a4c 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -5303,16 +5303,11 @@ _gtk_widget_grab_notify (GtkWidget *widget, * gtk_widget_grab_focus: * @widget: a #GtkWidget * - * Causes @widget to have the keyboard focus for the #GtkWindow it's - * inside. @widget must be a focusable widget, such as a #GtkEntry; - * something like #GtkFrame won’t work. + * Causes @widget (or one of its descendents) to have the keyboard focus + * for the #GtkWindow it's inside. * - * More precisely, it must have the %GTK_CAN_FOCUS flag set. Use - * gtk_widget_set_can_focus() to modify that flag. - * - * The widget also needs to be realized and mapped. This is indicated by the - * related signals. Grabbing the focus immediately after creating the widget - * will likely fail and cause critical warnings. + * @widget must be focusable, or have a ::grab_focus implementation that + * transfers the focus to a descendant of @widget that is focusable. **/ void gtk_widget_grab_focus (GtkWidget *widget) @@ -5468,9 +5463,15 @@ gtk_widget_real_keynav_failed (GtkWidget *widget, * @widget: a #GtkWidget * @can_focus: whether or not @widget can own the input focus. * - * Specifies whether @widget can own the input focus. See - * gtk_widget_grab_focus() for actually setting the input focus on a - * widget. + * Specifies whether @widget can own the input focus. + * + * Note that having @can_focus be %TRUE is only one of the + * necessary conditions for being focusable. A widget must + * also be sensitive and not have a ancestor that is marked + * as not child-focusable in order to receive input focus. + * + * See gtk_widget_grab_focus() for actually setting the input + * focus on a widget. **/ void gtk_widget_set_can_focus (GtkWidget *widget, diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index d7f906d52a4..df93e95f1e3 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -185,7 +185,10 @@ struct _GtkWidget * %FALSE, and just grabs the focus if @group_cycling is %TRUE. * @grab_focus: Causes @widget to have the keyboard focus for the * #GtkWindow it’s inside. - * @next_focus_child: Returns the next child that is a candidate for receiving focus + * @next_focus_child: Returns the next child that is a candidate for receiving focus. + * Note that the returned child does not have to be focusable itself, it might just + * contain focusable children. The default implementation returns all children, in + * their logical order. * @move_focus: Signal emitted when a change of focus is requested * @keynav_failed: Signal emitted if keyboard navigation fails. * @drag_begin: Signal emitted on the drag source when a drag is -- GitLab From 8db0ba6745073763e911f957aaf5656e12a7dcab Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 4 Mar 2019 18:08:18 -0500 Subject: [PATCH 38/70] Add a focus-chain test This test takes ui files and compares the focus chain with the expected output, as usual. ui files go in the testsuite/gtk/focus-chain directory, and expected output in the form of one widget ID per line goes in .txt files. --- testsuite/gtk/focus-chain/focus.test.in | 4 + testsuite/gtk/focus-chain/meson.build | 34 +++ testsuite/gtk/focus-chain/notebook.txt | 2 + testsuite/gtk/focus-chain/notebook.ui | 37 +++ testsuite/gtk/focus-chain/test-focus.c | 297 ++++++++++++++++++++++++ testsuite/gtk/meson.build | 2 + 6 files changed, 376 insertions(+) create mode 100644 testsuite/gtk/focus-chain/focus.test.in create mode 100644 testsuite/gtk/focus-chain/meson.build create mode 100644 testsuite/gtk/focus-chain/notebook.txt create mode 100644 testsuite/gtk/focus-chain/notebook.ui create mode 100644 testsuite/gtk/focus-chain/test-focus.c diff --git a/testsuite/gtk/focus-chain/focus.test.in b/testsuite/gtk/focus-chain/focus.test.in new file mode 100644 index 00000000000..2b15a4cf0a2 --- /dev/null +++ b/testsuite/gtk/focus-chain/focus.test.in @@ -0,0 +1,4 @@ +[Test] +Exec=@libexecdir@/installed-tests/gtk-4.0/gtk/focus/test-focus --tap -k +Type=session +Output=TAP diff --git a/testsuite/gtk/focus-chain/meson.build b/testsuite/gtk/focus-chain/meson.build new file mode 100644 index 00000000000..4de0970d378 --- /dev/null +++ b/testsuite/gtk/focus-chain/meson.build @@ -0,0 +1,34 @@ +testexecdir = join_paths(installed_test_bindir, 'gtk', 'focus-chain') +testdatadir = join_paths(installed_test_datadir, 'gtk', 'focus-chain') + +test_nodes = executable('test-focus', 'test-focus.c', + install: get_option('install-tests'), + install_dir: testexecdir, + dependencies: libgtk_dep) +test('nodes', test_nodes, + args: [ '--tap', '-k' ], + env: [ 'GIO_USE_VOLUME_MONITOR=unix', + 'GSETTINGS_BACKEND=memory', + 'GTK_CSD=1', + 'G_ENABLE_DIAGNOSTIC=0', + 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), + 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()) + ], + suite: 'css') + +test_data = [ + 'widget-factory.ui', + 'widget-factory.txt', +] + +if get_option('install-tests') + conf = configuration_data() + conf.set('libexecdir', gtk_libexecdir) + configure_file(input: 'focus.test.in', + output: 'focus.test', + configuration: conf, + install_dir: testdatadir) + + install_data(test_data, install_dir: testexecdir) + +endif diff --git a/testsuite/gtk/focus-chain/notebook.txt b/testsuite/gtk/focus-chain/notebook.txt new file mode 100644 index 00000000000..3fb79f17b02 --- /dev/null +++ b/testsuite/gtk/focus-chain/notebook.txt @@ -0,0 +1,2 @@ +notebook1 +page1 diff --git a/testsuite/gtk/focus-chain/notebook.ui b/testsuite/gtk/focus-chain/notebook.ui new file mode 100644 index 00000000000..e18759ca427 --- /dev/null +++ b/testsuite/gtk/focus-chain/notebook.ui @@ -0,0 +1,37 @@ + + + popup + + + + + + + Yes + + + + + Tab 1 + + + + + + + + + No + + + + + Tab 2 + + + + + + + + diff --git a/testsuite/gtk/focus-chain/test-focus.c b/testsuite/gtk/focus-chain/test-focus.c new file mode 100644 index 00000000000..468d0f4c5f7 --- /dev/null +++ b/testsuite/gtk/focus-chain/test-focus.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2019 Red Hat Inc. + * + * Author: + * Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include +#include +#include + +#ifdef G_OS_WIN32 +# include +#endif + + +static char * +test_get_reference_file (const char *ui_file) +{ + GString *file = g_string_new (NULL); + + if (g_str_has_suffix (ui_file, ".ui")) + g_string_append_len (file, ui_file, strlen (ui_file) - 3); + else + g_string_append (file, ui_file); + + g_string_append (file, ".txt"); + + if (!g_file_test (file->str, G_FILE_TEST_EXISTS)) + { + g_string_free (file, TRUE); + return g_strdup (ui_file); + } + + return g_string_free (file, FALSE); +} + +static char * +diff_with_file (const char *file1, + char *text, + gssize len, + GError **error) +{ + const char *command[] = { "diff", "-u", file1, NULL, NULL }; + char *diff, *tmpfile; + int fd; + + diff = NULL; + + if (len < 0) + len = strlen (text); + + /* write the text buffer to a temporary file */ + fd = g_file_open_tmp (NULL, &tmpfile, error); + if (fd < 0) + return NULL; + + if (write (fd, text, len) != (int) len) + { + close (fd); + g_set_error (error, + G_FILE_ERROR, G_FILE_ERROR_FAILED, + "Could not write data to temporary file '%s'", tmpfile); + goto done; + } + close (fd); + command[3] = tmpfile; + + /* run diff command */ + g_spawn_sync (NULL, + (char **) command, + NULL, + G_SPAWN_SEARCH_PATH, + NULL, NULL, + &diff, + NULL, NULL, + error); + +done: + g_unlink (tmpfile); + g_free (tmpfile); + + return diff; +} + +static char * +generate_output (GtkWidget *window) +{ + GString *s = g_string_new (""); + GtkWidget *initial_focus; + GtkWidget *f; + + initial_focus = gtk_root_get_focus (GTK_ROOT (window)); + if (initial_focus == NULL) + { + g_string_append (s, "NULL\n"); + initial_focus = gtk_widget_get_next_focus (window, GTK_DIR_TAB_FORWARD); + } + + f = initial_focus; + while (f) + { + const char *name = gtk_buildable_get_name (GTK_BUILDABLE (f)); + if (name == NULL) + name = G_OBJECT_TYPE_NAME (f); + g_string_append_printf (s, "%s\n", name); + f = gtk_widget_get_next_focus (f, GTK_DIR_TAB_FORWARD); + if (f == initial_focus) + break; + } + + if (f == NULL) + g_string_append (s, "NULL\n"); + + return g_string_free (s, FALSE); +} + +static void +load_ui_file (GFile *file, gboolean generate) +{ + GtkBuilder *builder; + GtkWidget *window; + char *output, *diff; + char *ui_file, *reference_file; + GError *error = NULL; + + ui_file = g_file_get_path (file); + + builder = gtk_builder_new_from_file (ui_file); + window = GTK_WIDGET (gtk_builder_get_object (builder, "window1")); + + g_assert (window != NULL); + + gtk_widget_show (window); + + output = generate_output (window); + + if (generate) + { + g_print ("%s", output); + goto out; + } + + reference_file = test_get_reference_file (ui_file); + + diff = diff_with_file (reference_file, output, -1, &error); + g_assert_no_error (error); + + if (diff && diff[0]) + { + g_test_message ("Resulting output doesn't match reference:\n%s", diff); + g_test_fail (); + } + g_free (reference_file); + g_free (diff); + +out: + g_free (output); + g_free (ui_file); +} + +static void +test_ui_file (GFile *file) +{ + load_ui_file (file, FALSE); +} + +static void +add_test_for_file (GFile *file) +{ + char *path; + + path = g_file_get_path (file); + + g_test_add_vtable (path, + 0, + g_object_ref (file), + NULL, + (GTestFixtureFunc) test_ui_file, + (GTestFixtureFunc) g_object_unref); + + g_free (path); +} + +static int +compare_files (gconstpointer a, gconstpointer b) +{ + GFile *file1 = G_FILE (a); + GFile *file2 = G_FILE (b); + char *path1, *path2; + int result; + + path1 = g_file_get_path (file1); + path2 = g_file_get_path (file2); + + result = strcmp (path1, path2); + + g_free (path1); + g_free (path2); + + return result; +} + +static void +add_tests_for_files_in_directory (GFile *dir) +{ + GFileEnumerator *enumerator; + GFileInfo *info; + GList *files; + GError *error = NULL; + + enumerator = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &error); + g_assert_no_error (error); + files = NULL; + + while ((info = g_file_enumerator_next_file (enumerator, NULL, &error))) + { + const char *filename; + + filename = g_file_info_get_name (info); + + if (!g_str_has_suffix (filename, ".ui") || + g_str_has_suffix (filename, ".txt")) + { + g_object_unref (info); + continue; + } + + files = g_list_prepend (files, g_file_get_child (dir, filename)); + + g_object_unref (info); + } + + g_assert_no_error (error); + g_object_unref (enumerator); + + files = g_list_sort (files, compare_files); + g_list_foreach (files, (GFunc) add_test_for_file, NULL); + g_list_free_full (files, g_object_unref); +} + +int +main (int argc, char *argv[]) +{ + gtk_test_init (&argc, &argv); + + if (argc < 2) + { + const char *basedir; + GFile *dir; + + basedir = g_test_get_dir (G_TEST_DIST); + dir = g_file_new_for_path (basedir); + add_tests_for_files_in_directory (dir); + + g_object_unref (dir); + } + else if (strcmp (argv[1], "--generate") == 0) + { + if (argc >= 3) + { + GFile *file = g_file_new_for_commandline_arg (argv[2]); + + load_ui_file (file, TRUE); + + g_object_unref (file); + } + } + else + { + guint i; + + for (i = 1; i < argc; i++) + { + GFile *file = g_file_new_for_commandline_arg (argv[i]); + + add_test_for_file (file); + + g_object_unref (file); + } + } + + return g_test_run (); +} diff --git a/testsuite/gtk/meson.build b/testsuite/gtk/meson.build index 97e5f47fce8..f56a3d4c791 100644 --- a/testsuite/gtk/meson.build +++ b/testsuite/gtk/meson.build @@ -1,3 +1,5 @@ +subdir('focus-chain') + testexecdir = join_paths(installed_test_bindir, 'gtk') testdatadir = join_paths(installed_test_datadir, 'gtk') -- GitLab From 4bc6ca2a8395901d3b2ca91e75d03e89bcd2c926 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 4 Mar 2019 19:53:33 -0500 Subject: [PATCH 39/70] Add some stack focus tests --- testsuite/gtk/focus-chain/stack.txt | 3 +++ testsuite/gtk/focus-chain/stack.ui | 40 ++++++++++++++++++++++++++++ testsuite/gtk/focus-chain/stack2.txt | 3 +++ testsuite/gtk/focus-chain/stack2.ui | 40 ++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 testsuite/gtk/focus-chain/stack.txt create mode 100644 testsuite/gtk/focus-chain/stack.ui create mode 100644 testsuite/gtk/focus-chain/stack2.txt create mode 100644 testsuite/gtk/focus-chain/stack2.ui diff --git a/testsuite/gtk/focus-chain/stack.txt b/testsuite/gtk/focus-chain/stack.txt new file mode 100644 index 00000000000..86879240260 --- /dev/null +++ b/testsuite/gtk/focus-chain/stack.txt @@ -0,0 +1,3 @@ +GtkToggleButton +GtkToggleButton +page1 diff --git a/testsuite/gtk/focus-chain/stack.ui b/testsuite/gtk/focus-chain/stack.ui new file mode 100644 index 00000000000..2ce89fd0342 --- /dev/null +++ b/testsuite/gtk/focus-chain/stack.ui @@ -0,0 +1,40 @@ + + + popup + + + + + stack1 + + + + + + + page1 + Page 1 + + + Yes + + + + + + + page2 + Page 2 + + + No + + + + + + + + + + diff --git a/testsuite/gtk/focus-chain/stack2.txt b/testsuite/gtk/focus-chain/stack2.txt new file mode 100644 index 00000000000..09079ee932c --- /dev/null +++ b/testsuite/gtk/focus-chain/stack2.txt @@ -0,0 +1,3 @@ +page1 +GtkToggleButton +GtkToggleButton diff --git a/testsuite/gtk/focus-chain/stack2.ui b/testsuite/gtk/focus-chain/stack2.ui new file mode 100644 index 00000000000..3b43c3f3c42 --- /dev/null +++ b/testsuite/gtk/focus-chain/stack2.ui @@ -0,0 +1,40 @@ + + + popup + + + + + + + page1 + Page 1 + + + Yes + + + + + + + page2 + Page 2 + + + No + + + + + + + + + stack1 + + + + + + -- GitLab From 7d7288eedbe420ccc144462a08e703f1f3d54191 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 4 Mar 2019 22:27:30 -0500 Subject: [PATCH 40/70] Add a nested boxes focus test --- testsuite/gtk/focus-chain/boxes.txt | 9 +++++ testsuite/gtk/focus-chain/boxes.ui | 60 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 testsuite/gtk/focus-chain/boxes.txt create mode 100644 testsuite/gtk/focus-chain/boxes.ui diff --git a/testsuite/gtk/focus-chain/boxes.txt b/testsuite/gtk/focus-chain/boxes.txt new file mode 100644 index 00000000000..623adf43f78 --- /dev/null +++ b/testsuite/gtk/focus-chain/boxes.txt @@ -0,0 +1,9 @@ +button1 +button2 +button3 +button4 +button5 +button6 +button7 +button8 +button9 diff --git a/testsuite/gtk/focus-chain/boxes.ui b/testsuite/gtk/focus-chain/boxes.ui new file mode 100644 index 00000000000..1bed0042a71 --- /dev/null +++ b/testsuite/gtk/focus-chain/boxes.ui @@ -0,0 +1,60 @@ + + + + + horizontal + + + vertical + + + + + + + + + + + + + + + + + vertical + + + + + + + + + + + + + + + + + vertical + + + + + + + + + + + + + + + + + + -- GitLab From da73ff62ddf00f3a49292989a33d4843e926bf5b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 6 Mar 2019 16:55:27 -0500 Subject: [PATCH 41/70] gdk: Add crossing mode and detail to focus events We want focus events more similar to crossing events. --- gdk/gdkevents.c | 10 ++++++++++ gdk/gdkeventsprivate.h | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index 3b0944d3cc4..55b744d4f34 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -1980,6 +1980,11 @@ gdk_event_get_crossing_mode (const GdkEvent *event, *mode = event->crossing.mode; return TRUE; } + else if (event->any.type == GDK_FOCUS_CHANGE) + { + *mode = event->focus_change.mode; + return TRUE; + } return FALSE; } @@ -2006,6 +2011,11 @@ gdk_event_get_crossing_detail (const GdkEvent *event, *detail = event->crossing.detail; return TRUE; } + else if (event->any.type == GDK_FOCUS_CHANGE) + { + *detail = event->focus_change.detail; + return TRUE; + } return FALSE; } diff --git a/gdk/gdkeventsprivate.h b/gdk/gdkeventsprivate.h index b81faba5895..1c6756e60af 100644 --- a/gdk/gdkeventsprivate.h +++ b/gdk/gdkeventsprivate.h @@ -312,6 +312,8 @@ struct _GdkEventCrossing * @send_event: %TRUE if the event was sent explicitly. * @in: %TRUE if the surface has gained the keyboard focus, %FALSE if * it has lost the focus. + * @mode: the crossing mode + * @detail: the kind of crossing that happened * * Describes a change of keyboard focus. */ @@ -319,6 +321,8 @@ struct _GdkEventFocus { GdkEventAny any; gint16 in; + GdkCrossingMode mode; + GdkNotifyType detail; }; /* -- GitLab From 684468f3d245cbbb4a624d32f1d453ba2a7de692 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 11:27:16 -0500 Subject: [PATCH 42/70] gdk: Rename gdk_event_set_user_data Lets call it what it is, the target. All of this is private api anyway. Update all callers. --- gdk/gdk-private.h | 2 -- gdk/gdkevents.c | 16 ++++++++-------- gdk/gdkeventsprivate.h | 7 ++++--- gtk/gtkmain.c | 6 +++--- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/gdk/gdk-private.h b/gdk/gdk-private.h index 9344ac2e497..b5551b750cb 100644 --- a/gdk/gdk-private.h +++ b/gdk/gdk-private.h @@ -20,8 +20,6 @@ void gdk_surface_thaw_toplevel_updates (GdkSurface *surface); gboolean gdk_surface_supports_edge_constraints (GdkSurface *surface); -GObject * gdk_event_get_user_data (const GdkEvent *event); - guint32 gdk_display_get_last_seen_time (GdkDisplay *display); void gdk_display_set_double_click_time (GdkDisplay *display, diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index 55b744d4f34..bacc1eb7774 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -564,8 +564,8 @@ gdk_event_copy (const GdkEvent *event) g_object_ref (new_event->any.device); if (new_event->any.source_device) g_object_ref (new_event->any.source_device); - if (new_event->any.user_data) - g_object_ref (new_event->any.user_data); + if (new_event->any.target) + g_object_ref (new_event->any.target); switch ((guint) event->any.type) { @@ -675,7 +675,7 @@ gdk_event_finalize (GObject *object) g_clear_object (&event->any.device); g_clear_object (&event->any.source_device); - g_clear_object (&event->any.user_data); + g_clear_object (&event->any.target); G_OBJECT_CLASS (gdk_event_parent_class)->finalize (object); } @@ -1904,16 +1904,16 @@ gdk_event_get_scancode (GdkEvent *event) } void -gdk_event_set_user_data (GdkEvent *event, - GObject *user_data) +gdk_event_set_target (GdkEvent *event, + GObject *target) { - g_set_object (&event->any.user_data, user_data); + g_set_object (&event->any.target, target); } GObject * -gdk_event_get_user_data (const GdkEvent *event) +gdk_event_get_target (const GdkEvent *event) { - return event->any.user_data; + return event->any.target; } /** diff --git a/gdk/gdkeventsprivate.h b/gdk/gdkeventsprivate.h index 1c6756e60af..f3ccd3d9f79 100644 --- a/gdk/gdkeventsprivate.h +++ b/gdk/gdkeventsprivate.h @@ -61,7 +61,7 @@ struct _GdkEventAny GdkDevice *device; GdkDevice *source_device; GdkDisplay *display; - GObject *user_data; + GObject *target; }; /* @@ -636,8 +636,9 @@ union _GdkEvent GdkEventPadGroupMode pad_group_mode; }; -void gdk_event_set_user_data (GdkEvent *event, - GObject *user_data); +void gdk_event_set_target (GdkEvent *event, + GObject *user_data); +GObject * gdk_event_get_target (const GdkEvent *event); #endif /* __GDK_EVENTS_PRIVATE_H__ */ diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index be19d7389cd..9cc8c22e7d8 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1416,7 +1416,7 @@ synth_crossing (GtkWidget *widget, gdouble x, y; event = gdk_event_new (enter ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY); - gdk_event_set_user_data (event, G_OBJECT (widget)); + gdk_event_set_target (event, G_OBJECT (widget)); gdk_event_set_device (event, gdk_event_get_device (source)); gdk_event_set_source_device (event, gdk_event_get_source_device (source)); @@ -1779,7 +1779,7 @@ gtk_main_do_event (GdkEvent *event) if (!target_widget) goto cleanup; - gdk_event_set_user_data (event, G_OBJECT (target_widget)); + gdk_event_set_target (event, G_OBJECT (target_widget)); window_group = gtk_main_get_window_group (target_widget); device = gdk_event_get_device (event); @@ -2398,7 +2398,7 @@ gtk_get_event_widget (const GdkEvent *event) GtkWidget * gtk_get_event_target (const GdkEvent *event) { - return GTK_WIDGET (gdk_event_get_user_data (event)); + return GTK_WIDGET (gdk_event_get_target (event)); } /** -- GitLab From a6c17846626433b1cb83c21c41fd2022389353b3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 11:12:30 -0500 Subject: [PATCH 43/70] gdk: Add a related_target field to some events Crossing and focus change events are emitted between two widgets, and want to associate both with the events. --- gdk/gdkevents.c | 35 +++++++++++++++++++++++++++++++++++ gdk/gdkeventsprivate.h | 5 +++++ 2 files changed, 40 insertions(+) diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index bacc1eb7774..d9e9af6eeff 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -573,6 +573,13 @@ gdk_event_copy (const GdkEvent *event) case GDK_LEAVE_NOTIFY: if (event->crossing.child_surface != NULL) g_object_ref (event->crossing.child_surface); + if (event->crossing.related_target) + g_object_ref (event->crossing.related_target); + break; + + case GDK_FOCUS_CHANGE: + if (event->focus_change.related_target) + g_object_ref (event->focus_change.related_target); break; case GDK_DRAG_ENTER: @@ -634,6 +641,11 @@ gdk_event_finalize (GObject *object) case GDK_ENTER_NOTIFY: case GDK_LEAVE_NOTIFY: g_clear_object (&event->crossing.child_surface); + g_clear_object (&event->crossing.related_target); + break; + + case GDK_FOCUS_CHANGE: + g_clear_object (&event->focus_change.related_target); break; case GDK_DRAG_ENTER: @@ -1916,6 +1928,29 @@ gdk_event_get_target (const GdkEvent *event) return event->any.target; } +void +gdk_event_set_related_target (GdkEvent *event, + GObject *target) +{ + if (event->any.type == GDK_ENTER_NOTIFY || + event->any.type == GDK_LEAVE_NOTIFY) + g_set_object (&event->crossing.related_target, target); + else if (event->any.type == GDK_FOCUS_CHANGE) + g_set_object (&event->focus_change.related_target, target); +} + +GObject * +gdk_event_get_related_target (const GdkEvent *event) +{ + if (event->any.type == GDK_ENTER_NOTIFY || + event->any.type == GDK_LEAVE_NOTIFY) + return event->crossing.related_target; + else if (event->any.type == GDK_FOCUS_CHANGE) + return event->focus_change.related_target; + + return NULL; +} + /** * gdk_event_is_sent: * @event: a #GdkEvent diff --git a/gdk/gdkeventsprivate.h b/gdk/gdkeventsprivate.h index f3ccd3d9f79..2db43d9a828 100644 --- a/gdk/gdkeventsprivate.h +++ b/gdk/gdkeventsprivate.h @@ -303,6 +303,7 @@ struct _GdkEventCrossing GdkNotifyType detail; gboolean focus; guint state; + GObject *related_target; }; /* @@ -323,6 +324,7 @@ struct _GdkEventFocus gint16 in; GdkCrossingMode mode; GdkNotifyType detail; + GObject *related_target; }; /* @@ -639,6 +641,9 @@ union _GdkEvent void gdk_event_set_target (GdkEvent *event, GObject *user_data); GObject * gdk_event_get_target (const GdkEvent *event); +void gdk_event_set_related_target (GdkEvent *event, + GObject *user_data); +GObject * gdk_event_get_related_target (const GdkEvent *event); #endif /* __GDK_EVENTS_PRIVATE_H__ */ -- GitLab From 3ef019be6a0fe6c97bab407c47fe3a0f489275e3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 6 Mar 2019 22:26:27 -0500 Subject: [PATCH 44/70] widget: Add a private setter for has-focus --- gtk/gtkwidget.c | 13 +++++++++++++ gtk/gtkwidgetprivate.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 12161890a4c..628b6f8f9bd 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -11611,6 +11611,19 @@ gtk_widget_send_focus_change (GtkWidget *widget, return TRUE; } +void +gtk_widget_set_has_focus (GtkWidget *widget, + gboolean has_focus) +{ + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); + + if (priv->has_focus == has_focus) + return; + + priv->has_focus = has_focus; + g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_HAS_FOCUS]); +} + /** * gtk_widget_in_destruction: * @widget: a #GtkWidget diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index 639ecafc7eb..fb449bae874 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -320,6 +320,8 @@ void gtk_widget_focus_sort (GtkWidget *wi GtkWidget * gtk_widget_next_focus_child (GtkWidget *widget, GtkWidget *child, GtkDirectionType direction); +void gtk_widget_set_has_focus (GtkWidget *widget, + gboolean has_focus); void gtk_widget_get_surface_allocation (GtkWidget *widget, GtkAllocation *allocation); -- GitLab From 54fd25a6a190ce54d6fd1755cec044e2de3b0251 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 6 Mar 2019 22:25:31 -0500 Subject: [PATCH 45/70] Share crossing and focus change event code Make a single function that can emit both enter/leave and focus change events. --- gtk/gtkmain.c | 98 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 9cc8c22e7d8..d5c7b9d1434 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1413,55 +1413,57 @@ synth_crossing (GtkWidget *widget, GdkCrossingMode crossing_mode) { GdkEvent *event; - gdouble x, y; + GtkStateFlags flags; + + if (gdk_event_get_event_type (source) == GDK_FOCUS_CHANGE) + { + event = gdk_event_new (GDK_FOCUS_CHANGE); + event->focus_change.in = enter; + event->focus_change.mode = crossing_mode; + event->focus_change.detail = notify_type; + + flags = GTK_STATE_FLAG_FOCUSED; + if (!GTK_IS_WINDOW (toplevel) || gtk_window_get_focus_visible (GTK_WINDOW (toplevel))) + flags |= GTK_STATE_FLAG_FOCUS_VISIBLE; + } + else + { + gdouble x, y; + event = gdk_event_new (enter ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY); + if (other_widget) + event->crossing.child_surface = g_object_ref (gtk_widget_get_surface (other_widget)); + gdk_event_get_coords (source, &x, &y); + event->crossing.x = x; + event->crossing.y = y; + event->crossing.mode = crossing_mode; + event->crossing.detail = notify_type; + + flags = GTK_STATE_FLAG_PRELIGHT; + } - event = gdk_event_new (enter ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY); - gdk_event_set_target (event, G_OBJECT (widget)); + gdk_event_set_target (event, G_OBJECT (target)); gdk_event_set_device (event, gdk_event_get_device (source)); gdk_event_set_source_device (event, gdk_event_get_source_device (source)); event->any.surface = g_object_ref (gtk_widget_get_surface (toplevel)); - if (other_widget) - event->crossing.child_surface = g_object_ref (gtk_widget_get_surface (other_widget)); if (enter) - gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE); + gtk_widget_set_state_flags (widget, flags, FALSE); else - gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_PRELIGHT); - - gdk_event_get_coords (source, &x, &y); - event->crossing.x = x; - event->crossing.y = y; - event->crossing.mode = crossing_mode; - event->crossing.detail = notify_type; + gtk_widget_unset_state_flags (widget, flags); + if (gdk_event_get_event_type (source) == GDK_FOCUS_CHANGE) + { + if (notify_type == GDK_NOTIFY_ANCESTOR || + notify_type == GDK_NOTIFY_INFERIOR || + notify_type == GDK_NOTIFY_NONLINEAR) + gtk_widget_set_has_focus (widget, enter); + } + gtk_widget_event (widget, event); g_object_unref (event); } -static GtkWidget * -update_pointer_focus_state (GtkWindow *toplevel, - GdkEvent *event, - GtkWidget *new_target) -{ - GtkWidget *old_target = NULL; - GdkEventSequence *sequence; - GdkDevice *device; - gdouble x, y; - - device = gdk_event_get_device (event); - sequence = gdk_event_get_event_sequence (event); - old_target = gtk_window_lookup_pointer_focus_widget (toplevel, device, sequence); - if (old_target == new_target) - return old_target; - - gdk_event_get_coords (event, &x, &y); - gtk_window_update_pointer_focus (toplevel, device, sequence, - new_target, x, y); - - return old_target; -} - static void gtk_synthesize_crossing_events (GtkWindow *toplevel, GtkWidget *old_target, @@ -1528,6 +1530,30 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel, } } + +static GtkWidget * +update_pointer_focus_state (GtkWindow *toplevel, + GdkEvent *event, + GtkWidget *new_target) +{ + GtkWidget *old_target = NULL; + GdkEventSequence *sequence; + GdkDevice *device; + gdouble x, y; + + device = gdk_event_get_device (event); + sequence = gdk_event_get_event_sequence (event); + old_target = gtk_window_lookup_pointer_focus_widget (toplevel, device, sequence); + if (old_target == new_target) + return old_target; + + gdk_event_get_coords (event, &x, &y); + gtk_window_update_pointer_focus (toplevel, device, sequence, + new_target, x, y); + + return old_target; +} + static gboolean is_pointing_event (GdkEvent *event) { -- GitLab From 364b3ad8deb397587748c53aeb0ae69557fc15f7 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 6 Mar 2019 22:51:19 -0500 Subject: [PATCH 46/70] drop gtk_widget_send_focus_change Replace this with gtk_widget_set_has_focus + gtk_widget_event. --- gtk/gtktreeview.c | 3 ++- gtk/gtkwidget.c | 50 ------------------------------------------ gtk/gtkwidgetprivate.h | 3 --- gtk/gtkwindow.c | 5 +++-- 4 files changed, 5 insertions(+), 56 deletions(-) diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index c7dc3b1d3d3..d9e2012dde3 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -9931,7 +9931,8 @@ send_focus_change (GtkWidget *widget, fevent->focus_change.in = in; gdk_event_set_device (fevent, device); - gtk_widget_send_focus_change (widget, fevent); + gtk_widget_set_has_focus (widget, in); + gtk_widget_event (widget, fevent); g_object_unref (fevent); } diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 628b6f8f9bd..94f95c4bf36 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -11561,56 +11561,6 @@ gtk_widget_get_overflow (GtkWidget *widget) return priv->overflow; } -/** - * gtk_widget_send_focus_change: - * @widget: a #GtkWidget - * @event: a #GdkEvent of type GDK_FOCUS_CHANGE - * - * Sends the focus change @event to @widget - * - * This function is not meant to be used by applications. The only time it - * should be used is when it is necessary for a #GtkWidget to assign focus - * to a widget that is semantically owned by the first widget even though - * it’s not a direct child - for instance, a search entry in a floating - * window similar to the quick search in #GtkTreeView. - * - * An example of its usage is: - * - * |[ - * GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE); - * - * fevent->focus_change.type = GDK_FOCUS_CHANGE; - * fevent->focus_change.in = TRUE; - * fevent->focus_change.surface = _gtk_widget_get_surface (widget); - * if (fevent->focus_change.surface != NULL) - * g_object_ref (fevent->focus_change.surface); - * - * gtk_widget_send_focus_change (widget, fevent); - * - * g_object_unref (event); - * ]| - * - * Returns: the return value from the event signal emission: %TRUE - * if the event was handled, and %FALSE otherwise - */ -gboolean -gtk_widget_send_focus_change (GtkWidget *widget, - GdkEvent *event) -{ - GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); - - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - g_return_val_if_fail (event != NULL && event->any.type == GDK_FOCUS_CHANGE, FALSE); - - priv->has_focus = event->focus_change.in; - - gtk_propagate_event (widget, event); - - g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_HAS_FOCUS]); - - return TRUE; -} - void gtk_widget_set_has_focus (GtkWidget *widget, gboolean has_focus) diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index fb449bae874..0b57dcea427 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -338,9 +338,6 @@ gboolean gtk_widget_run_controllers (GtkWidget const GdkEvent *event, GtkPropagationPhase phase); -gboolean gtk_widget_send_focus_change (GtkWidget *widget, - GdkEvent *event); - gboolean gtk_widget_can_take_focus (GtkWidget *widget); diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 6b6b462e972..7f4da962512 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -6901,7 +6901,7 @@ gtk_window_real_activate_focus (GtkWindow *window) static void do_focus_change (GtkWidget *widget, - gboolean in) + gboolean in) { GdkSeat *seat; GList *devices, *d; @@ -6930,7 +6930,8 @@ do_focus_change (GtkWidget *widget, fevent->focus_change.in = in; gdk_event_set_device (fevent, dev); - gtk_widget_send_focus_change (widget, fevent); + gtk_widget_set_has_focus (widget, in); + gtk_widget_event (widget, fevent); g_object_unref (fevent); } -- GitLab From 162122133c88e46ae3f9b1d20b23152f4fcebf18 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 6 Mar 2019 23:39:26 -0500 Subject: [PATCH 47/70] Avoid a critical --- gtk/gtkwidget.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 94f95c4bf36..d6e4154c229 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -7459,6 +7459,9 @@ gtk_widget_child_focus (GtkWidget *widget, g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + if (!gtk_widget_get_root (widget)) + return FALSE; + focus_child = gtk_root_get_focus (gtk_widget_get_root (widget)); if (focus_child) next_focus = gtk_widget_get_next_focus (focus_child, direction); -- GitLab From 1ba7ff4ae2ad4b85325dd4fd8fe706e740f01f2f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 6 Mar 2019 23:39:53 -0500 Subject: [PATCH 48/70] Export gtk_synthesize_crossing_events internally We want to use this for focus changes, now that it can emit focus change events. --- gtk/gtkmain.c | 2 +- gtk/gtkwidgetprivate.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index d5c7b9d1434..36a53a83f00 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1464,7 +1464,7 @@ synth_crossing (GtkWidget *widget, g_object_unref (event); } -static void +void gtk_synthesize_crossing_events (GtkWindow *toplevel, GtkWidget *old_target, GtkWidget *new_target, diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index 0b57dcea427..543173e8e0e 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -243,6 +243,12 @@ GdkSurface * _gtk_widget_get_device_surface (GtkWidget *widget, GdkDevice *device); GList * _gtk_widget_list_devices (GtkWidget *widget); +void gtk_synthesize_crossing_events (GtkWindow *toplevel, + GtkWidget *from, + GtkWidget *to, + GdkEvent *event, + GdkCrossingMode mode); + void _gtk_widget_synthesize_crossing (GtkWidget *from, GtkWidget *to, GdkDevice *device, -- GitLab From 56c862dc1fae7e054fad756426e4b89742edc5e3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 6 Mar 2019 23:40:29 -0500 Subject: [PATCH 49/70] window: Use gtk_synthesize_crossing_events Emit focus change events in the same way as crossing events. Also change the code to only emit focus change events for the master keyboard - we only maintain a single focus location, so sending multiple focus change events for different devices seems confusing. --- gtk/gtkwindow.c | 119 +++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 68 deletions(-) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 7f4da962512..32edc43723e 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -6904,40 +6904,28 @@ do_focus_change (GtkWidget *widget, gboolean in) { GdkSeat *seat; - GList *devices, *d; - - g_object_ref (widget); + GdkDevice *device; + GdkEvent *event; seat = gdk_display_get_default_seat (gtk_widget_get_display (widget)); - devices = gdk_seat_get_slaves (seat, GDK_SEAT_CAPABILITY_KEYBOARD); - devices = g_list_prepend (devices, gdk_seat_get_keyboard (seat)); - - for (d = devices; d; d = d->next) - { - GdkDevice *dev = d->data; - GdkEvent *fevent; - GdkSurface *surface; - - surface = _gtk_widget_get_surface (widget); + device = gdk_seat_get_keyboard (seat); - fevent = gdk_event_new (GDK_FOCUS_CHANGE); - gdk_event_set_display (fevent, gtk_widget_get_display (widget)); + event = gdk_event_new (GDK_FOCUS_CHANGE); + gdk_event_set_display (event, gtk_widget_get_display (widget)); + gdk_event_set_device (event, device); - fevent->any.type = GDK_FOCUS_CHANGE; - fevent->any.surface = surface; - if (surface) - g_object_ref (surface); - fevent->focus_change.in = in; - gdk_event_set_device (fevent, dev); + event->any.type = GDK_FOCUS_CHANGE; + event->any.surface = _gtk_widget_get_surface (widget); + if (event->any.surface) + g_object_ref (event->any.surface); + event->focus_change.in = in; + event->focus_change.mode = GDK_CROSSING_STATE_CHANGED; + event->focus_change.detail = GDK_NOTIFY_ANCESTOR; - gtk_widget_set_has_focus (widget, in); - gtk_widget_event (widget, fevent); + gtk_widget_set_has_focus (widget, in); + gtk_widget_event (widget, event); - g_object_unref (fevent); - } - - g_list_free (devices); - g_object_unref (widget); + g_object_unref (event); } static gboolean @@ -7063,44 +7051,6 @@ gtk_window_forall (GtkContainer *container, (* callback) (priv->title_box, callback_data); } -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); -} - /** * gtk_window_set_focus: * @window: a #GtkWindow @@ -7117,13 +7067,46 @@ void gtk_window_set_focus (GtkWindow *window, GtkWidget *focus) { + GtkWindowPrivate *priv = gtk_window_get_instance_private (window); + GtkWidget *old_focus = NULL; + GtkWidget *f; + GdkSeat *seat; + GdkDevice *device; + GdkEvent *event; + g_return_if_fail (GTK_IS_WINDOW (window)); if (focus && !gtk_widget_can_take_focus (focus)) return; - unset_focus_widget (window); - set_focus_widget (window, focus); + if (priv->focus_widget) + old_focus = g_object_ref (priv->focus_widget); + g_set_object (&priv->focus_widget, NULL); + + seat = gdk_display_get_default_seat (gtk_widget_get_display (GTK_WIDGET (window))); + device = gdk_seat_get_keyboard (seat); + + event = gdk_event_new (GDK_FOCUS_CHANGE); + gdk_event_set_display (event, gtk_widget_get_display (GTK_WIDGET (window))); + gdk_event_set_device (event, device); + event->any.surface = _gtk_widget_get_surface (GTK_WIDGET (window)); + if (event->any.surface) + g_object_ref (event->any.surface); + + gtk_synthesize_crossing_events (window, old_focus, focus, event, GDK_CROSSING_NORMAL); + + g_object_unref (event); + + g_set_object (&priv->focus_widget, focus); + + g_clear_object (&old_focus); + + for (f = focus; f; f = gtk_widget_get_parent (f)) + { + GtkWidget *parent = gtk_widget_get_parent (f); + if (parent) + gtk_widget_set_focus_child (parent, f); + } g_object_notify (G_OBJECT (window), "focus-widget"); } -- GitLab From 8dc276b20b199c7cb3deb1ed81970d1091c221a0 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 7 Mar 2019 00:14:26 -0500 Subject: [PATCH 50/70] Pass mode and detail to focus-in/out signals This information is useful when maintaining a 'last focus' field. Update all users. --- gtk/gtkcalendar.c | 4 ++++ gtk/gtkeventcontrollerkey.c | 32 +++++++++++++++++++++++--------- gtk/gtklabel.c | 2 ++ gtk/gtkspinbutton.c | 2 ++ gtk/gtktreeview.c | 4 ++++ gtk/gtktreeviewcolumn.c | 2 ++ 6 files changed, 37 insertions(+), 9 deletions(-) diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c index 6b941ced3e2..32df686852f 100644 --- a/gtk/gtkcalendar.c +++ b/gtk/gtkcalendar.c @@ -295,6 +295,8 @@ static gboolean gtk_calendar_key_controller_key_pressed (GtkEventControllerKey * GdkModifierType state, GtkWidget *widget); static void gtk_calendar_key_controller_focus (GtkEventControllerKey *controller, + GdkCrossingMode mode, + GdkNotifyType detail, GtkWidget *widget); static void gtk_calendar_grab_notify (GtkWidget *widget, gboolean was_grabbed); @@ -2854,6 +2856,8 @@ gtk_calendar_key_controller_key_pressed (GtkEventControllerKey *controller, static void gtk_calendar_key_controller_focus (GtkEventControllerKey *key, + GdkCrossingMode mode, + GdkNotifyType detail, GtkWidget *widget) { GtkCalendar *calendar = GTK_CALENDAR (widget); diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index f1b28d999b3..2c5a69f7a70 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -93,11 +93,16 @@ gtk_event_controller_key_handle_event (GtkEventController *controller, if (event_type == GDK_FOCUS_CHANGE) { gboolean focus_in; + GdkCrossingMode mode; + GdkNotifyType detail; + + gdk_event_get_crossing_mode (event, &mode); + gdk_event_get_crossing_detail (event, &detail); if (gdk_event_get_focus_in (event, &focus_in) && focus_in) - g_signal_emit (controller, signals[FOCUS_IN], 0); + g_signal_emit (controller, signals[FOCUS_IN], 0, mode, detail); else - g_signal_emit (controller, signals[FOCUS_OUT], 0); + g_signal_emit (controller, signals[FOCUS_OUT], 0, mode, detail); return FALSE; } @@ -233,12 +238,14 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) GTK_TYPE_EVENT_CONTROLLER_KEY, G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, + NULL, G_TYPE_NONE, 0); /** * GtkEventControllerKey::focus-in: * @controller: the object which received the signal. + * @mode: crossing mode indicating what caused this change + * @detail: detail indication where the focus is coming from * * This signal is emitted whenever the #GtkEventController:widget controlled * by the @controller is given the keyboard focus. @@ -248,12 +255,17 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) GTK_TYPE_EVENT_CONTROLLER_KEY, G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + NULL, + G_TYPE_NONE, + 2, + GDK_TYPE_CROSSING_MODE, + GDK_TYPE_NOTIFY_TYPE); /** * GtkEventControllerKey::focus-out: * @controller: the object which received the signal. + * @mode: crossing mode indicating what caused this change + * @detail: detail indication where the focus is going * * This signal is emitted whenever the #GtkEventController:widget controlled * by the @controller loses the keyboard focus. @@ -263,8 +275,11 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) GTK_TYPE_EVENT_CONTROLLER_KEY, G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + NULL, + G_TYPE_NONE, + 2, + GDK_TYPE_CROSSING_MODE, + GDK_TYPE_NOTIFY_TYPE); } static void @@ -283,8 +298,7 @@ gtk_event_controller_key_init (GtkEventControllerKey *controller) GtkEventController * gtk_event_controller_key_new (void) { - return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY, - NULL); + return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY, NULL); } /** diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index 9ca02781ad5..95a2ea42b56 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -1126,6 +1126,8 @@ gtk_label_class_init (GtkLabelClass *class) static void focus_in_cb (GtkEventControllerKey *controller, + GdkCrossingMode mode, + GdkNotifyType detail, GtkWidget *widget) { GtkLabel *label = GTK_LABEL (widget); diff --git a/gtk/gtkspinbutton.c b/gtk/gtkspinbutton.c index 8e16faa8370..b273b2e30dd 100644 --- a/gtk/gtkspinbutton.c +++ b/gtk/gtkspinbutton.c @@ -824,6 +824,8 @@ key_controller_key_released (GtkEventControllerKey *key, static void key_controller_focus_out (GtkEventControllerKey *key, + GdkCrossingMode mode, + GdkNotifyType detail, GtkSpinButton *spin_button) { GtkSpinButtonPrivate *priv = gtk_spin_button_get_instance_private (spin_button); diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index d9e2012dde3..8262be20cb6 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -614,6 +614,8 @@ static void gtk_tree_view_key_controller_key_released (GtkEventControllerKey GdkModifierType state, GtkTreeView *tree_view); static void gtk_tree_view_key_controller_focus_out (GtkEventControllerKey *key, + GdkCrossingMode mode, + GdkNotifyType detail, GtkTreeView *tree_view); static void gtk_tree_view_grab_focus (GtkWidget *widget); @@ -5472,6 +5474,8 @@ gtk_tree_view_motion_controller_leave (GtkEventControllerMotion *controller, static void gtk_tree_view_key_controller_focus_out (GtkEventControllerKey *key, + GdkCrossingMode mode, + GdkNotifyType detail, GtkTreeView *tree_view) { gtk_widget_queue_draw (GTK_WIDGET (tree_view)); diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c index 8c113ad3c4c..78e22c5ef2f 100644 --- a/gtk/gtktreeviewcolumn.c +++ b/gtk/gtktreeviewcolumn.c @@ -814,6 +814,8 @@ gtk_tree_view_column_cell_layout_get_area (GtkCellLayout *cell_layout) static void focus_in (GtkEventControllerKey *controller, + GdkCrossingMode mode, + GdkNotifyType detail, GtkTreeViewColumn *column) { _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (column->priv->tree_view), column); -- GitLab From 7b084d05fa1233af9e6a0edc7d51de4d6b1c32f4 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 11:13:40 -0500 Subject: [PATCH 51/70] Use gdk_event_set_related_target --- gtk/gtkmain.c | 18 +++++++++++++----- gtk/gtkmain.h | 3 +++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 36a53a83f00..13151b398a5 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1407,7 +1407,8 @@ static void synth_crossing (GtkWidget *widget, GtkWidget *toplevel, gboolean enter, - GtkWidget *other_widget, + GtkWidget *target, + GtkWidget *related_target, GdkEvent *source, GdkNotifyType notify_type, GdkCrossingMode crossing_mode) @@ -1430,8 +1431,8 @@ synth_crossing (GtkWidget *widget, { gdouble x, y; event = gdk_event_new (enter ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY); - if (other_widget) - event->crossing.child_surface = g_object_ref (gtk_widget_get_surface (other_widget)); + if (related_target) + event->crossing.child_surface = g_object_ref (gtk_widget_get_surface (related_target)); gdk_event_get_coords (source, &x, &y); event->crossing.x = x; event->crossing.y = y; @@ -1442,6 +1443,7 @@ synth_crossing (GtkWidget *widget, } gdk_event_set_target (event, G_OBJECT (target)); + gdk_event_set_related_target (event, G_OBJECT (related_target)); gdk_event_set_device (event, gdk_event_get_device (source)); gdk_event_set_source_device (event, gdk_event_get_source_device (source)); @@ -1500,7 +1502,7 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel, leave_type : get_virtual_notify_type (leave_type); synth_crossing (widget, GTK_WIDGET (toplevel), FALSE, - new_target, event, notify_type, mode); + old_target, new_target, event, notify_type, mode); widget = gtk_widget_get_parent (widget); } } @@ -1525,7 +1527,7 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel, enter_type : get_virtual_notify_type (enter_type); synth_crossing (widget, GTK_WIDGET (toplevel), TRUE, - old_target, event, notify_type, mode); + new_target, old_target, event, notify_type, mode); } } } @@ -2427,6 +2429,12 @@ gtk_get_event_target (const GdkEvent *event) return GTK_WIDGET (gdk_event_get_target (event)); } +GtkWidget * +gtk_get_event_related_target (const GdkEvent *event) +{ + return GTK_WIDGET (gdk_event_get_related_target (event)); +} + /** * gtk_get_event_target_with_type: * @event: a #GdkEvent diff --git a/gtk/gtkmain.h b/gtk/gtkmain.h index 81c1e574ada..b1199e6f932 100644 --- a/gtk/gtkmain.h +++ b/gtk/gtkmain.h @@ -155,6 +155,9 @@ GtkWidget *gtk_get_event_widget (const GdkEvent *event); GDK_AVAILABLE_IN_ALL GtkWidget *gtk_get_event_target (const GdkEvent *event); +GDK_AVAILABLE_IN_ALL +GtkWidget *gtk_get_event_related_target (const GdkEvent *event); + GDK_AVAILABLE_IN_ALL GtkWidget *gtk_get_event_target_with_type (GdkEvent *event, GType type); -- GitLab From 7cc045666a5aa7b566a651c07a423391664ca89a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 09:08:05 -0500 Subject: [PATCH 52/70] Some fixes to crossing event generation In the inferior and ancestor cases, we were missing the initial/final event. --- gtk/gtkmain.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 13151b398a5..909827decdd 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1447,7 +1447,9 @@ synth_crossing (GtkWidget *widget, gdk_event_set_device (event, gdk_event_get_device (source)); gdk_event_set_source_device (event, gdk_event_get_source_device (source)); - event->any.surface = g_object_ref (gtk_widget_get_surface (toplevel)); + event->any.surface = gtk_widget_get_surface (toplevel); + if (event->any.surface) + g_object_ref (event->any.surface); if (enter) gtk_widget_set_state_flags (widget, flags, FALSE); @@ -1496,13 +1498,16 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel, { widget = old_target; - while (widget != ancestor) + while (widget) { notify_type = (widget == old_target) ? leave_type : get_virtual_notify_type (leave_type); - synth_crossing (widget, GTK_WIDGET (toplevel), FALSE, - old_target, new_target, event, notify_type, mode); + if (widget != ancestor || widget == old_target) + synth_crossing (widget, GTK_WIDGET (toplevel), FALSE, + old_target, new_target, event, notify_type, mode); + if (widget == ancestor) + break; widget = gtk_widget_get_parent (widget); } } @@ -1513,9 +1518,11 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel, widget = new_target; - while (widget != ancestor) + while (widget) { widgets = g_slist_prepend (widgets, widget); + if (widget == ancestor) + break; widget = gtk_widget_get_parent (widget); } @@ -1526,8 +1533,9 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel, notify_type = (widget == new_target) ? enter_type : get_virtual_notify_type (enter_type); - synth_crossing (widget, GTK_WIDGET (toplevel), TRUE, - new_target, old_target, event, notify_type, mode); + if (widget != ancestor || widget == new_target) + synth_crossing (widget, GTK_WIDGET (toplevel), TRUE, + new_target, old_target, event, notify_type, mode); } } } -- GitLab From b408a2674a9319b8fee0896385c7e8b44d7d4675 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 7 Mar 2019 23:25:50 -0500 Subject: [PATCH 53/70] Update the focus test Check that we get the expected sequences of focus change events for the nonlinear, inferior and ancestor cases. It would be nice to do the same checks for crossing events, but we have no gtk_window_set_hover(). --- testsuite/gtk/focus.c | 163 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 154 insertions(+), 9 deletions(-) diff --git a/testsuite/gtk/focus.c b/testsuite/gtk/focus.c index 0605c8eb538..a6ffeccbade 100644 --- a/testsuite/gtk/focus.c +++ b/testsuite/gtk/focus.c @@ -1,41 +1,186 @@ #include +const char * +widget_name (GtkWidget *widget) +{ + if (gtk_widget_get_name (widget)) + return gtk_widget_get_name (widget); + else if (GTK_IS_LABEL (widget)) + return gtk_label_get_label (GTK_LABEL (widget)); + else if (GTK_IS_EDITABLE (widget)) + return gtk_editable_get_text (GTK_EDITABLE (widget)); + else + return G_OBJECT_TYPE_NAME (widget); +} + +static char * +mode_to_string (GdkCrossingMode mode) +{ + return g_enum_to_string (GDK_TYPE_CROSSING_MODE, mode); +} + +static char * +detail_to_string (GdkNotifyType detail) +{ + return g_enum_to_string (GDK_TYPE_NOTIFY_TYPE, detail); +} + +static void +focus_in (GtkEventController *controller, + GdkCrossingMode mode, + GdkNotifyType detail, + GString *s) +{ + GtkWidget *widget = gtk_event_controller_get_widget (controller); + g_string_append_printf (s, "%s: focus-in %s %s\n", + widget_name (widget), + mode_to_string (mode), + detail_to_string (detail)); +} + +static void +focus_out (GtkEventController *controller, + GdkCrossingMode mode, + GdkNotifyType detail, + GString *s) +{ + GtkWidget *widget = gtk_event_controller_get_widget (controller); + g_string_append_printf (s, "%s: focus-out %s %s\n", + widget_name (widget), + mode_to_string (mode), + detail_to_string (detail)); +} + +static void +add_controller (GtkWidget *widget, GString *s) +{ + GtkEventController *controller; + + controller = gtk_event_controller_key_new (); + g_signal_connect (controller, "focus-in", G_CALLBACK (focus_in), s); + g_signal_connect (controller, "focus-out", G_CALLBACK (focus_out), s); + gtk_widget_add_controller (widget, controller); +} + static void test_window_focus (void) { GtkWidget *window; GtkWidget *box; + GtkWidget *box1; + GtkWidget *box2; + GtkWidget *label1; + GtkWidget *label2; GtkWidget *entry1; GtkWidget *entry2; + GString *s = g_string_new (""); + + /* + * The tree look like this, with [] indicating + * focus locations: + * + * window + * | + * +----[box]-----+ + * | | | + * label1 box1 box2------+ + * | | | + * [entry1] label2 [entry2] + */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_name (window, "window"); + add_controller (window, s); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_can_focus (box, TRUE); + gtk_widget_set_name (box, "box"); + add_controller (box, s); gtk_container_add (GTK_CONTAINER (window), box); - gtk_container_add (GTK_CONTAINER (box), gtk_label_new ("label1")); + label1 = gtk_label_new ("label1"); + gtk_widget_set_name (label1, "label1"); + add_controller (label1, s); + gtk_container_add (GTK_CONTAINER (box), label1); + box1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_name (box1, "box1"); + add_controller (box1, s); + gtk_container_add (GTK_CONTAINER (box), box1); entry1 = gtk_text_new (); - gtk_container_add (GTK_CONTAINER (box), entry1); - gtk_container_add (GTK_CONTAINER (box), gtk_label_new ("label2")); + gtk_widget_set_name (entry1, "entry1"); + add_controller (entry1, s); + gtk_container_add (GTK_CONTAINER (box1), entry1); + box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_name (box2, "box2"); + add_controller (box2, s); + gtk_container_add (GTK_CONTAINER (box), box2); + label2 = gtk_label_new ("label2"); + gtk_widget_set_name (label2, "label2"); + add_controller (label2, s); + gtk_container_add (GTK_CONTAINER (box2), label2); entry2 = gtk_text_new (); - gtk_container_add (GTK_CONTAINER (box), entry2); + gtk_widget_set_name (entry2, "entry2"); + add_controller (entry2, s); + gtk_container_add (GTK_CONTAINER (box2), entry2); g_assert_null (gtk_window_get_focus (GTK_WINDOW (window))); - gtk_window_set_focus (GTK_WINDOW (window), entry1); + gtk_widget_show (window); - g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry1); + /* show puts the initial focus on box */ + g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == box); - gtk_widget_show (window); + if (g_test_verbose ()) + g_print ("-> box\n%s\n", s->str); + + g_assert_cmpstr (s->str, ==, +"window: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL\n" +"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR\n"); + g_string_truncate (s, 0); + + gtk_widget_grab_focus (entry1); + + if (g_test_verbose ()) + g_print ("box -> entry1\n%s\n", s->str); + + g_assert_cmpstr (s->str, ==, +"box: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR\n" +"box1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL\n" +"entry1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR\n"); + + g_string_truncate (s, 0); g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry1); gtk_widget_grab_focus (entry2); - g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry2); + if (g_test_verbose ()) + g_print ("entry1 -> entry2\n%s\n", s->str); - gtk_widget_hide (window); + g_assert_cmpstr (s->str, ==, +"entry1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR\n" +"box1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL\n" +"box2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL\n" +"entry2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR\n"); + + g_string_truncate (s, 0); g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry2); + gtk_widget_grab_focus (box); + + if (g_test_verbose ()) + g_print ("entry2 -> box\n%s", s->str); + + g_assert_cmpstr (s->str, ==, +"entry2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR\n" +"box2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL\n" +"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR\n"); + + g_string_truncate (s, 0); + + gtk_widget_hide (window); + + g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == box); + gtk_window_set_focus (GTK_WINDOW (window), entry1); g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry1); -- GitLab From 53ae3f777e8774cfa12beca94254071afd30773f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 09:20:06 -0500 Subject: [PATCH 54/70] key controller: Fix a copy/paste error Prefix the finalize function properly. --- gtk/gtkeventcontrollerkey.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index 2c5a69f7a70..9f32cc90230 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -69,7 +69,7 @@ G_DEFINE_TYPE (GtkEventControllerKey, gtk_event_controller_key, GTK_TYPE_EVENT_CONTROLLER) static void -gtk_event_controller_finalize (GObject *object) +gtk_event_controller_key_finalize (GObject *object) { GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (object); @@ -169,7 +169,7 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = gtk_event_controller_finalize; + object_class->finalize = gtk_event_controller_key_finalize; controller_class->handle_event = gtk_event_controller_key_handle_event; /** -- GitLab From 1960f44fe38a074f6874fcd95815b12942aa1ac5 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 09:55:27 -0500 Subject: [PATCH 55/70] key controller: Add focus properties Add boolean properties, is-focus and contains-focus, that track whether the focus is in the widget itself or in one of its descendants. --- gtk/gtkeventcontrollerkey.c | 131 ++++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 5 deletions(-) diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index 9f32cc90230..390ec4e2f47 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -46,6 +46,9 @@ struct _GtkEventControllerKey GHashTable *pressed_keys; const GdkEvent *current_event; + + guint is_focus : 1; + guint contains_focus : 1; }; struct _GtkEventControllerKeyClass @@ -65,6 +68,14 @@ enum { static guint signals[N_SIGNALS] = { 0 }; +enum { + PROP_IS_FOCUS = 1, + PROP_CONTAINS_FOCUS, + NUM_PROPERTIES +}; + +static GParamSpec *props[NUM_PROPERTIES] = { NULL, }; + G_DEFINE_TYPE (GtkEventControllerKey, gtk_event_controller_key, GTK_TYPE_EVENT_CONTROLLER) @@ -79,6 +90,50 @@ gtk_event_controller_key_finalize (GObject *object) G_OBJECT_CLASS (gtk_event_controller_key_parent_class)->finalize (object); } +static void +update_focus (GtkEventControllerKey *key, + gboolean focus_in, + GdkNotifyType detail) +{ + gboolean is_focus; + gboolean contains_focus; + + switch (detail) + { + case GDK_NOTIFY_VIRTUAL: + case GDK_NOTIFY_NONLINEAR_VIRTUAL: + is_focus = FALSE; + contains_focus = focus_in; + break; + case GDK_NOTIFY_ANCESTOR: + case GDK_NOTIFY_NONLINEAR: + is_focus = focus_in; + contains_focus = FALSE; + break; + case GDK_NOTIFY_INFERIOR: + is_focus = focus_in; + contains_focus = !focus_in; + break; + case GDK_NOTIFY_UNKNOWN: + default: + g_warning ("Unknown focus change detail"); + return; + } + + g_object_freeze_notify (G_OBJECT (key)); + if (key->is_focus != is_focus) + { + key->is_focus = is_focus; + g_object_notify (G_OBJECT (key), "is-focus"); + } + if (key->contains_focus != contains_focus) + { + key->contains_focus = contains_focus; + g_object_notify (G_OBJECT (key), "contains-focus"); + } + g_object_thaw_notify (G_OBJECT (key)); +} + static gboolean gtk_event_controller_key_handle_event (GtkEventController *controller, const GdkEvent *event) @@ -96,10 +151,13 @@ gtk_event_controller_key_handle_event (GtkEventController *controller, GdkCrossingMode mode; GdkNotifyType detail; + gdk_event_get_focus_in (event, &focus_in); gdk_event_get_crossing_mode (event, &mode); gdk_event_get_crossing_detail (event, &detail); - if (gdk_event_get_focus_in (event, &focus_in) && focus_in) + update_focus (key, focus_in, detail); + + if (focus_in) g_signal_emit (controller, signals[FOCUS_IN], 0, mode, detail); else g_signal_emit (controller, signals[FOCUS_OUT], 0, mode, detail); @@ -163,6 +221,29 @@ gtk_event_controller_key_handle_event (GtkEventController *controller, return handled; } +static void +gtk_event_controller_key_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkEventControllerKey *controller = GTK_EVENT_CONTROLLER_KEY (object); + + switch (prop_id) + { + case PROP_IS_FOCUS: + g_value_set_boolean (value, controller->is_focus); + break; + + case PROP_CONTAINS_FOCUS: + g_value_set_boolean (value, controller->contains_focus); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + static void gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) { @@ -170,8 +251,46 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gtk_event_controller_key_finalize; + object_class->get_property = gtk_event_controller_key_get_property; controller_class->handle_event = gtk_event_controller_key_handle_event; + /** + * GtkEventControllerKey:is-focus: + * + * Whether focus is in the controllers widget itself, + * as opposed to in a descendent widget. See + * #GtkEventControllerKey:contains-focus. + * + * When handling focus events, this property is updated + * before #GtkEventControllerKey::focus-in or + * #GtkEventControllerKey::focus-out are emitted. + */ + props[PROP_IS_FOCUS] = + g_param_spec_boolean ("is-focus", + P_("Is Focus"), + P_("Whether the focus is in the controllers widget"), + FALSE, + G_PARAM_READABLE); + + /** + * GtkEventControllerKey:contains-focus: + * + * Whether focus is in a descendant of the controllers widget. + * See #GtkEventControllerKey:is-focus. + * + * When handling focus events, this property is updated + * before #GtkEventControllerKey::focus-in or + * #GtkEventControllerKey::focus-out are emitted. + */ + props[PROP_CONTAINS_FOCUS] = + g_param_spec_boolean ("contains-focus", + P_("Contains Focus"), + P_("Whether the focus is in a descendant of the controllers widget"), + FALSE, + G_PARAM_READABLE); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, props); + /** * GtkEventControllerKey::key-pressed: * @controller: the object which received the signal. @@ -247,8 +366,9 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) * @mode: crossing mode indicating what caused this change * @detail: detail indication where the focus is coming from * - * This signal is emitted whenever the #GtkEventController:widget controlled - * by the @controller is given the keyboard focus. + * This signal is emitted whenever the widget controlled + * by the @controller or one of its descendants) is given + * the keyboard focus. */ signals[FOCUS_IN] = g_signal_new (I_("focus-in"), @@ -267,8 +387,9 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) * @mode: crossing mode indicating what caused this change * @detail: detail indication where the focus is going * - * This signal is emitted whenever the #GtkEventController:widget controlled - * by the @controller loses the keyboard focus. + * This signal is emitted whenever the widget controlled + * by the @controller (or one of its descendants) loses + * the keyboard focus. */ signals[FOCUS_OUT] = g_signal_new (I_("focus-out"), -- GitLab From 8e6985bdc0610e055db2cb9017f4741951a6e76d Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 09:56:17 -0500 Subject: [PATCH 56/70] focus test: check the new properties Test that is-focus and contains-focus have the expected values, as focus moves around. --- testsuite/gtk/focus.c | 58 +++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/testsuite/gtk/focus.c b/testsuite/gtk/focus.c index a6ffeccbade..d89004df025 100644 --- a/testsuite/gtk/focus.c +++ b/testsuite/gtk/focus.c @@ -25,17 +25,35 @@ detail_to_string (GdkNotifyType detail) return g_enum_to_string (GDK_TYPE_NOTIFY_TYPE, detail); } +static void +add_event (GtkEventController *controller, + gboolean in, + GdkCrossingMode mode, + GdkNotifyType detail, + GString *s) +{ + gboolean is_focus; + gboolean contains_focus; + GtkWidget *widget = gtk_event_controller_get_widget (controller); + g_object_get (controller, + "is-focus", &is_focus, + "contains-focus", &contains_focus, + NULL); + g_string_append_printf (s, "%s: %s %s %s is-focus: %d contains-focus: %d\n", + widget_name (widget), + in ? "focus-in" : "focus-out", + mode_to_string (mode), + detail_to_string (detail), + is_focus, contains_focus); +} + static void focus_in (GtkEventController *controller, GdkCrossingMode mode, GdkNotifyType detail, GString *s) { - GtkWidget *widget = gtk_event_controller_get_widget (controller); - g_string_append_printf (s, "%s: focus-in %s %s\n", - widget_name (widget), - mode_to_string (mode), - detail_to_string (detail)); + add_event (controller, TRUE, mode, detail, s); } static void @@ -44,11 +62,7 @@ focus_out (GtkEventController *controller, GdkNotifyType detail, GString *s) { - GtkWidget *widget = gtk_event_controller_get_widget (controller); - g_string_append_printf (s, "%s: focus-out %s %s\n", - widget_name (widget), - mode_to_string (mode), - detail_to_string (detail)); + add_event (controller, FALSE, mode, detail, s); } static void @@ -132,8 +146,8 @@ test_window_focus (void) g_print ("-> box\n%s\n", s->str); g_assert_cmpstr (s->str, ==, -"window: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL\n" -"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR\n"); +"window: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 1\n" +"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 1 contains-focus: 0\n"); g_string_truncate (s, 0); gtk_widget_grab_focus (entry1); @@ -142,9 +156,9 @@ test_window_focus (void) g_print ("box -> entry1\n%s\n", s->str); g_assert_cmpstr (s->str, ==, -"box: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR\n" -"box1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL\n" -"entry1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR\n"); +"box: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR is-focus: 0 contains-focus: 1\n" +"box1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 1\n" +"entry1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 1 contains-focus: 0\n"); g_string_truncate (s, 0); @@ -156,10 +170,10 @@ test_window_focus (void) g_print ("entry1 -> entry2\n%s\n", s->str); g_assert_cmpstr (s->str, ==, -"entry1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR\n" -"box1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL\n" -"box2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL\n" -"entry2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR\n"); +"entry1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR is-focus: 0 contains-focus: 0\n" +"box1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL is-focus: 0 contains-focus: 0\n" +"box2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL is-focus: 0 contains-focus: 1\n" +"entry2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR is-focus: 1 contains-focus: 0\n"); g_string_truncate (s, 0); @@ -171,9 +185,9 @@ test_window_focus (void) g_print ("entry2 -> box\n%s", s->str); g_assert_cmpstr (s->str, ==, -"entry2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR\n" -"box2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL\n" -"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR\n"); +"entry2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 0 contains-focus: 0\n" +"box2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 0\n" +"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR is-focus: 1 contains-focus: 0\n"); g_string_truncate (s, 0); -- GitLab From c55d5384473c8f5082f0835abdbfd841727cf2d4 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 10:16:27 -0500 Subject: [PATCH 57/70] key controller: Add getters for focus event targets This information can be needed in signal handlers, so make it available. --- gtk/gtkeventcontrollerkey.c | 66 +++++++++++++++++++++++++++++++++++++ gtk/gtkeventcontrollerkey.h | 5 +++ 2 files changed, 71 insertions(+) diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index 390ec4e2f47..b57e9df19e2 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -36,6 +36,7 @@ #include "gtkeventcontrollerkey.h" #include "gtkbindings.h" #include "gtkenums.h" +#include "gtkmain.h" #include @@ -157,11 +158,15 @@ gtk_event_controller_key_handle_event (GtkEventController *controller, update_focus (key, focus_in, detail); + key->current_event = event; + if (focus_in) g_signal_emit (controller, signals[FOCUS_IN], 0, mode, detail); else g_signal_emit (controller, signals[FOCUS_OUT], 0, mode, detail); + key->current_event = NULL; + return FALSE; } @@ -512,3 +517,64 @@ gtk_event_controller_key_get_group (GtkEventControllerKey *controller) return group; } + +/** + * gtk_event_controller_key_get_focus_origin: + * @controller: a #GtkEventControllerKey + * + * Returns the widget that was holding focus before. + * + * This function can only be used in handlers for the + * #GtkEventControllerKey::focus-in and + * #GtkEventControllerKey::focus-out signals. + * + * Returns: (transfer none): the previous focus + */ +GtkWidget * +gtk_event_controller_key_get_focus_origin (GtkEventControllerKey *controller) +{ + gboolean focus_in; + GtkWidget *origin; + + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL); + g_return_val_if_fail (controller->current_event != NULL, NULL); + g_return_val_if_fail (gdk_event_get_event_type (controller->current_event) == GDK_FOCUS_CHANGE, NULL); + + gdk_event_get_focus_in (controller->current_event, &focus_in); + + if (focus_in) + origin = (GtkWidget *)gdk_event_get_related_target (controller->current_event); + else + origin = (GtkWidget *)gdk_event_get_target (controller->current_event); + + return origin; +} + +/** + * gtk_event_controller_key_get_focus_target: + * @controller: a #GtkEventControllerKey + * + * Returns the widget that will be holding focus afterwards. + * + * This function can only be used in handlers for the + * #GtkEventControllerKey::focus-in and + * #GtkEventControllerKey::focus-out signals. + * + * Returns: (transfer none): the next focus + */ +GtkWidget * +gtk_event_controller_key_get_focus_target (GtkEventControllerKey *controller) +{ + gboolean focus_in; + + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL); + g_return_val_if_fail (controller->current_event != NULL, NULL); + g_return_val_if_fail (gdk_event_get_event_type (controller->current_event) == GDK_FOCUS_CHANGE, NULL); + + gdk_event_get_focus_in (controller->current_event, &focus_in); + + if (focus_in) + return (GtkWidget *)gdk_event_get_target (controller->current_event); + else + return (GtkWidget *)gdk_event_get_related_target (controller->current_event); +} diff --git a/gtk/gtkeventcontrollerkey.h b/gtk/gtkeventcontrollerkey.h index c8a579773a4..b036bde7ec4 100644 --- a/gtk/gtkeventcontrollerkey.h +++ b/gtk/gtkeventcontrollerkey.h @@ -58,6 +58,11 @@ gboolean gtk_event_controller_key_forward (GtkEventControllerK GDK_AVAILABLE_IN_ALL guint gtk_event_controller_key_get_group (GtkEventControllerKey *controller); +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_event_controller_key_get_focus_origin (GtkEventControllerKey *controller); +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_event_controller_key_get_focus_target (GtkEventControllerKey *controller); + G_END_DECLS #endif /* __GTK_EVENT_CONTROLLER_KEY_H__ */ -- GitLab From 23d98a956d795c2de70f36c9e0eb3fb69f5f1ebd Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 11:36:07 -0500 Subject: [PATCH 58/70] focus test: test origin and targets Check that these functions return the expected widgets during crossing event generation. --- testsuite/gtk/focus.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/testsuite/gtk/focus.c b/testsuite/gtk/focus.c index d89004df025..f8e4557ac46 100644 --- a/testsuite/gtk/focus.c +++ b/testsuite/gtk/focus.c @@ -3,7 +3,9 @@ const char * widget_name (GtkWidget *widget) { - if (gtk_widget_get_name (widget)) + if (!widget) + return NULL; + else if (gtk_widget_get_name (widget)) return gtk_widget_get_name (widget); else if (GTK_IS_LABEL (widget)) return gtk_label_get_label (GTK_LABEL (widget)); @@ -32,19 +34,27 @@ add_event (GtkEventController *controller, GdkNotifyType detail, GString *s) { + GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller); gboolean is_focus; gboolean contains_focus; GtkWidget *widget = gtk_event_controller_get_widget (controller); + GtkWidget *origin = gtk_event_controller_key_get_focus_origin (key); + GtkWidget *target = gtk_event_controller_key_get_focus_target (key); + g_object_get (controller, "is-focus", &is_focus, "contains-focus", &contains_focus, NULL); - g_string_append_printf (s, "%s: %s %s %s is-focus: %d contains-focus: %d\n", + + g_string_append_printf (s, "%s: %s %s %s is-focus: %d contains-focus: %d origin: %s target: %s\n", widget_name (widget), in ? "focus-in" : "focus-out", mode_to_string (mode), detail_to_string (detail), - is_focus, contains_focus); + is_focus, + contains_focus, + widget_name (origin), + widget_name (target)); } static void @@ -146,8 +156,8 @@ test_window_focus (void) g_print ("-> box\n%s\n", s->str); g_assert_cmpstr (s->str, ==, -"window: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 1\n" -"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 1 contains-focus: 0\n"); +"window: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 1 origin: (null) target: box\n" +"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 1 contains-focus: 0 origin: (null) target: box\n"); g_string_truncate (s, 0); gtk_widget_grab_focus (entry1); @@ -156,9 +166,9 @@ test_window_focus (void) g_print ("box -> entry1\n%s\n", s->str); g_assert_cmpstr (s->str, ==, -"box: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR is-focus: 0 contains-focus: 1\n" -"box1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 1\n" -"entry1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 1 contains-focus: 0\n"); +"box: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR is-focus: 0 contains-focus: 1 origin: box target: entry1\n" +"box1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 1 origin: box target: entry1\n" +"entry1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 1 contains-focus: 0 origin: box target: entry1\n"); g_string_truncate (s, 0); @@ -170,10 +180,10 @@ test_window_focus (void) g_print ("entry1 -> entry2\n%s\n", s->str); g_assert_cmpstr (s->str, ==, -"entry1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR is-focus: 0 contains-focus: 0\n" -"box1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL is-focus: 0 contains-focus: 0\n" -"box2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL is-focus: 0 contains-focus: 1\n" -"entry2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR is-focus: 1 contains-focus: 0\n"); +"entry1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR is-focus: 0 contains-focus: 0 origin: entry1 target: entry2\n" +"box1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL is-focus: 0 contains-focus: 0 origin: entry1 target: entry2\n" +"box2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL is-focus: 0 contains-focus: 1 origin: entry1 target: entry2\n" +"entry2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR is-focus: 1 contains-focus: 0 origin: entry1 target: entry2\n"); g_string_truncate (s, 0); @@ -185,9 +195,9 @@ test_window_focus (void) g_print ("entry2 -> box\n%s", s->str); g_assert_cmpstr (s->str, ==, -"entry2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 0 contains-focus: 0\n" -"box2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 0\n" -"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR is-focus: 1 contains-focus: 0\n"); +"entry2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 0 contains-focus: 0 origin: entry2 target: box\n" +"box2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 0 origin: entry2 target: box\n" +"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR is-focus: 1 contains-focus: 0 origin: entry2 target: box\n"); g_string_truncate (s, 0); -- GitLab From c389e1fea6c0a564ede2666bc86860506d68462a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 10:19:12 -0500 Subject: [PATCH 59/70] key controller: Improve the docs Mention the limitations of the key forwarding api. --- gtk/gtkeventcontrollerkey.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index b57e9df19e2..29090959794 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -470,6 +470,13 @@ gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller) * * Forwards the current event of this @controller to a @widget. * + * This function can only be used in handlers for the + * #GtkEventControllerKey::key-pressed, + * #GtkEventControllerKey::key-released + * or + * #GtkEventControllerKey::modifiers + * signals. + * * Returns: whether the @widget handled the event **/ gboolean -- GitLab From a93f9394684ef8cc43a1b2a992ad97d1ea2d07c2 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 10:21:02 -0500 Subject: [PATCH 60/70] key controller: Enforce limitations of key forwarding We now set current_event for focus-change as well, so make sure to check the event type. --- gtk/gtkeventcontrollerkey.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index 29090959794..f7f3fe1017e 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -486,6 +486,8 @@ gtk_event_controller_key_forward (GtkEventControllerKey *controller, g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE); g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); g_return_val_if_fail (controller->current_event != NULL, FALSE); + g_return_val_if_fail (gdk_event_get_event_type (controller->current_event) == GDK_KEY_PRESS || + gdk_event_get_event_type (controller->current_event) == GDK_KEY_RELEASE, FALSE); if (!gtk_widget_get_realized (widget)) gtk_widget_realize (widget); -- GitLab From 0021b023806e1b048d8ada246f5f3d10c939e09d Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 11:44:43 -0500 Subject: [PATCH 61/70] Add key controller API to the docs A bunch of API was missing here. --- docs/reference/gtk/gtk4-sections.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 699cb2d56b9..8bd4732e04b 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -6699,6 +6699,12 @@ gtk_event_controller_motion_get_type GtkEventControllerKey GtkEventControllerKey gtk_event_controller_key_new +gtk_event_controller_key_set_im_context +gtk_event_controller_key_get_im_context +gtk_event_controller_key_forward +gtk_event_controller_key_get_group +gtk_event_controller_key_get_focus_origin +gtk_event_controller_key_get_focus_target GTK_TYPE_EVENT_CONTROLLER_KEY -- GitLab From 42c73a3495fa6b7021de949f8dcb4c9389905349 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 13:16:13 -0500 Subject: [PATCH 62/70] Add a ::set_focus_child vfunc back This will allow widgets to update state when a child is focused. An example for where this is needed is updating the selection in listbox or flowbox. --- gtk/gtkwidget.c | 16 +++++++++++++--- gtk/gtkwidget.h | 6 +++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index d6e4154c229..5bd58fb4501 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -588,7 +588,9 @@ static void gtk_widget_real_size_allocate (GtkWidget *widget, static void gtk_widget_real_direction_changed(GtkWidget *widget, GtkTextDirection previous_direction); -static void gtk_widget_real_grab_focus (GtkWidget *focus_widget); +static void gtk_widget_real_grab_focus (GtkWidget *focus_widget); +static void gtk_widget_real_set_focus_child (GtkWidget *widget, + GtkWidget *child); static gboolean gtk_widget_real_query_tooltip (GtkWidget *widget, gint x, gint y, @@ -946,6 +948,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) klass->snapshot = gtk_widget_real_snapshot; klass->mnemonic_activate = gtk_widget_real_mnemonic_activate; klass->grab_focus = gtk_widget_real_grab_focus; + klass->set_focus_child = gtk_widget_real_set_focus_child; klass->next_focus_child = gtk_widget_next_focus_child; klass->move_focus = gtk_widget_real_move_focus; klass->keynav_failed = gtk_widget_real_keynav_failed; @@ -13286,8 +13289,6 @@ void gtk_widget_set_focus_child (GtkWidget *widget, GtkWidget *child) { - GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); - g_return_if_fail (GTK_IS_WIDGET (widget)); if (child != NULL) @@ -13296,6 +13297,15 @@ gtk_widget_set_focus_child (GtkWidget *widget, g_return_if_fail (gtk_widget_get_parent (child) == widget); } + GTK_WIDGET_GET_CLASS (widget)->set_focus_child (widget, child); +} + +static void +gtk_widget_real_set_focus_child (GtkWidget *widget, + GtkWidget *child) +{ + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); + g_set_object (&priv->focus_child, child); } diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index df93e95f1e3..438ef53011a 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -185,6 +185,8 @@ struct _GtkWidget * %FALSE, and just grabs the focus if @group_cycling is %TRUE. * @grab_focus: Causes @widget to have the keyboard focus for the * #GtkWindow it’s inside. + * @set_focus_child: Allows widget implementations to do extra actions when + * the child containing the focus is changed. Must chain up * @next_focus_child: Returns the next child that is a candidate for receiving focus. * Note that the returned child does not have to be focusable itself, it might just * contain focusable children. The default implementation returns all children, in @@ -280,7 +282,9 @@ struct _GtkWidgetClass gboolean group_cycling); /* explicit focus */ - void (* grab_focus) (GtkWidget *widget); + void (* grab_focus) (GtkWidget *widget); + void (* set_focus_child) (GtkWidget *widget, + GtkWidget *child); GtkWidget * (* next_focus_child) (GtkWidget *widget, GtkWidget *child, GtkDirectionType direction); -- GitLab From 0de3ac9bd809880d0e85c5ea776640971fccf84f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 13:52:46 -0500 Subject: [PATCH 63/70] Move maintaining the focus chain Put this code in the same place where we generate the crossing events. --- gtk/gtkmain.c | 15 +++++++++++++++ gtk/gtkwindow.c | 8 -------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 909827decdd..13a2aa5e76e 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1458,6 +1458,21 @@ synth_crossing (GtkWidget *widget, if (gdk_event_get_event_type (source) == GDK_FOCUS_CHANGE) { + /* maintain focus chain */ + if (enter || notify_type == GDK_NOTIFY_INFERIOR) + { + GtkWidget *parent = gtk_widget_get_parent (widget); + if (parent) + gtk_widget_set_focus_child (parent, widget); + } + else if (!enter && notify_type != GDK_NOTIFY_INFERIOR) + { + GtkWidget *parent = gtk_widget_get_parent (widget); + if (parent) + gtk_widget_set_focus_child (parent, NULL); + } + + /* maintain widget state */ if (notify_type == GDK_NOTIFY_ANCESTOR || notify_type == GDK_NOTIFY_INFERIOR || notify_type == GDK_NOTIFY_NONLINEAR) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 32edc43723e..0caa84e78bd 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -7069,7 +7069,6 @@ gtk_window_set_focus (GtkWindow *window, { GtkWindowPrivate *priv = gtk_window_get_instance_private (window); GtkWidget *old_focus = NULL; - GtkWidget *f; GdkSeat *seat; GdkDevice *device; GdkEvent *event; @@ -7101,13 +7100,6 @@ gtk_window_set_focus (GtkWindow *window, g_clear_object (&old_focus); - for (f = focus; f; f = gtk_widget_get_parent (f)) - { - GtkWidget *parent = gtk_widget_get_parent (f); - if (parent) - gtk_widget_set_focus_child (parent, f); - } - g_object_notify (G_OBJECT (window), "focus-widget"); } -- GitLab From 469f038c0024beb563bc03a1675e45e2207c5c33 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 13:46:17 -0500 Subject: [PATCH 64/70] Make gtk_widget_get_focus_child public I want to use it in a test, if nothing else. --- gtk/gtkwidget.h | 3 +++ gtk/gtkwidgetprivate.h | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 438ef53011a..c7581e77c0e 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -1070,6 +1070,9 @@ void gtk_widget_insert_before (GtkWidget *widget, GDK_AVAILABLE_IN_ALL void gtk_widget_set_focus_child (GtkWidget *widget, GtkWidget *child); +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_widget_get_focus_child (GtkWidget *widget); + GDK_AVAILABLE_IN_ALL void gtk_widget_set_child_focusable (GtkWidget *widget, gboolean focusable); diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index 543173e8e0e..c5f6949d169 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -318,8 +318,6 @@ void gtk_widget_forall (GtkWidget GtkCallback callback, gpointer user_data); -GtkWidget *gtk_widget_get_focus_child (GtkWidget *widget); - void gtk_widget_focus_sort (GtkWidget *widget, GtkDirectionType direction, GPtrArray *focus_order); -- GitLab From 5ce164ce8b52247619e7d61354161813eb88ca84 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Mar 2019 13:48:09 -0500 Subject: [PATCH 65/70] focus test: Verify the focus chain Make sure that the focus chain always leads from the toplevel to the focus widget. We don't verify that it is properly unset everywhere else, atm. --- testsuite/gtk/focus.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/testsuite/gtk/focus.c b/testsuite/gtk/focus.c index f8e4557ac46..82c188e0e24 100644 --- a/testsuite/gtk/focus.c +++ b/testsuite/gtk/focus.c @@ -75,6 +75,21 @@ focus_out (GtkEventController *controller, add_event (controller, FALSE, mode, detail, s); } +static void +verify_focus_chain (GtkWidget *window) +{ + GtkWidget *child, *last; + + child = window; + while (child) + { + last = child; + child = gtk_widget_get_focus_child (child); + } + + g_assert (last == gtk_root_get_focus (GTK_ROOT (window))); +} + static void add_controller (GtkWidget *widget, GString *s) { @@ -151,6 +166,7 @@ test_window_focus (void) /* show puts the initial focus on box */ g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == box); + verify_focus_chain (window); if (g_test_verbose ()) g_print ("-> box\n%s\n", s->str); @@ -158,6 +174,7 @@ test_window_focus (void) g_assert_cmpstr (s->str, ==, "window: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 1 origin: (null) target: box\n" "box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 1 contains-focus: 0 origin: (null) target: box\n"); + g_string_truncate (s, 0); gtk_widget_grab_focus (entry1); @@ -173,6 +190,7 @@ test_window_focus (void) g_string_truncate (s, 0); g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry1); + verify_focus_chain (window); gtk_widget_grab_focus (entry2); @@ -188,6 +206,7 @@ test_window_focus (void) g_string_truncate (s, 0); g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry2); + verify_focus_chain (window); gtk_widget_grab_focus (box); @@ -204,6 +223,7 @@ test_window_focus (void) gtk_widget_hide (window); g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == box); + verify_focus_chain (window); gtk_window_set_focus (GTK_WINDOW (window), entry1); -- GitLab From 2511c4586b42fa9cd17a3e61476def04b1e3211c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 9 Mar 2019 21:04:33 -0500 Subject: [PATCH 66/70] toolbar: Fix keynav Reinstate the only-arrows-move inside behavior. --- gtk/gtktoolbar.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gtk/gtktoolbar.c b/gtk/gtktoolbar.c index f7c41b87030..c2453fc7a8d 100644 --- a/gtk/gtktoolbar.c +++ b/gtk/gtktoolbar.c @@ -345,6 +345,19 @@ add_ctrl_tab_bindings (GtkBindingSet *binding_set, GTK_TYPE_DIRECTION_TYPE, direction); } +static GtkWidget * +gtk_toolbar_next_focus_child (GtkWidget *widget, + GtkWidget *child, + GtkDirectionType direction) +{ + if (child && + (direction == GTK_DIR_TAB_FORWARD || + direction == GTK_DIR_TAB_BACKWARD)) + return NULL; + + return GTK_WIDGET_CLASS (gtk_toolbar_parent_class)->next_focus_child (widget, child, direction); +} + static void gtk_toolbar_class_init (GtkToolbarClass *klass) { @@ -366,6 +379,7 @@ gtk_toolbar_class_init (GtkToolbarClass *klass) widget_class->measure = gtk_toolbar_measure; widget_class->size_allocate = gtk_toolbar_size_allocate; widget_class->style_updated = gtk_toolbar_style_updated; + widget_class->next_focus_child = gtk_toolbar_next_focus_child; gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_TOOL_BAR); -- GitLab From 998215942c8e3912b0569489e177623dc058880a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 11 Mar 2019 13:34:00 -0400 Subject: [PATCH 67/70] notebook: Remove can-focus This is not really necessary, and removing it makes the focus chain work better. --- gtk/gtknotebook.c | 1 - 1 file changed, 1 deletion(-) diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index c7fa88bc322..0c48edfbfa4 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -1232,7 +1232,6 @@ gtk_notebook_init (GtkNotebook *notebook) GtkEventController *controller; GtkGesture *gesture; - gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE); gtk_widget_set_has_surface (GTK_WIDGET (notebook), FALSE); notebook->priv = gtk_notebook_get_instance_private (notebook); -- GitLab From 6a54cb3d3b0bb076aac079a8be13ffadbc887d2c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 11 Mar 2019 15:08:12 -0400 Subject: [PATCH 68/70] box: Add a next-focus-chain implementation This just uses the child order for Tab/Shift-Tab. --- gtk/gtkbox.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/gtk/gtkbox.c b/gtk/gtkbox.c index 307276c63f4..851b3efde53 100644 --- a/gtk/gtkbox.c +++ b/gtk/gtkbox.c @@ -123,6 +123,9 @@ static void gtk_box_measure (GtkWidget *widget, int *natural, int *minimum_baseline, int *natural_baseline); +GtkWidget * gtk_box_next_focus_child (GtkWidget *widget, + GtkWidget *child, + GtkDirectionType direction); G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER, G_ADD_PRIVATE (GtkBox) @@ -140,6 +143,7 @@ gtk_box_class_init (GtkBoxClass *class) widget_class->size_allocate = gtk_box_size_allocate; widget_class->measure = gtk_box_measure; + widget_class->next_focus_child = gtk_box_next_focus_child; container_class->add = gtk_box_add; container_class->remove = gtk_box_remove; @@ -1149,3 +1153,26 @@ gtk_box_reorder_child_after (GtkBox *box, gtk_widget_get_css_node (child), sibling ? gtk_widget_get_css_node (sibling) : NULL); } + +GtkWidget * +gtk_box_next_focus_child (GtkWidget *widget, + GtkWidget *child, + GtkDirectionType direction) +{ + if (direction == GTK_DIR_TAB_FORWARD) + { + if (child) + return gtk_widget_get_next_sibling (child); + else + return gtk_widget_get_first_child (widget); + } + else if (direction == GTK_DIR_TAB_BACKWARD) + { + if (child) + return gtk_widget_get_prev_sibling (child); + else + return gtk_widget_get_last_child (widget); + } + + return GTK_WIDGET_CLASS (gtk_box_parent_class)->next_focus_child (widget, child, direction); +} -- GitLab From 115c7b6dba8a0d7c2f4c9524c4d8cada2f715eaf Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 11 Mar 2019 15:35:13 -0400 Subject: [PATCH 69/70] gizmo: Make it possible to override focus This will be used in GtkNotebook. --- gtk/gtkgizmo.c | 25 +++++++++++++++++++++++++ gtk/gtkgizmoprivate.h | 11 ++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/gtk/gtkgizmo.c b/gtk/gtkgizmo.c index 305e0b524e5..b3272578a99 100644 --- a/gtk/gtkgizmo.c +++ b/gtk/gtkgizmo.c @@ -46,6 +46,19 @@ gtk_gizmo_snapshot (GtkWidget *widget, GTK_WIDGET_CLASS (gtk_gizmo_parent_class)->snapshot (widget, snapshot); } +static GtkWidget * +gtk_gizmo_next_focus_child (GtkWidget *widget, + GtkWidget *child, + GtkDirectionType direction) +{ + GtkGizmo *self = GTK_GIZMO (widget); + + if (self->focus_func) + return self->focus_func (self, child, direction); + else + return GTK_WIDGET_CLASS (gtk_gizmo_parent_class)->next_focus_child (widget, child, direction); +} + static void gtk_gizmo_finalize (GObject *object) { @@ -76,6 +89,7 @@ gtk_gizmo_class_init (GtkGizmoClass *klass) widget_class->measure = gtk_gizmo_measure; widget_class->size_allocate = gtk_gizmo_size_allocate; widget_class->snapshot = gtk_gizmo_snapshot; + widget_class->next_focus_child = gtk_gizmo_next_focus_child; } static void @@ -89,6 +103,16 @@ gtk_gizmo_new (const char *css_name, GtkGizmoMeasureFunc measure_func, GtkGizmoAllocateFunc allocate_func, GtkGizmoSnapshotFunc snapshot_func) +{ + return gtk_gizmo_new_with_focus (css_name, measure_func, allocate_func, snapshot_func, NULL); +} + +GtkWidget * +gtk_gizmo_new_with_focus (const char *css_name, + GtkGizmoMeasureFunc measure_func, + GtkGizmoAllocateFunc allocate_func, + GtkGizmoSnapshotFunc snapshot_func, + GtkGizmoFocusFunc focus_func) { GtkGizmo *gizmo = GTK_GIZMO (g_object_new (GTK_TYPE_GIZMO, "css-name", css_name, @@ -97,6 +121,7 @@ gtk_gizmo_new (const char *css_name, gizmo->measure_func = measure_func; gizmo->allocate_func = allocate_func; gizmo->snapshot_func = snapshot_func; + gizmo->focus_func = focus_func; return GTK_WIDGET (gizmo); } diff --git a/gtk/gtkgizmoprivate.h b/gtk/gtkgizmoprivate.h index c65a4303517..d1e879d6326 100644 --- a/gtk/gtkgizmoprivate.h +++ b/gtk/gtkgizmoprivate.h @@ -27,7 +27,9 @@ typedef void (* GtkGizmoAllocateFunc) (GtkGizmo *gizmo, int baseline); typedef void (* GtkGizmoSnapshotFunc) (GtkGizmo *gizmo, GtkSnapshot *snapshot); - +typedef GtkWidget * (* GtkGizmoFocusFunc) (GtkGizmo *gizmo, + GtkWidget *child, + GtkDirectionType direction); struct _GtkGizmo { @@ -36,6 +38,7 @@ struct _GtkGizmo GtkGizmoMeasureFunc measure_func; GtkGizmoAllocateFunc allocate_func; GtkGizmoSnapshotFunc snapshot_func; + GtkGizmoFocusFunc focus_func; }; struct _GtkGizmoClass @@ -50,5 +53,11 @@ GtkWidget *gtk_gizmo_new (const char *css_name, GtkGizmoAllocateFunc allocate_func, GtkGizmoSnapshotFunc snapshot_func); +GtkWidget *gtk_gizmo_new_with_focus (const char *css_name, + GtkGizmoMeasureFunc measure_func, + GtkGizmoAllocateFunc allocate_func, + GtkGizmoSnapshotFunc snapshot_func, + GtkGizmoFocusFunc focus_func); + #endif -- GitLab From 5ddbfef8c9fb8ad75d0e9d0f487971832ff06579 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 11 Mar 2019 15:35:51 -0400 Subject: [PATCH 70/70] notebook: Reinstate previous focus behavior We want the overall tab order to be start-action current tab end-action page To achieve this, make the tabs_widget have only a single focus location, for the current tab. --- gtk/gtknotebook.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index 0c48edfbfa4..099b470d3b0 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -704,6 +704,9 @@ static void gtk_notebook_allocate_tabs (GtkGizmo *gizmo, int baseline); static void gtk_notebook_snapshot_tabs (GtkGizmo *gizmo, GtkSnapshot *snapshot); +static GtkWidget *gtk_notebook_next_focus_child_tabs (GtkGizmo *gizmo, + GtkWidget *child, + GtkDirectionType direction); /*** GtkNotebook Private Functions ***/ static void gtk_notebook_real_remove (GtkNotebook *notebook, @@ -1281,10 +1284,11 @@ gtk_notebook_init (GtkNotebook *notebook) gtk_widget_hide (priv->header_widget); gtk_container_add (GTK_CONTAINER (priv->box), priv->header_widget); - priv->tabs_widget = gtk_gizmo_new ("tabs", - gtk_notebook_measure_tabs, - gtk_notebook_allocate_tabs, - gtk_notebook_snapshot_tabs); + priv->tabs_widget = gtk_gizmo_new_with_focus ("tabs", + gtk_notebook_measure_tabs, + gtk_notebook_allocate_tabs, + gtk_notebook_snapshot_tabs, + gtk_notebook_next_focus_child_tabs); gtk_widget_set_hexpand (priv->tabs_widget, TRUE); gtk_container_add (GTK_CONTAINER (priv->header_widget), priv->tabs_widget); @@ -4238,6 +4242,27 @@ gtk_notebook_snapshot_tabs (GtkGizmo *gizmo, gtk_widget_snapshot_child (GTK_WIDGET (gizmo), priv->cur_page->tab_widget, snapshot); } +static GtkWidget * +gtk_notebook_next_focus_child_tabs (GtkGizmo *gizmo, + GtkWidget *child, + GtkDirectionType direction) +{ + GtkWidget *widget = gtk_widget_get_parent (gtk_widget_get_parent (GTK_WIDGET (gizmo))); + GtkNotebook *notebook = GTK_NOTEBOOK (gtk_widget_get_parent (widget)); + GtkNotebookPrivate *priv = notebook->priv; + + if (direction == GTK_DIR_TAB_FORWARD || + direction == GTK_DIR_TAB_BACKWARD) + { + if (child == NULL) + return priv->cur_page->tab_widget; + else + return NULL; + } + + return gtk_widget_next_focus_child (GTK_WIDGET (gizmo), child, direction); +} + /* Private GtkNotebook Size Allocate Functions: * * gtk_notebook_calculate_shown_tabs @@ -7040,4 +7065,3 @@ gtk_notebook_get_pages (GtkNotebook *notebook) return priv->pages; } - -- GitLab