what a pity!



I have been trying for a few days to make entries and text widgets
behave correctly with numerical arrows keys.
By correctly I mean, being able to use them to move/select/delete/etc.
in the same way that normal arrows (those without the numbers over them,
that are usually located at the left of the numerical ones).
As you can note, when you are in a entry or a text widget pressing
normal arrows makes you move/select/etc., but pressing numerical ones
makes you move the widget focus...

I have not read any CUA document, but I think this is not the intended
behavior on a good GUI.
I thought: hey, move your bottom and take a look at gtkentry.c/gtktext.c
to change this behavior for yourself and submit a patch!
Not so easy as I thought.
To follow what I'm going to say, it's useful to use the gtk program I
enclose. Its mission is to tell you which key combination you are
pressing on the window it creates.

As you can see, pressing normal arrows (without shift, control, etc.),
gives you something like this:

You pressed me, don't you?
Time: 3615334757
State: 0
Keyval: 65362
Length: 0
String:
String from X Server: Up
You pressed me, don't you?
Time: 3615336264
State: 0
Keyval: 65364
Length: 0
String:
String from X Server: Down
You pressed me, don't you?
Time: 3615337083
State: 0
Keyval: 65361
Length: 0
String:
String from X Server: Left
You pressed me, don't you?
Time: 3615337830
State: 0
Keyval: 65363
Length: 0
String:
String from X Server: Right

Pressing normal arrows with shift at the same time (for selecting text
in entries or text widgets):
You pressed me, don't you?
Time: 3615341698
State: 1
Keyval: 65362
Length: 0
String:
String from X Server: Up

You pressed me, don't you?
Time: 3615343947
State: 1
Keyval: 65364
Length: 0
String:
String from X Server: Down

You pressed me, don't you?
Time: 3615344789
State: 1
Keyval: 65361
Length: 0
String:
String from X Server: Left

You pressed me, don't you?
Time: 3615345714
State: 1
Keyval: 65363
Length: 0
String:
String from X Server: Right

As you can see, the keyvals are the same as the normal arrows without
shift, the only change is state that now is 1.

So it's easy for entries or text widgets to check which arrow is being
pressed:
just check (using gdkkeysyms.h) if keyval equals GDK_UP,
GDK_DOWN, GDK_LEFT, GDK_RIGHT, etc.
To deduce that we are pressing shift at the same time, we only have to
check if state & GDK_SHIFT_MASK is not 0. (the same for control, etc...)

Good and neat.

Now comes the problem I tried to solve:

The GDK layer treats X key events in almost the same manner than X does.
I mean, it simply defines in gdkkeysyms.h the same symbols and keyvals
as X in keysymdef.h, only adding GDK_ before them.

With this, and using my program, if you press the numerical arrows, you
get something like:

You pressed me, don't you?
Time: 3616248179
State: 0
Keyval: 65431
Length: 0
String:
String from X Server: KP_Up
You pressed me, don't you?
Time: 3616248934
State: 0
Keyval: 65433
Length: 0
String:
String from X Server: KP_Down
You pressed me, don't you?
Time: 3616249454
State: 0
Keyval: 65430
Length: 0
String:
String from X Server: KP_Left
You pressed me, don't you?
Time: 3616250006
State: 0
Keyval: 65432
Length: 0
String:
String from X Server: KP_Right

Note, that the symbols for Up and KP_Up (Down and KP_Down, etc.) are
different, so the application can differenciate between the keys. This
is good and at the same time bad (because I wanted GDK_UP and GDK_KP_UP,
etc. be the same logical key in my program. But this is another story).

Worse is that when I press numerical arrows + shift at the same time I
get something like:

You pressed me, don't you?
Time: 3616252328
State: 1
Keyval: 65464
Length: 1
String: 8
String from X Server: KP_8
You pressed me, don't you?
Time: 3616253336
State: 1
Keyval: 65458
Length: 1
String: 2
String from X Server: KP_2
You pressed me, don't you?
Time: 3616253787
State: 1
Keyval: 65460
Length: 1
String: 4
String from X Server: KP_4
You pressed me, don't you?
Time: 3616254190
State: 1
Keyval: 65462
Length: 1
String: 6
String from X Server: KP_6

Note that now, the keyvals are not even the same that numerical arrows
without shift (a pity!), so in entries or text widgets, I have to check
something like this:

case GDK_KP_8:
case GDK_KP_UP:
case GDK_UP:

and then check the state variable.

Worse: If I press Num lock, and them the numerical arrows without shift,
I get the GDK_KP_8, etc... but in the state I get 16, that equals
GDK_MOD2_MASK (that in my system is bound to Num lock, but it could not
be that: just use xmodmap to change Num lock into GDK_MOD3_MASK). So I
have no way (at least in GDK I think) that lets me know if the user is
pressing Num lock + numerical arrows to change the behavior of the
numerical arrows.

So the code to handle all of these situations in entries or text widgets
(or even the widget to set the names of files in gmc of GNOME) is
over-complicated and duplicated.

Proposal: Knowing exactly what is being pressed by the user is sometimes
useful and for those situations the GDK layer is more or less OK.
But sometimes one only wants to know what LOGICAL KEY is being pressed
by the user.
I mean, some convenience functions could be added to the GTK layer:
Something like these functions (to filter gdk key events) in
gtkkeys.h/gtkkeys.c:

/* return TRUE if the key in eventk is a movement key,
like: GDK_UP,GDK_KP_UP,GDK_KP_8, GDK_PG_UP, etc...
checking if the key is supposed to produce a movement/selection/etc...

For example:

  GDK_UP
  GDK_UP + Shift
  GDK_UP + Control
  GDK_KP_UP
  GDK_KP_8 (that is GDK_KP_UP + Shift)
  GDK_KP_UP + Control
  GDK_KP_UP + Numlock + Shift (because Shift changes the numlock state
and the key becomes a movement key again)
  GDK_KP_UP + Numlock + Shift + Control ...

  Not included:

  GDK_KP_UP + Numlock (because that is supposed to produce the "8" above
the numerical key
  ...
*/

gboolean gtk_is_movement_key(GdkEventKey *eventk);


/* get the logical movement key.

  typedef struct _GtkLogicalKey GtkLogicalKey;

  struct _GtkLogicalKey
  {
    gint keyval;
    gint state;
  }

  Here for keyval we should have defined some values:

  GTK_UP, GTK_DOWN, etc...

  And state should be a bit mask of:

  GTK_SHIFT_MASK, GTK_CONTROL_MASK, ...

  But not NUM_LOCK_MASK, MOD1_MASK, etc... We are not interested in this

*/
void gtk_logical_key_get(GdkEventKey *eventk,GtkLogicalKey *key);



So, now in gtkentry.c or gtktext.c I can say something like:

if (gtk_is_movement_key(gdkevent))
{
  GtkLogicalKey key;

  gtk_logical_key_get(gdkevent,&key));

  switch (key->keyval)
  {
    case GTK_UP:
    case GTK_LEFT:
      if (key->state & GTK_SHIFT_MASK) /* We are selecting */
      {
      }
    ....

  }
}

That would be useful for widget like the entry, the text, the gmc one
that edits a file name... and even for not widget usage, because it
limits the complexity of having to manage GDK/X keycodes and state
convinations...
It could make the code of these widgets clearer and simpler.

What do you people think about this?
#include <gtk/gtk.h>
#include <gtk/gtkprivate.h>

gint keypress_cb(GtkWidget *widget,GdkEvent *event,gpointer data)
{
  GdkEventKey *eventk=(GdkEventKey *) event;

  g_print("You pressed me, don't you?\n");
  g_print("Time: %u\n",eventk->time);
  g_print("State: %u\n",eventk->state);
  g_print("Keyval: %u\n",eventk->keyval);
  g_print("Length: %d\n",eventk->length);
  g_print("String: %s\n",eventk->string);
  g_print("String from X Server: %s\n",XKeysymToString (eventk->keyval));

  return TRUE;
}

int main(int argc,char **argv)
{
  GtkWidget *w;

  gtk_init(&argc,&argv);

  w=gtk_window_new(GTK_WINDOW_TOPLEVEL);

  gtk_widget_set_events(w,gtk_widget_get_events(w) | GDK_KEY_PRESS_MASK);

  gtk_signal_connect(GTK_OBJECT(w),"key_press_event",GTK_SIGNAL_FUNC(keypress_cb),NULL);

  gtk_widget_show(w);

  gtk_main();

  return 0;
}


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