Key navigation with reduced set of keys



Hi,

When keys are used for navigation, gtk+ uses arrow keys and 
different tab + modifier
combinations to move focus around. With normal 
keyboard this works nicely. 

When the used hardware has limited set of 
keys, problems start to pile up. If only arrow keys
are available (but 
no tab, shift, ctrl etc), everything looks good at first, but it will 
soon come 
clear that there are problems. This is because some widgets 
handle arrow keys themselves and
do not allow to use them for 
navigation. For example, GtkEntry uses left/right arrows to 
move the 
cursor horizontally and GtkTextView also up/down arrows to move cursor 
vertically. And
then there are comboboxes, spinbuttons, treeviews 
etc...

Under normal desktop use you have to use shift/ctrl modifiers 
to escape widgets of this kind,
but otherwise you are jammed. Things 
are even worse if no pointer device is available...
(see http:
//bugzilla.gnome.org/show_bug.cgi?id=318827).

For entries and 
textviews it would be logical that the focus would move to next widget 
in the desired
direction if the cursor cannot be moved further into 
desired direction. 

There is also an open bug in gnome bugzilla about 
navigating past end of buffer 
(see http://bugzilla.gnome.org/show_bug.
cgi?id=70986). Even though the wanted effect is different 
(beep wanted 
when out of buffer navigation takes place), the actual problem is the 
same.

So, I'm proposing new api addition to gtk that would allow both 
of the mentioned
problems to be solved: Widgets that have some king of 
internal navigation, could send
an "out-of-bounds" or similar signal. 
For example:

--- gtk+-2.6.10/gtk/gtkwidget.h  2005-08-18 17:10:
59.000000000 +0300
+++ gtk+-2.6.10/gtk/gtkwidget.h 2005-11-07 13:29:
54.000000000 +0200
@@ -406,8 +406,11 @@
   gboolean     
(*can_activate_accel) (GtkWidget *widget,
                                       guint      signal_id);

+  /* 
Navigation past-end-of-buffer support for various widgets */
+  
gboolean     (*out_of_bounds)      (GtkWidget *widget,
+                                      GtkDirectionType dir);
+
   /* 
Padding for future expansion */
-  void (*_gtk_reserved2) (void);
   
void (*_gtk_reserved3) (void);
   void (*_gtk_reserved4) (void);
   
void (*_gtk_reserved5) (void);
@@ -569,6 +572,10 @@
 void       
gtk_widget_get_size_request    (GtkWidget           *widget,
                                            gint                *width,
                                            gint                
*height);
+
+gboolean   gtk_widget_out_of_bounds       (GtkWidget 
*widget,
+                                           GtkDirectionType 
dir);
+
 #ifndef GTK_DISABLE_DEPRECATED
 void      
gtk_widget_set_uposition       (GtkWidget           *widget,
                                           gint                 x,

--- 
gtk+-2.6.10/gtk/gtkwidget.c 2005-08-18 17:10:59.000000000 +0300
+++ 
gtk+-2.6.10/gtk/gtkwidget.c 2005-11-07 13:57:26.000000000 +0200
@@ 
-120,6 +120,7 @@
   ACCEL_CLOSURES_CHANGED,
   SCREEN_CHANGED,
   
CAN_ACTIVATE_ACCEL,
+  OUT_OF_BOUNDS,
   LAST_SIGNAL
 };

@@ -1389,6 
+1390,15 @@
                  _gtk_marshal_BOOLEAN__UINT,
                   G_TYPE_BOOLEAN, 1, G_TYPE_UINT);

+  widget_signals
[OUT_OF_BOUNDS] =
+    g_signal_new ("out_of_bounds",
+                 
G_TYPE_FROM_CLASS (gobject_class),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GtkWidgetClass, out_of_bounds),
+                  _gtk_boolean_handled_accumulator, NULL,
+                 _gtk_marshal_BOOLEAN__ENUM,
+                  
G_TYPE_BOOLEAN, 1, GTK_TYPE_DIRECTION_TYPE);
+
   binding_set = 
gtk_binding_set_by_class (klass);
   gtk_binding_entry_add_signal 
(binding_set, GDK_F10, GDK_SHIFT_MASK,
                                 
"popup_menu", 0);
@@ -7600,5 +7610,15 @@
   g_object_notify (G_OBJECT 
(widget), "no-show-all");
 }

+gboolean
+gtk_widget_out_of_bounds
(GtkWidget *widget,
+                         GtkDirectionType dir)
+{
+  gboolean result;
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), 
FALSE);
+  g_signal_emit (widget, widget_signals[OUT_OF_BOUNDS], 0, 
dir, &result);
+  return result;
+}
+
 #define __GTK_WIDGET_C__
 
#include "gtkaliasdef.c"


This could be then be used by various 
widgets. GtkEntry, for example:

--- gtk+-2.6.10/gtk/gtkentry.c  2005-
08-18 17:10:57.000000000 +0300
+++ gtk+-2.6.10/gtk/gtkentry.c  2005-11-
07 14:14:08.000000000 +0200
@@ -2386,6 +2386,9 @@
          break;
        case GTK_MOVEMENT_VISUAL_POSITIONS:
          new_pos = 
gtk_entry_move_visually (entry, new_pos, count);
+     if (entry-
>current_pos == new_pos && !extend_selection)
+       
gtk_widget_out_of_bounds(GTK_WIDGET(entry), count > 0 ? GTK_DIR_RIGHT :
GTK_DIR_LEFT);
+
          break;
        case GTK_MOVEMENT_WORDS:
          while (count > 0)


The default handler of this signal could 
then use some GtkSetting to decide whether or not some
kind of message 
(visual/beep) is needed and if focus needs to be moved into desired 
direction.
This functionality could also be implemented as separate gtk 
module using signal emission hook.

I'm not sure if this can solve all 
cases with different widgets. For example, GtkComboBoxEntry uses
left/right to move cursor and up/down to change selected item. If we 
now would like to navigate below
the widget, we would have to keep 
pressing arrow down until we have reached the end of items...

All 
ideas, comments and other feedback is highly welcomed ;)

Markku Vire



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