From 23b98cc1ec97aa5b50c28af204eb6d3fe7bf00c6 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 22 Mar 2001 20:59:37 +0000 Subject: [PATCH] Add ability to override the focus chain for a container explicitly 2001-03-22 Havoc Pennington * gtk/gtkcontainer.c (gtk_container_set_focus_chain): (gtk_container_unset_focus_chain): Add ability to override the focus chain for a container explicitly --- ChangeLog | 6 ++ ChangeLog.pre-2-0 | 6 ++ ChangeLog.pre-2-10 | 6 ++ ChangeLog.pre-2-2 | 6 ++ ChangeLog.pre-2-4 | 6 ++ ChangeLog.pre-2-6 | 6 ++ ChangeLog.pre-2-8 | 6 ++ gtk/gtkcontainer.c | 206 ++++++++++++++++++++++++++++++++++++++------- gtk/gtkcontainer.h | 5 ++ gtk/testgtk.c | 111 ++++++++++++++++++++++++ tests/testgtk.c | 111 ++++++++++++++++++++++++ 11 files changed, 446 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6d3f0fcced..d14b417bcc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2001-03-22 Havoc Pennington + + * gtk/gtkcontainer.c (gtk_container_set_focus_chain): + (gtk_container_unset_focus_chain): Add ability to override the + focus chain for a container explicitly + Thu Mar 22 13:01:44 2001 Tim Janik * gtk/gtklabel.[hc]: some cleanups, fixed mnemonic_widget handling, diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index 6d3f0fcced..d14b417bcc 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,9 @@ +2001-03-22 Havoc Pennington + + * gtk/gtkcontainer.c (gtk_container_set_focus_chain): + (gtk_container_unset_focus_chain): Add ability to override the + focus chain for a container explicitly + Thu Mar 22 13:01:44 2001 Tim Janik * gtk/gtklabel.[hc]: some cleanups, fixed mnemonic_widget handling, diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 6d3f0fcced..d14b417bcc 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,9 @@ +2001-03-22 Havoc Pennington + + * gtk/gtkcontainer.c (gtk_container_set_focus_chain): + (gtk_container_unset_focus_chain): Add ability to override the + focus chain for a container explicitly + Thu Mar 22 13:01:44 2001 Tim Janik * gtk/gtklabel.[hc]: some cleanups, fixed mnemonic_widget handling, diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index 6d3f0fcced..d14b417bcc 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,9 @@ +2001-03-22 Havoc Pennington + + * gtk/gtkcontainer.c (gtk_container_set_focus_chain): + (gtk_container_unset_focus_chain): Add ability to override the + focus chain for a container explicitly + Thu Mar 22 13:01:44 2001 Tim Janik * gtk/gtklabel.[hc]: some cleanups, fixed mnemonic_widget handling, diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 6d3f0fcced..d14b417bcc 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,9 @@ +2001-03-22 Havoc Pennington + + * gtk/gtkcontainer.c (gtk_container_set_focus_chain): + (gtk_container_unset_focus_chain): Add ability to override the + focus chain for a container explicitly + Thu Mar 22 13:01:44 2001 Tim Janik * gtk/gtklabel.[hc]: some cleanups, fixed mnemonic_widget handling, diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 6d3f0fcced..d14b417bcc 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,9 @@ +2001-03-22 Havoc Pennington + + * gtk/gtkcontainer.c (gtk_container_set_focus_chain): + (gtk_container_unset_focus_chain): Add ability to override the + focus chain for a container explicitly + Thu Mar 22 13:01:44 2001 Tim Janik * gtk/gtklabel.[hc]: some cleanups, fixed mnemonic_widget handling, diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 6d3f0fcced..d14b417bcc 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,9 @@ +2001-03-22 Havoc Pennington + + * gtk/gtkcontainer.c (gtk_container_set_focus_chain): + (gtk_container_unset_focus_chain): Add ability to override the + focus chain for a container explicitly + Thu Mar 22 13:01:44 2001 Tim Janik * gtk/gtklabel.[hc]: some cleanups, fixed mnemonic_widget handling, diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c index 760e152966..58bdd5674e 100644 --- a/gtk/gtkcontainer.c +++ b/gtk/gtkcontainer.c @@ -42,6 +42,7 @@ enum { SET_FOCUS_CHILD, LAST_SIGNAL }; + enum { ARG_0, ARG_BORDER_WIDTH, @@ -626,6 +627,12 @@ gtk_container_destroy (GtkObject *object) gtk_container_dequeue_resize_handler (container); if (container->resize_widgets) gtk_container_clear_resize_widgets (container); + + /* do this before walking child widgets, to avoid + * removing children from focus chain one by one. + */ + if (container->has_focus_chain) + gtk_container_unset_focus_chain (container); gtk_container_foreach (container, (GtkCallback) gtk_widget_destroy, NULL); @@ -1415,13 +1422,48 @@ gtk_container_real_set_focus_child (GtkContainer *container, } } +static GList* +get_focus_chain (GtkContainer *container) +{ + GList *chain; + + chain = g_object_get_data (G_OBJECT (container), "gtk-container-focus-chain"); + + return chain; +} + +static GList* +filter_unfocusable (GtkContainer *container, + GList *list) +{ + GList *tmp_list; + GList *tmp_list2; + + tmp_list = list; + while (tmp_list) + { + if (GTK_WIDGET_IS_SENSITIVE (tmp_list->data) && + GTK_WIDGET_DRAWABLE (tmp_list->data) && + (GTK_IS_CONTAINER (tmp_list->data) || GTK_WIDGET_CAN_FOCUS (tmp_list->data))) + tmp_list = tmp_list->next; + else + { + tmp_list2 = tmp_list; + tmp_list = tmp_list->next; + + list = g_list_remove_link (list, tmp_list2); + g_list_free_1 (tmp_list2); + } + } + + return list; +} + static gboolean gtk_container_real_focus (GtkContainer *container, GtkDirectionType direction) { GList *children; - GList *tmp_list; - GList *tmp_list2; gint return_val; g_return_val_if_fail (container != NULL, FALSE); @@ -1445,41 +1487,44 @@ gtk_container_real_focus (GtkContainer *container, } else { - /* Get a list of the containers children + /* Get a list of the containers children, allowing focus + * chain to override. */ - children = NULL; - gtk_container_forall (container, - gtk_container_children_callback, - &children); - children = g_list_reverse (children); - /* children = gtk_container_children (container); */ + if (container->has_focus_chain) + { + GList *chain; + + chain = get_focus_chain (container); + + children = g_list_copy (chain); + } + else + { + children = NULL; + gtk_container_forall (container, + gtk_container_children_callback, + &children); + children = g_list_reverse (children); + } if (children) { /* Remove any children which are inappropriate for focus movement */ - tmp_list = children; - while (tmp_list) - { - if (GTK_WIDGET_IS_SENSITIVE (tmp_list->data) && - GTK_WIDGET_DRAWABLE (tmp_list->data) && - (GTK_IS_CONTAINER (tmp_list->data) || GTK_WIDGET_CAN_FOCUS (tmp_list->data))) - tmp_list = tmp_list->next; - else - { - tmp_list2 = tmp_list; - tmp_list = tmp_list->next; - - children = g_list_remove_link (children, tmp_list2); - g_list_free_1 (tmp_list2); - } - } - + children = filter_unfocusable (container, children); + switch (direction) { case GTK_DIR_TAB_FORWARD: case GTK_DIR_TAB_BACKWARD: - return_val = gtk_container_focus_tab (container, children, direction); + if (container->has_focus_chain) + { + if (direction == GTK_DIR_TAB_BACKWARD) + children = g_list_reverse (children); + return_val = gtk_container_focus_move (container, children, direction); + } + else + return_val = gtk_container_focus_tab (container, children, direction); break; case GTK_DIR_UP: case GTK_DIR_DOWN: @@ -1864,7 +1909,7 @@ gtk_container_focus_move (GtkContainer *container, if (!child) continue; - + if (focus_child) { if (focus_child == child) @@ -1877,7 +1922,8 @@ gtk_container_focus_move (GtkContainer *container, return TRUE; } } - else if (GTK_WIDGET_DRAWABLE (child)) + else if (GTK_WIDGET_DRAWABLE (child) && + gtk_widget_is_ancestor (child, GTK_WIDGET (container))) { if (GTK_IS_CONTAINER (child)) { @@ -1906,6 +1952,108 @@ gtk_container_children_callback (GtkWidget *widget, *children = g_list_prepend (*children, widget); } + +/* Hack-around */ +#define g_signal_handlers_disconnect_by_func(obj, func, data) g_signal_handlers_disconnect_matched (obj, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, func, data) + +static void +chain_widget_destroyed (GtkWidget *widget, + gpointer user_data) +{ + GtkContainer *container; + GList *chain; + + container = GTK_CONTAINER (user_data); + + chain = g_object_get_data (G_OBJECT (container), + "gtk-container-focus-chain"); + + chain = g_list_remove (chain, widget); + + g_signal_handlers_disconnect_by_func (G_OBJECT (widget), + chain_widget_destroyed, + user_data); + + g_object_set_data (G_OBJECT (container), + "gtk-container-focus-chain", + chain); +} + +void +gtk_container_set_focus_chain (GtkContainer *container, + GList *focusable_widgets) +{ + GList *chain; + GList *tmp_list; + + g_return_if_fail (GTK_IS_CONTAINER (container)); + + if (container->has_focus_chain) + gtk_container_unset_focus_chain (container); + + container->has_focus_chain = TRUE; + + chain = NULL; + tmp_list = focusable_widgets; + while (tmp_list != NULL) + { + g_return_if_fail (GTK_IS_WIDGET (tmp_list->data)); + + /* In principle each widget in the chain should be a descendant + * of the container, but we don't want to check that here, it's + * expensive and also it's allowed to set the focus chain before + * you pack the widgets, or have a widget in the chain that isn't + * always packed. So we check for ancestor during actual traversal. + */ + + chain = g_list_prepend (chain, tmp_list->data); + + gtk_signal_connect (GTK_OBJECT (tmp_list->data), + "destroy", + GTK_SIGNAL_FUNC (chain_widget_destroyed), + container); + + tmp_list = g_list_next (tmp_list); + } + + chain = g_list_reverse (chain); + + g_object_set_data (G_OBJECT (container), + "gtk-container-focus-chain", + chain); +} + +void +gtk_container_unset_focus_chain (GtkContainer *container) +{ + g_return_if_fail (GTK_IS_CONTAINER (container)); + + if (container->has_focus_chain) + { + GList *chain; + GList *tmp_list; + + chain = get_focus_chain (container); + + container->has_focus_chain = FALSE; + + g_object_set_data (G_OBJECT (container), "gtk-container-focus-chain", + NULL); + + tmp_list = chain; + while (tmp_list != NULL) + { + g_signal_handlers_disconnect_by_func (G_OBJECT (tmp_list->data), + chain_widget_destroyed, + container); + + tmp_list = g_list_next (tmp_list); + } + + g_list_free (chain); + } +} + void gtk_container_set_focus_vadjustment (GtkContainer *container, GtkAdjustment *adjustment) diff --git a/gtk/gtkcontainer.h b/gtk/gtkcontainer.h index 9b725b89f8..fa1c03add5 100644 --- a/gtk/gtkcontainer.h +++ b/gtk/gtkcontainer.h @@ -62,6 +62,7 @@ struct _GtkContainer guint need_resize : 1; guint resize_mode : 2; guint reallocate_redraws : 1; + guint has_focus_chain : 1; /* The list of children that requested a resize */ @@ -134,6 +135,10 @@ void gtk_container_propagate_expose (GtkContainer *container, GtkWidget *child, GdkEventExpose *event); +void gtk_container_set_focus_chain (GtkContainer *container, + GList *focusable_widgets); +void gtk_container_unset_focus_chain (GtkContainer *container); + /* Widget-level methods */ void gtk_container_set_reallocate_redraws (GtkContainer *container, diff --git a/gtk/testgtk.c b/gtk/testgtk.c index b44d880c6f..37ec0b7761 100644 --- a/gtk/testgtk.c +++ b/gtk/testgtk.c @@ -6215,6 +6215,116 @@ create_flipping (void) gtk_widget_destroy (window); } +/* + * Focus test + */ + +static GtkWidget* +make_focus_table (GList **list) +{ + GtkWidget *table; + gint i, j; + + table = gtk_table_new (5, 5, FALSE); + + i = 0; + j = 0; + + while (i < 5) + { + j = 0; + while (j < 5) + { + GtkWidget *widget; + + if ((i + j) % 2) + widget = gtk_entry_new (); + else + widget = gtk_button_new_with_label ("Foo"); + + *list = g_list_prepend (*list, widget); + + gtk_table_attach (GTK_TABLE (table), + widget, + i, i + 1, + j, j + 1, + GTK_EXPAND | GTK_FILL, + GTK_EXPAND | GTK_FILL, + 5, 5); + + ++j; + } + + ++i; + } + + *list = g_list_reverse (*list); + + return table; +} + +static void +create_focus (void) +{ + static GtkWidget *window = NULL; + + if (!window) + { + GtkWidget *table; + GtkWidget *frame; + GList *list = NULL; + GList *first = NULL, *second = NULL, *tmp_list = NULL; + gint i; + + window = gtk_dialog_new_with_buttons ("Keyboard focus navigation", + NULL, 0, + GTK_STOCK_BUTTON_CLOSE, + GTK_RESPONSE_NONE, + NULL); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (gtk_widget_destroyed), + &window); + + gtk_signal_connect (GTK_OBJECT (window), "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); + + gtk_window_set_title (GTK_WINDOW (window), "Keyboard Focus Navigation"); + + frame = gtk_frame_new ("Weird tab focus chain"); + + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), + frame, TRUE, TRUE, 0); + + table = make_focus_table (&list); + + gtk_container_add (GTK_CONTAINER (frame), table); + + gtk_container_set_focus_chain (GTK_CONTAINER (table), + list); + + g_list_free (list); + + frame = gtk_frame_new ("Default tab focus chain"); + + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), + frame, TRUE, TRUE, 0); + + list = NULL; + table = make_focus_table (&list); + + g_list_free (list); + + gtk_container_add (GTK_CONTAINER (frame), table); + } + + if (!GTK_WIDGET_VISIBLE (window)) + gtk_widget_show_all (window); + else + gtk_widget_destroy (window); +} + /* * GtkFontSelection */ @@ -9705,6 +9815,7 @@ create_main_window (void) { "event watcher", create_event_watcher }, { "file selection", create_file_selection }, { "flipping", create_flipping }, + { "focus", create_focus }, { "font selection", create_font_selection }, { "gamma curve", create_gamma_curve }, { "handle box", create_handle_box }, diff --git a/tests/testgtk.c b/tests/testgtk.c index b44d880c6f..37ec0b7761 100644 --- a/tests/testgtk.c +++ b/tests/testgtk.c @@ -6215,6 +6215,116 @@ create_flipping (void) gtk_widget_destroy (window); } +/* + * Focus test + */ + +static GtkWidget* +make_focus_table (GList **list) +{ + GtkWidget *table; + gint i, j; + + table = gtk_table_new (5, 5, FALSE); + + i = 0; + j = 0; + + while (i < 5) + { + j = 0; + while (j < 5) + { + GtkWidget *widget; + + if ((i + j) % 2) + widget = gtk_entry_new (); + else + widget = gtk_button_new_with_label ("Foo"); + + *list = g_list_prepend (*list, widget); + + gtk_table_attach (GTK_TABLE (table), + widget, + i, i + 1, + j, j + 1, + GTK_EXPAND | GTK_FILL, + GTK_EXPAND | GTK_FILL, + 5, 5); + + ++j; + } + + ++i; + } + + *list = g_list_reverse (*list); + + return table; +} + +static void +create_focus (void) +{ + static GtkWidget *window = NULL; + + if (!window) + { + GtkWidget *table; + GtkWidget *frame; + GList *list = NULL; + GList *first = NULL, *second = NULL, *tmp_list = NULL; + gint i; + + window = gtk_dialog_new_with_buttons ("Keyboard focus navigation", + NULL, 0, + GTK_STOCK_BUTTON_CLOSE, + GTK_RESPONSE_NONE, + NULL); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (gtk_widget_destroyed), + &window); + + gtk_signal_connect (GTK_OBJECT (window), "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); + + gtk_window_set_title (GTK_WINDOW (window), "Keyboard Focus Navigation"); + + frame = gtk_frame_new ("Weird tab focus chain"); + + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), + frame, TRUE, TRUE, 0); + + table = make_focus_table (&list); + + gtk_container_add (GTK_CONTAINER (frame), table); + + gtk_container_set_focus_chain (GTK_CONTAINER (table), + list); + + g_list_free (list); + + frame = gtk_frame_new ("Default tab focus chain"); + + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), + frame, TRUE, TRUE, 0); + + list = NULL; + table = make_focus_table (&list); + + g_list_free (list); + + gtk_container_add (GTK_CONTAINER (frame), table); + } + + if (!GTK_WIDGET_VISIBLE (window)) + gtk_widget_show_all (window); + else + gtk_widget_destroy (window); +} + /* * GtkFontSelection */ @@ -9705,6 +9815,7 @@ create_main_window (void) { "event watcher", create_event_watcher }, { "file selection", create_file_selection }, { "flipping", create_flipping }, + { "focus", create_focus }, { "font selection", create_font_selection }, { "gamma curve", create_gamma_curve }, { "handle box", create_handle_box },