add type-to-select support

This commit is contained in:
phillbush 2020-12-29 22:26:48 -03:00
parent 6bbc0e45ea
commit b33886d7a2
2 changed files with 81 additions and 17 deletions

91
xmenu.c
View File

@ -1140,20 +1140,61 @@ isclickbutton(unsigned int button)
return 0; return 0;
} }
/* append buf into text */
static int
append(char *text, char *buf, size_t textsize, size_t buflen)
{
size_t textlen;
textlen = strlen(text);
if (iscntrl(*buf))
return 0;
if (textlen + buflen > textsize - 1)
return 0;
if (buflen < 1)
return 0;
memcpy(text + textlen, buf, buflen);
text[textlen + buflen] = '\0';
return 1;
}
/* get item in menu matching text */
static struct Item *
matchitem(struct Menu *menu, char *text)
{
struct Item *item;
char *s;
size_t textlen;
textlen = strlen(text);
for (item = menu->list; item; item = item->next)
for (s = item->label; s && *s; s++)
if (strncasecmp(s, text, textlen) == 0)
return item;
return NULL;
}
/* run event loop */ /* run event loop */
static void static void
run(struct Menu *currmenu) run(struct Menu *currmenu)
{ {
char text[BUFSIZ];
char buf[32];
struct Menu *menu; struct Menu *menu;
struct Item *item; struct Item *item;
struct Item *previtem = NULL; struct Item *previtem = NULL;
struct Item *lastitem; struct Item *lastitem, *select;
KeySym ksym; KeySym ksym;
Status status;
XEvent ev; XEvent ev;
int action; int action;
int len;
text[0] = '\0';
mapmenu(currmenu); mapmenu(currmenu);
while (!XNextEvent(dpy, &ev)) { while (!XNextEvent(dpy, &ev)) {
if (XFilterEvent(&ev, None))
continue;
action = ACTION_NOP; action = ACTION_NOP;
switch(ev.type) { switch(ev.type) {
case Expose: case Expose:
@ -1166,14 +1207,14 @@ run(struct Menu *currmenu)
if (menu == NULL || item == NULL || previtem == item) if (menu == NULL || item == NULL || previtem == item)
break; break;
previtem = item; previtem = item;
menu->selected = item; select = menu->selected = item;
if (item->submenu != NULL) { if (item->submenu != NULL) {
currmenu = item->submenu; currmenu = item->submenu;
currmenu->selected = NULL; select = NULL;
} else { } else {
currmenu = menu; currmenu = menu;
} }
action = ACTION_MAP | ACTION_DRAW; action = ACTION_SELECT | ACTION_MAP | ACTION_DRAW;
break; break;
case ButtonRelease: case ButtonRelease:
if (!isclickbutton(ev.xbutton.button)) if (!isclickbutton(ev.xbutton.button))
@ -1182,7 +1223,7 @@ run(struct Menu *currmenu)
item = getitem(menu, ev.xbutton.y); item = getitem(menu, ev.xbutton.y);
if (menu == NULL || item == NULL) if (menu == NULL || item == NULL)
break; break;
selectitem: enteritem:
if (item->label == NULL) if (item->label == NULL)
break; /* ignore separators */ break; /* ignore separators */
if (item->submenu != NULL) { if (item->submenu != NULL) {
@ -1191,8 +1232,8 @@ selectitem:
printf("%s\n", item->output); printf("%s\n", item->output);
return; return;
} }
currmenu->selected = currmenu->list; select = currmenu->list;
action = ACTION_MAP | ACTION_DRAW; action = ACTION_SELECT | ACTION_MAP | ACTION_DRAW;
break; break;
case ButtonPress: case ButtonPress:
menu = getmenu(currmenu, ev.xbutton.window); menu = getmenu(currmenu, ev.xbutton.window);
@ -1200,7 +1241,16 @@ selectitem:
return; return;
break; break;
case KeyPress: case KeyPress:
ksym = XkbKeycodeToKeysym(dpy, ev.xkey.keycode, 0, 0); len = XmbLookupString(currmenu->xic, &ev.xkey, buf, sizeof buf, &ksym, &status);
switch(status) {
default: /* XLookupNone, XBufferOverflow */
continue;
case XLookupChars:
goto append;
case XLookupKeySym: /* FALLTHROUGH */
case XLookupBoth:
break;
}
/* esc closes xmenu when current menu is the root menu */ /* esc closes xmenu when current menu is the root menu */
if (ksym == XK_Escape && currmenu->parent == NULL) if (ksym == XK_Escape && currmenu->parent == NULL)
@ -1230,21 +1280,30 @@ selectitem:
} else if ((ksym == XK_Return || ksym == XK_Right || ksym == KSYMRIGHT) && } else if ((ksym == XK_Return || ksym == XK_Right || ksym == KSYMRIGHT) &&
currmenu->selected != NULL) { currmenu->selected != NULL) {
item = currmenu->selected; item = currmenu->selected;
goto selectitem; goto enteritem;
} else if ((ksym == XK_Escape || ksym == XK_Left || ksym == KSYMLEFT) && } else if ((ksym == XK_Escape || ksym == XK_Left || ksym == KSYMLEFT) &&
currmenu->parent != NULL) { currmenu->parent != NULL) {
item = currmenu->parent->selected; item = currmenu->parent->selected;
currmenu = currmenu->parent; currmenu = currmenu->parent;
action = ACTION_MAP; action = ACTION_MAP;
} else } else {
append:
if (append(text, buf, sizeof text, len)) {
currmenu->selected = matchitem(currmenu, text);
action = ACTION_DRAW;
} else {
select = NULL;
action = ACTION_SELECT | ACTION_DRAW;
}
break; break;
currmenu->selected = item; }
action |= ACTION_DRAW; select = item;
action |= ACTION_SELECT | ACTION_DRAW;
break; break;
case LeaveNotify: case LeaveNotify:
previtem = NULL; previtem = NULL;
currmenu->selected = NULL; select = NULL;
action = ACTION_DRAW; action = ACTION_SELECT | ACTION_DRAW;
break; break;
case ConfigureNotify: case ConfigureNotify:
menu = getmenu(currmenu, ev.xconfigure.window); menu = getmenu(currmenu, ev.xconfigure.window);
@ -1264,6 +1323,10 @@ selectitem:
action = ACTION_MAP; action = ACTION_MAP;
break; break;
} }
if (action & ACTION_SELECT) {
currmenu->selected = select;
text[0] = '\0';
}
if (action & ACTION_MAP) if (action & ACTION_MAP)
mapmenu(currmenu); mapmenu(currmenu);
if (action & ACTION_DRAW) if (action & ACTION_DRAW)

View File

@ -2,8 +2,9 @@
/* Actions for the main loop */ /* Actions for the main loop */
#define ACTION_NOP 0 #define ACTION_NOP 0
#define ACTION_MAP 1<<0 #define ACTION_SELECT 1<<0 /* select item and clear text */
#define ACTION_DRAW 1<<1 #define ACTION_MAP 1<<1 /* remap menu windows */
#define ACTION_DRAW 1<<2 /* redraw menu windows */
/* enum for keyboard menu navigation */ /* enum for keyboard menu navigation */
enum { ITEMPREV, ITEMNEXT, ITEMFIRST, ITEMLAST }; enum { ITEMPREV, ITEMNEXT, ITEMFIRST, ITEMLAST };