Proposed fix for bug 53612 (KEYNAV:GtkNotebook)



Bug 53612 identifies four changes to the operation of GtkNotebook:

1) If any control on the frontmost page of the notebook (including its page
label) has focus, Ctrl+Tab (*) should move focus to the first control after
the notebook.

If any control on the frontmost page of the notebook (including its page
label) has focus, Shift+Ctrl+Tab should move focus to the last control
before the notebook.

2) When any control in the notebook has focus (including any page label),
Ctrl+PgUp should bring the next tab to the front, and Ctrl+PgDn should
bring the previous tab to the front.  In either case, the control on the
new frontmost page that last had focus is given focus again.

3) When any page label has focus, pressing Tab or Ctrl+down arrow should
give focus to the first control on that page, bringing the selected page to
the front if necessary.

4) When any control on the frontmost page has focus, pressing Ctrl+up arrow
should give focus to that page's label.

I have attempted to address these issues withe the attached patch.
For 1), 2) and 4) a signal move_focus_out, move_focus_page, move_focus_up 
is defined for the required operation and if an ancestor of the focus
widget supports the required operation it happens.
For 3) a signal move_focus and key bindings are defined in gtknotebook.c 
to implement the operation.

What changes are needed before this change can be committed?

Padraig

Index: gtknotebook.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtknotebook.c,v
retrieving revision 1.97
diff -u -p -r1.97 gtknotebook.c
--- gtknotebook.c	2001/07/31 21:23:19	1.97
+++ gtknotebook.c	2001/09/14 11:11:36
@@ -49,6 +49,7 @@ enum {
   SWITCH_PAGE,
   FOCUS_TAB,
   SELECT_PAGE,
+  MOVE_FOCUS,
   LAST_SIGNAL
 };
 
@@ -115,6 +116,7 @@ static void gtk_notebook_init           
 
 static void gtk_notebook_select_page         (GtkNotebook       *notebook,
                                               gboolean           move_focus);
+static void gtk_notebook_move_focus          (GtkNotebook       *notebook);
 static void gtk_notebook_focus_tab           (GtkNotebook       *notebook,
                                               GtkNotebookTab     type);
 
@@ -317,6 +319,7 @@ gtk_notebook_class_init (GtkNotebookClas
 
   class->focus_tab = gtk_notebook_focus_tab;
   class->select_page = gtk_notebook_select_page;
+  class->move_focus = gtk_notebook_move_focus;
   
   g_object_class_install_property (gobject_class,
 				   PROP_PAGE,
@@ -456,6 +459,15 @@ gtk_notebook_class_init (GtkNotebookClas
                   gtk_marshal_VOID__BOOLEAN,
                   G_TYPE_NONE, 1,
                   G_TYPE_BOOLEAN);
+  notebook_signals[MOVE_FOCUS] = 
+    g_signal_new ("move_focus",
+                  G_TYPE_FROM_CLASS (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (GtkNotebookClass, move_focus),
+                  NULL, NULL,
+                  gtk_marshal_VOID__VOID,
+                  G_TYPE_NONE, 
+                  0);
   
   binding_set = gtk_binding_set_by_class (object_class);
   gtk_binding_entry_add_signal (binding_set,
@@ -491,6 +503,16 @@ gtk_notebook_class_init (GtkNotebookClas
                                 GDK_KP_End, 0,
                                 "focus_tab", 1, 
                                 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
+  gtk_binding_entry_add_signal (binding_set, GDK_Tab, 0,
+                                "move_focus", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, 0,
+                                "move_focus", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_ISO_Left_Tab, 0,
+                                "move_focus", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_Down, GDK_CONTROL_MASK,
+                                "move_focus", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, GDK_CONTROL_MASK,
+                                "move_focus", 0);
 }
 
 static void
@@ -519,6 +541,12 @@ gtk_notebook_init (GtkNotebook *notebook
   notebook->need_timer = 0;
   notebook->child_has_focus = FALSE;
   notebook->have_visible_child = FALSE;
+  gtk_object_set_data (GTK_OBJECT (notebook), "support-focus-out", 
+                       GINT_TO_POINTER (1));
+  gtk_object_set_data (GTK_OBJECT (notebook), "support-focus-page", 
+                       GINT_TO_POINTER (1));
+  gtk_object_set_data (GTK_OBJECT (notebook), "support-focus-up", 
+                       GINT_TO_POINTER (1));
 }
 
 static void
@@ -529,6 +557,35 @@ gtk_notebook_select_page (GtkNotebook *n
 }
 
 static void
+gtk_notebook_move_focus (GtkNotebook *notebook)
+{
+  GtkNotebookPage *page;
+  GtkDirectionType direction = GTK_DIR_TAB_FORWARD;
+
+  g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
+
+  if (!notebook->focus_tab)
+    return;
+
+  page = notebook->focus_tab->data;
+
+  gtk_notebook_page_select (notebook, FALSE);
+
+  switch (notebook->tab_pos)
+    {
+    case GTK_POS_LEFT:
+    case GTK_POS_TOP:
+        direction = GTK_DIR_TAB_FORWARD;
+        break;
+    case GTK_POS_RIGHT:
+    case GTK_POS_BOTTOM:
+        direction = GTK_DIR_TAB_BACKWARD;
+        break;
+    }
+  gtk_widget_child_focus (page->child, direction);
+}
+
+static void
 gtk_notebook_focus_tab (GtkNotebook       *notebook,
                         GtkNotebookTab     type)
 {
@@ -1691,6 +1748,9 @@ gtk_notebook_focus (GtkWidget        *wi
 
   gboolean widget_is_focus;
   GtkContainer *container;
+  gint support_focus_out;
+  gint support_focus_page;
+  gint support_focus_up;
 
   g_return_val_if_fail (GTK_IS_NOTEBOOK (widget), FALSE);
 
@@ -1701,6 +1761,54 @@ gtk_notebook_focus (GtkWidget        *wi
   old_focus_child = container->focus_child; 
 
   effective_direction = translate_direction[notebook->tab_pos][direction];
+
+  support_focus_out = GPOINTER_TO_INT ( gtk_object_get_data (
+                            GTK_OBJECT (notebook), "support-focus-out")); 
+  support_focus_page = GPOINTER_TO_INT ( gtk_object_get_data (
+                            GTK_OBJECT (notebook), "support-focus-page")); 
+  support_focus_up = GPOINTER_TO_INT ( gtk_object_get_data (
+                            GTK_OBJECT (notebook), "support-focus-up")); 
+  if (support_focus_out > 1) 
+    /*
+     * Request to move focus out of notebook
+     */
+    return FALSE;
+
+  if (support_focus_page > 1)
+    {
+    /*
+     * Request to select to another page
+     */
+      gboolean retval;
+      if (!notebook->show_tabs)
+        return TRUE;
+
+      switch (effective_direction)
+        {
+	case GTK_DIR_LEFT:
+	  retval =  focus_tabs_move (notebook, direction, STEP_PREV);
+          break;
+	case GTK_DIR_RIGHT:
+	  retval =  focus_tabs_move (notebook, direction, STEP_NEXT);
+          break;
+        default:
+          g_assert_not_reached();
+          return FALSE;
+        }
+      if (retval)
+        gtk_notebook_page_select (notebook, FALSE);
+      return retval;
+    }
+
+  if (support_focus_up > 1)
+    {
+    /*
+     * Request to focus on page tab
+     */
+      if (notebook->show_tabs)
+        focus_tabs_in (notebook);
+      return TRUE;
+  }
 
   if (old_focus_child)		/* Focus on page child */
     {
Index: gtknotebook.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtknotebook.h,v
retrieving revision 1.26
diff -u -p -r1.26 gtknotebook.h
--- gtknotebook.h	2001/08/23 15:26:48	1.26
+++ gtknotebook.h	2001/09/14 11:11:36
@@ -100,6 +100,8 @@ struct _GtkNotebookClass
   void (* focus_tab)         (GtkNotebook       *notebook,
                               GtkNotebookTab     type);
   
+  void (* move_focus)        (GtkNotebook       *notebook);
+  
 };
 
 /***********************************************************
Index: gtkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v
retrieving revision 1.151
diff -u -p -r1.151 gtkwindow.c
--- gtkwindow.c	2001/09/10 14:59:49	1.151
+++ gtkwindow.c	2001/09/14 11:11:37
@@ -55,6 +55,9 @@ enum {
   ACTIVATE_FOCUS,
   ACTIVATE_DEFAULT,
   MOVE_FOCUS,
+  MOVE_FOCUS_OUT,
+  MOVE_FOCUS_PAGE,
+  MOVE_FOCUS_UP,
   LAST_SIGNAL
 };
 
@@ -182,6 +185,11 @@ static void gtk_window_real_activate_def
 static void gtk_window_real_activate_focus   (GtkWindow         *window);
 static void gtk_window_move_focus            (GtkWindow         *window,
                                               GtkDirectionType   dir);
+static void gtk_window_move_focus_out        (GtkWindow         *window,
+                                              GtkDirectionType   dir);
+static void gtk_window_move_focus_page       (GtkWindow         *window,
+                                              GtkDirectionType   dir);
+static void gtk_window_move_focus_up         (GtkWindow         *window);
 static void gtk_window_read_rcfiles       (GtkWidget         *widget,
 					   GdkEventClient    *event);
 static void gtk_window_paint              (GtkWidget         *widget,
@@ -355,6 +363,9 @@ gtk_window_class_init (GtkWindowClass *k
   klass->activate_default = gtk_window_real_activate_default;
   klass->activate_focus = gtk_window_real_activate_focus;
   klass->move_focus = gtk_window_move_focus;
+  klass->move_focus_out = gtk_window_move_focus_out;
+  klass->move_focus_page = gtk_window_move_focus_page;
+  klass->move_focus_up = gtk_window_move_focus_up;
   
   /* Construct */
   g_object_class_install_property (gobject_class,
@@ -503,6 +514,38 @@ gtk_window_class_init (GtkWindowClass *k
                   1,
                   GTK_TYPE_DIRECTION_TYPE);
   
+  window_signals[MOVE_FOCUS_OUT] =
+    g_signal_new ("move_focus_out",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  GTK_SIGNAL_OFFSET (GtkWindowClass, move_focus_out),
+                  NULL, NULL,
+                  gtk_marshal_VOID__ENUM,
+                  G_TYPE_NONE,
+                  1,
+                  GTK_TYPE_DIRECTION_TYPE);
+  
+  window_signals[MOVE_FOCUS_PAGE] =
+    g_signal_new ("move_focus_page",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  GTK_SIGNAL_OFFSET (GtkWindowClass, move_focus_page),
+                  NULL, NULL,
+                  gtk_marshal_VOID__ENUM,
+                  G_TYPE_NONE,
+                  1,
+                  GTK_TYPE_DIRECTION_TYPE);
+  
+  window_signals[MOVE_FOCUS_UP] =
+    g_signal_new ("move_focus_up",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  GTK_SIGNAL_OFFSET (GtkWindowClass, move_focus_up),
+                  NULL, NULL,
+                  gtk_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+  
   if (!mnemonic_hash_table)
     mnemonic_hash_table = g_hash_table_new (mnemonic_hash,
 					    mnemonic_equal);
@@ -571,6 +614,48 @@ gtk_window_class_init (GtkWindowClass *k
   gtk_binding_entry_add_signal (binding_set, GDK_ISO_Left_Tab, GDK_SHIFT_MASK,
                                 "move_focus", 1,
                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Tab, GDK_CONTROL_MASK,
+                                "move_focus_out", 1,
+                                GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, GDK_CONTROL_MASK,
+                                "move_focus_out", 1,
+                                GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
+  gtk_binding_entry_add_signal (binding_set, GDK_ISO_Left_Tab, GDK_CONTROL_MASK,
+                                "move_focus_out", 1,
+                                GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Tab, 
+                                GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+                                "move_focus_out", 1,
+                                GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab,
+                                GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+                                "move_focus_out", 1,
+                                GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
+  gtk_binding_entry_add_signal (binding_set, GDK_ISO_Left_Tab,
+                                GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+                                "move_focus_out", 1,
+                                GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, GDK_CONTROL_MASK,
+                                "move_focus_page", 1,
+                                GTK_TYPE_DIRECTION_TYPE, GTK_DIR_LEFT);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, GDK_CONTROL_MASK,
+                                "move_focus_page", 1,
+                                GTK_TYPE_DIRECTION_TYPE, GTK_DIR_LEFT);
+  gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, GDK_CONTROL_MASK,
+                                "move_focus_page", 1,
+                                GTK_TYPE_DIRECTION_TYPE, GTK_DIR_RIGHT);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, GDK_CONTROL_MASK,
+                                "move_focus_page", 1,
+                                GTK_TYPE_DIRECTION_TYPE, GTK_DIR_RIGHT);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Up, GDK_CONTROL_MASK,
+                                "move_focus_up", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, GDK_CONTROL_MASK,
+                                "move_focus_up", 0);
+
 }
 
 static void
@@ -3415,6 +3500,118 @@ gtk_window_move_focus (GtkWindow       *
   
   if (!GTK_CONTAINER (window)->focus_child)
     gtk_window_set_focus (window, NULL);
+}
+
+static void
+gtk_window_move_focus_out (GtkWindow       *window,
+                           GtkDirectionType dir)
+{
+  /*
+   * This operation checks whether an antecedent of the focus widget
+   * supports focus_out and if so moves the focus from the antecedent.
+   *
+   * An example is moving focus from a widget with a notebook to the
+   * widget before or after the notebook.
+   */
+  if (window->focus_widget)
+  {
+    GtkWidget *widget;
+
+    widget = window->focus_widget;
+    while (widget != GTK_WIDGET (window))
+    {
+      gint val;
+
+      g_return_if_fail (widget != NULL);
+      val = GPOINTER_TO_INT (
+                gtk_object_get_data (GTK_OBJECT (widget), "support-focus-out"));
+      if (val)
+      {
+        gtk_object_set_data (GTK_OBJECT (widget), "support-focus-out", 
+                             GINT_TO_POINTER (++val));
+        gtk_widget_child_focus (GTK_WIDGET (window), dir);
+        gtk_object_set_data (GTK_OBJECT (widget), "support-focus-out",
+                             GINT_TO_POINTER (--val));
+  
+        if (!GTK_CONTAINER (window)->focus_child)
+          gtk_window_set_focus (window, NULL);
+        return;
+      }
+      widget = widget->parent;
+    }
+  }
+}
+
+static void
+gtk_window_move_focus_page (GtkWindow       *window,
+                            GtkDirectionType dir)
+{
+  if (window->focus_widget)
+  {
+    GtkWidget *widget;
+
+    widget = window->focus_widget;
+    while (widget != GTK_WIDGET (window))
+    {
+      gint val;
+
+      g_return_if_fail (widget != NULL);
+      val = GPOINTER_TO_INT (
+               gtk_object_get_data (GTK_OBJECT (widget), "support-focus-page"));
+      if (val)
+      {
+        gtk_object_set_data (GTK_OBJECT (widget), "support-focus-page", 
+                             GINT_TO_POINTER (++val));
+        gtk_widget_child_focus (GTK_WIDGET (window), dir);
+        gtk_object_set_data (GTK_OBJECT (widget), "support-focus-page",
+                             GINT_TO_POINTER (--val));
+  
+        if (!GTK_CONTAINER (window)->focus_child)
+          gtk_window_set_focus (window, NULL);
+        return;
+      }
+      widget = widget->parent;
+    }
+  }
+}
+
+static void
+gtk_window_move_focus_up (GtkWindow       *window)
+{
+  if (window->focus_widget)
+  {
+    GtkWidget *widget;
+
+    widget = window->focus_widget;
+    while (widget != GTK_WIDGET (window))
+    {
+      gint val;
+
+      /*
+       * Focus must be in a child of widget supporting focus up
+       */
+      widget = widget->parent;
+      g_return_if_fail (widget != NULL);
+      val = GPOINTER_TO_INT (
+               gtk_object_get_data (GTK_OBJECT (widget), "support-focus-up"));
+      if (val)
+      {
+        gtk_object_set_data (GTK_OBJECT (widget), "support-focus-up", 
+                             GINT_TO_POINTER (++val));
+        /* 
+         * The direction value should have no effect in this call.
+         */
+        gtk_widget_child_focus (GTK_WIDGET (window),
+                                GTK_DIR_UP);
+        gtk_object_set_data (GTK_OBJECT (widget), "support-focus-up",
+                             GINT_TO_POINTER (--val));
+  
+        if (!GTK_CONTAINER (window)->focus_child)
+          gtk_window_set_focus (window, NULL);
+        return;
+      }
+    }
+  }
 }
 
 static gint
Index: gtkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.h,v
retrieving revision 1.50
diff -u -p -r1.50 gtkwindow.h
--- gtkwindow.h	2001/09/08 06:24:46	1.50
+++ gtkwindow.h	2001/09/14 11:11:37
@@ -124,6 +124,11 @@ struct _GtkWindowClass
   void     (* activate_default)        (GtkWindow       *window);
   void     (* move_focus)              (GtkWindow       *window,
                                         GtkDirectionType direction);  
+  void     (* move_focus_out)          (GtkWindow       *window,
+                                        GtkDirectionType direction);  
+  void     (* move_focus_page)         (GtkWindow       *window,
+                                        GtkDirectionType direction);  
+  void     (* move_focus_up)           (GtkWindow       *window);
 };
 
 #define GTK_TYPE_WINDOW_GROUP             (gtk_window_group_get_type ())


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]