diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 07080d6b9e..dc2d00c207 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -5522,31 +5522,79 @@ gtk_widget_real_style_updated (GtkWidget *widget) } } +static gboolean +direction_is_forward (GtkDirectionType direction) +{ + switch (direction) + { + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_RIGHT: + case GTK_DIR_DOWN: + return TRUE; + case GTK_DIR_TAB_BACKWARD: + case GTK_DIR_LEFT: + case GTK_DIR_UP: + return FALSE; + default: + g_assert_not_reached (); + } +} + static gboolean gtk_widget_real_focus (GtkWidget *widget, GtkDirectionType direction) { - if (gtk_widget_get_can_focus (widget)) + GtkWidget *focus; + + /* The easy case: not focusable. Just try the children */ + if (!gtk_widget_get_can_focus (widget)) { - if (!gtk_widget_is_focus (widget)) + if (gtk_widget_focus_move (widget, direction)) + return TRUE; + + return FALSE; + } + + /* For focusable widgets, we want to focus the widget + * before its children. We differentiate 3 cases: + * 1) focus is currently on widget + * 2) focus is on some child + * 3) focus is outside + */ + + if (gtk_widget_is_focus (widget)) + { + if (direction_is_forward (direction) && + gtk_widget_focus_move (widget, direction)) + return TRUE; + + return FALSE; + } + + focus = gtk_window_get_focus (GTK_WINDOW (gtk_widget_get_root (widget))); + + if (focus && gtk_widget_is_ancestor (focus, widget)) + { + if (gtk_widget_focus_move (widget, direction)) + return TRUE; + + if (direction_is_forward (direction)) + return FALSE; + else { gtk_widget_grab_focus (widget); return TRUE; } } - else if (_gtk_widget_get_first_child (widget) == NULL) + + if (!direction_is_forward (direction)) { - /* No children, no possibility to focus anything */ - return FALSE; - } - else - { - /* Try focusing any of the child widgets, depending on the given direction */ if (gtk_widget_focus_move (widget, direction)) return TRUE; } - return FALSE; + gtk_widget_grab_focus (widget); + return TRUE; } static void diff --git a/testsuite/gtk/focus-chain/focusable-container.tab b/testsuite/gtk/focus-chain/focusable-container.tab new file mode 100644 index 0000000000..21816d5316 --- /dev/null +++ b/testsuite/gtk/focus-chain/focusable-container.tab @@ -0,0 +1,5 @@ +entry1 GtkText +box +entry2 GtkText +entry3 GtkText +WRAP diff --git a/testsuite/gtk/focus-chain/focusable-container.tab-backward b/testsuite/gtk/focus-chain/focusable-container.tab-backward new file mode 100644 index 0000000000..46d54a6137 --- /dev/null +++ b/testsuite/gtk/focus-chain/focusable-container.tab-backward @@ -0,0 +1,5 @@ +entry3 GtkText +entry2 GtkText +box +entry1 GtkText +WRAP diff --git a/testsuite/gtk/focus-chain/focusable-container.ui b/testsuite/gtk/focus-chain/focusable-container.ui new file mode 100644 index 0000000000..e6d2947d20 --- /dev/null +++ b/testsuite/gtk/focus-chain/focusable-container.ui @@ -0,0 +1,29 @@ + + + + + + + entry1 + + + + + box + 1 + + + entry2 + + + + + entry3 + + + + + + + + diff --git a/testsuite/gtk/meson.build b/testsuite/gtk/meson.build index f331e02d41..d3a4ea13f0 100644 --- a/testsuite/gtk/meson.build +++ b/testsuite/gtk/meson.build @@ -136,6 +136,8 @@ focus_chain_tests = [ [ 'basic', 'tab-backward' ], [ 'basic', 'left' ], [ 'basic', 'right' ], + [ 'focusable-container', 'tab' ], + [ 'focusable-container', 'tab-backward' ], ] focus_chain = executable( diff --git a/testsuite/gtk/test-focus-chain.c b/testsuite/gtk/test-focus-chain.c index dac0d114ac..a76082feca 100644 --- a/testsuite/gtk/test-focus-chain.c +++ b/testsuite/gtk/test-focus-chain.c @@ -92,9 +92,10 @@ generate_focus_chain (GtkWidget *window, { char *first = NULL; char *last = NULL; - char *name; + char *name = NULL; GString *output = g_string_new (""); GtkWidget *focus; + int count = 0; gtk_widget_show (window); @@ -135,6 +136,7 @@ generate_focus_chain (GtkWidget *window, } g_string_append_printf (output, "%s\n", name); + count++; if (!first) first = g_strdup (name); @@ -142,9 +144,16 @@ generate_focus_chain (GtkWidget *window, g_free (last); last = g_strdup (name); + if (count == 100) + { + g_string_append (output, "ABORT\n"); + break; + } + g_free (name); } + g_free (name); g_free (first); g_free (last);