Keyboard navigatable menus
Scott Moynes
smoynes at nerdnest.org
Sun Jul 20 20:08:02 EDT 2003
Here is my one day hack to add keyboard navigation to the menus.
Up/down selects entries, enter executes menu actions/descends into
submenus, escape backs out of submenus/closes the menu.
This patch applies to the current CVS HEAD.
Nota bene:
There is a bug in the current tree, and perhaps the alpha release that
can cause a keybinding action that opens a menu to lock up your X
server under some cases, so don't do it.
--
Scott Moynes http://www.icculus.org/openbox/
"Computer science is as much about computers
as astronomy is about telescopes." -- Dijkstra
-------------- next part --------------
Index: kernel/event.c
===================================================================
RCS file: /cvs/cvsroot/openbox/kernel/event.c,v
retrieving revision 1.124
diff -p -u -r1.124 event.c
--- kernel/event.c 2003/07/10 19:27:12 1.124
+++ kernel/event.c 2003/07/21 00:02:15
@@ -966,27 +966,37 @@ static void event_handle_menu(ObClient *
static ObMenuEntry *over = NULL;
ObMenuEntry *entry;
ObMenu *top;
- GSList *it;
+ GList *it = NULL;
- top = g_slist_nth_data(menu_visible, 0);
+ top = g_list_nth_data(menu_visible, 0);
g_message("EVENT %d", e->type);
switch (e->type) {
case KeyPress:
- if (over) {
- if (over->parent->mouseover)
- over->parent->mouseover(over, FALSE);
- else
- menu_control_mouseover(over, FALSE);
- menu_entry_render(over);
- over = NULL;
- }
+ if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
+ over = menu_control_keyboard_nav(over, OB_KEY_DOWN);
+ else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
+ over = menu_control_keyboard_nav(over, OB_KEY_UP);
+ else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
+ over = menu_control_keyboard_nav(over, OB_KEY_RETURN);
+ else if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
+ over = menu_control_keyboard_nav(over, OB_KEY_ESCAPE);
+ else {
+ if (over) {
+ if (over->parent->mouseover)
+ over->parent->mouseover(over, FALSE);
+ else
+ menu_control_mouseover(over, FALSE);
+ menu_entry_render(over);
+ over = NULL;
+ }
/*
- if (top->hide)
- top->hide(top);
- else
+ if (top->hide)
+ top->hide(top);
+ else
*/
menu_hide(top);
+ }
break;
case ButtonPress:
if (e->xbutton.button > 3) break;
@@ -998,7 +1008,7 @@ static void event_handle_menu(ObClient *
g_message("BUTTON RELEASED");
- for (it = menu_visible; it; it = g_slist_next(it)) {
+ for (it = menu_visible; it; it = g_list_next(it)) {
ObMenu *m = it->data;
if (e->xbutton.x_root >= m->location.x - ob_rr_theme->bwidth &&
e->xbutton.y_root >= m->location.y - ob_rr_theme->bwidth &&
@@ -1045,7 +1055,7 @@ static void event_handle_menu(ObClient *
break;
case MotionNotify:
g_message("motion");
- for (it = menu_visible; it; it = g_slist_next(it)) {
+ for (it = menu_visible; it; it = g_list_next(it)) {
ObMenu *m = it->data;
if ((entry = menu_find_entry_by_pos(it->data,
e->xmotion.x_root -
Index: kernel/menu.c
===================================================================
RCS file: /cvs/cvsroot/openbox/kernel/menu.c,v
retrieving revision 1.44
diff -p -u -r1.44 menu.c
--- kernel/menu.c 2003/07/19 23:58:45 1.44
+++ kernel/menu.c 2003/07/21 00:02:17
@@ -6,9 +6,10 @@
#include "screen.h"
#include "geom.h"
#include "plugin.h"
+#include "misc.h"
GHashTable *menu_hash = NULL;
-GSList *menu_visible = NULL;
+GList *menu_visible = NULL;
#define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask | \
LeaveWindowMask)
@@ -341,7 +342,7 @@ void menu_show_full(ObMenu *self, int x,
grab_pointer(TRUE, None);
grab_keyboard(TRUE);
}
- menu_visible = g_slist_append(menu_visible, self);
+ menu_visible = g_list_append(menu_visible, self);
}
if (self->show) {
@@ -364,7 +365,7 @@ void menu_hide(ObMenu *self) {
grab_keyboard(FALSE);
grab_pointer(FALSE, None);
}
- menu_visible = g_slist_remove(menu_visible, self);
+ menu_visible = g_list_remove(menu_visible, self);
}
}
@@ -423,7 +424,8 @@ void menu_entry_fire(ObMenuEntry *self)
Default menu controller action for showing.
*/
-void menu_control_show(ObMenu *self, int x, int y, ObClient *client) {
+void menu_control_show(ObMenu *self, int x, int y, ObClient *client)
+{
guint i;
Rect *a = NULL;
@@ -451,7 +453,8 @@ void menu_control_show(ObMenu *self, int
}
}
-void menu_control_mouseover(ObMenuEntry *self, gboolean enter) {
+void menu_control_mouseover(ObMenuEntry *self, gboolean enter)
+{
int x;
Rect *a;
@@ -486,4 +489,117 @@ void menu_control_mouseover(ObMenuEntry
self->parent->client);
}
}
+}
+
+ObMenuEntry *menu_control_keyboard_nav(ObMenuEntry *over, ObKey key)
+{
+ GList *it = NULL;
+
+ switch (key) {
+ case OB_KEY_DOWN: {
+ if (over != NULL) {
+ if (over->parent->mouseover)
+ over->parent->mouseover(over, FALSE);
+ else
+ menu_control_mouseover(over, FALSE);
+ menu_entry_render(over);
+
+ it = over->parent->entries;
+ while (it != NULL && it->data != over)
+ it = it->next;
+ }
+
+ if (it && it->next)
+ over = (ObMenuEntry *)it->next->data;
+ else if (over == NULL) {
+ over = (ObMenuEntry *)(menu_visible ?
+ ((ObMenu *)menu_visible->data)
+ ->entries->data :
+ NULL);
+ } else {
+ over = (over->parent->entries != NULL ?
+ over->parent->entries->data : NULL);
+ }
+
+ if (over) {
+ if (over->parent->mouseover)
+ over->parent->mouseover(over, TRUE);
+ else
+ menu_control_mouseover(over, TRUE);
+ menu_entry_render(over);
+ }
+
+ break;
+ }
+ case OB_KEY_UP: {
+ if (over != NULL) {
+ if (over->parent->mouseover)
+ over->parent->mouseover(over, FALSE);
+ else
+ menu_control_mouseover(over, FALSE);
+ menu_entry_render(over);
+
+ it = g_list_last(over->parent->entries);
+ while (it != NULL && it->data != over)
+ it = it->prev;
+ }
+
+ if (it && it->prev)
+ over = (ObMenuEntry *)it->prev->data;
+ else
+ over = (over->parent->entries != NULL ?
+ g_list_last(over->parent->entries)->data :
+ NULL);
+
+ if (over->parent->mouseover)
+ over->parent->mouseover(over, TRUE);
+ else
+ menu_control_mouseover(over, TRUE);
+ menu_entry_render(over);
+ break;
+ }
+ case OB_KEY_RETURN: {
+ if (over == NULL)
+ return over;
+
+ if (over->submenu) {
+ if (over->parent->mouseover)
+ over->parent->mouseover(over, FALSE);
+ else
+ menu_control_mouseover(over, FALSE);
+ menu_entry_render(over);
+
+ over = (over->submenu->entries ?
+ over->submenu->entries->data :
+ NULL);
+
+ if (over->parent->mouseover)
+ over->parent->mouseover(over, TRUE);
+ else
+ menu_control_mouseover(over, TRUE);
+ menu_entry_render(over);
+ }
+ else
+ menu_entry_fire(over);
+ break;
+ }
+ case OB_KEY_ESCAPE: {
+ if (over != NULL) {
+ if (over->parent->mouseover)
+ over->parent->mouseover(over, FALSE);
+ else
+ menu_control_mouseover(over, FALSE);
+ menu_entry_render(over);
+
+ menu_hide(over->parent);
+ }
+
+ over = NULL;
+ break;
+ }
+ default:
+ g_error("Unknown key");
+ }
+
+ return over;
}
Index: kernel/menu.h
===================================================================
RCS file: /cvs/cvsroot/openbox/kernel/menu.h,v
retrieving revision 1.25
diff -p -u -r1.25 menu.h
--- kernel/menu.h 2003/07/17 01:40:27 1.25
+++ kernel/menu.h 2003/07/21 00:02:17
@@ -19,7 +19,7 @@ typedef void(*menu_controller_update)(Ob
typedef void(*menu_controller_mouseover)(ObMenuEntry *self, gboolean enter);
extern GHashTable *menu_hash;
-extern GSList *menu_visible;
+extern GList *menu_visible;
struct _ObMenu
{
@@ -157,4 +157,5 @@ void menu_render_full(ObMenu *self);
//so plugins can call it?
void parse_menu_full(xmlDocPtr doc, xmlNodePtr node, void *data, gboolean new);
void menu_control_mouseover(ObMenuEntry *entry, gboolean enter);
+ObMenuEntry *menu_control_keyboard_nav(ObMenuEntry *over, ObKey key);
#endif
Index: plugins/menu/fifo_menu.c
===================================================================
RCS file: /cvs/cvsroot/openbox/plugins/menu/fifo_menu.c,v
retrieving revision 1.6
diff -p -u -r1.6 fifo_menu.c
--- plugins/menu/fifo_menu.c 2003/07/19 19:09:38 1.6
+++ plugins/menu/fifo_menu.c 2003/07/21 00:02:17
@@ -130,6 +130,11 @@ void plugin_destroy (ObMenu *m)
FIFO_MENU_DATA(m)->fifo = NULL;
}
+ if (FIFO_MENU_DATA(m)->buf != NULL) {
+ g_free(FIFO_MENU_DATA(m)->buf);
+ FIFO_MENU_DATA(m)->buf = NULL;
+ }
+
g_free(m->plugin_data);
menu_free(m->name);
More information about the openbox
mailing list