Added separators

Now lines without labels in the input generate a menu separator.
This commit is contained in:
phillbush 2020-05-15 22:51:26 -03:00
parent a80fee227a
commit 7fbd1c5ed0
3 changed files with 44 additions and 22 deletions

4
README
View File

@ -22,5 +22,7 @@ build and install dwm (if necessary as root).
xmenu receives as input a menu specification where each line is a menu xmenu receives as input a menu specification where each line is a menu
entry. Each line can be indented with tabs to represent nested menus. entry. Each line can be indented with tabs to represent nested menus.
Each line is made out of a label and a command separated by any number Each line is made out of a label and a command separated by any number
of tabs. of tabs. Lines without labels are menu separators.
See the script ./xmenu.sh for an example of how xmenu can be used to
draw a simple menu with submenus and separators.

48
xmenu.c
View File

@ -43,7 +43,8 @@ struct ScreenGeometry {
struct Item { struct Item {
char *label; char *label;
char *output; char *output;
int y; /* only y is necessary, item's x is always 0 relative to the menu*/ int y;
int h;
struct Item *next; struct Item *next;
struct Menu *submenu; struct Menu *submenu;
}; };
@ -199,11 +200,17 @@ allocitem(const char *label, const char *output)
if ((item = malloc(sizeof *item)) == NULL) if ((item = malloc(sizeof *item)) == NULL)
err(1, "malloc"); err(1, "malloc");
if ((item->label = strdup(label)) == NULL) if (*label == '\0') {
err(1, "strdup"); item->label = NULL;
if ((item->output = strdup(output)) == NULL) item->output = NULL;
err(1, "strdup"); } else {
if ((item->label = strdup(label)) == NULL)
err(1, "strdup");
if ((item->output = strdup(output)) == NULL)
err(1, "strdup");
}
item->y = 0; item->y = 0;
item->h = item->label ? geom.itemh : geom.separator;
item->next = NULL; item->next = NULL;
item->submenu = NULL; item->submenu = NULL;
@ -343,7 +350,7 @@ calcmenu(struct Menu *menu)
/* calculate items positions and menu height */ /* calculate items positions and menu height */
for (item = menu->list; item != NULL; item = item->next) { for (item = menu->list; item != NULL; item = item->next) {
item->y = menu->h; item->y = menu->h;
if (*item->label == '\0') /* height for separator item */ if (item->label == NULL) /* height for separator item */
menu->h += geom.separator; menu->h += geom.separator;
else else
menu->h += geom.itemh; menu->h += geom.itemh;
@ -403,7 +410,7 @@ getmenuitem(Window win, int y,
for (menu = currmenu; menu != NULL; menu = menu->parent) { for (menu = currmenu; menu != NULL; menu = menu->parent) {
if (menu->win == win) { if (menu->win == win) {
for (item = menu->list; item != NULL; item = item->next) { for (item = menu->list; item != NULL; item = item->next) {
if (y >= item->y && y <= item->y + geom.itemh) { if (y >= item->y && y <= item->y + item->h) {
goto done; goto done;
} }
} }
@ -443,40 +450,39 @@ drawmenu(void)
struct Item *item; struct Item *item;
for (menu = currmenu; menu != NULL; menu = menu->parent) { for (menu = currmenu; menu != NULL; menu = menu->parent) {
size_t nitems; /* number of items before current item */
nitems = 0;
for (item = menu->list; item != NULL; item = item->next) { for (item = menu->list; item != NULL; item = item->next) {
unsigned long *color; unsigned long *color;
size_t labellen; size_t labellen;
int labelx, labely; int labelx, labely;
int y;
/* determine item color */ /* determine item color */
if (item == menu->selected) if (item->label == NULL)
color = dc.decoration;
else if (item == menu->selected)
color = dc.pressed; color = dc.pressed;
else else
color = dc.unpressed; color = dc.unpressed;
/* calculate item's y position */
y = nitems * geom.itemh;
/* draw item box */ /* draw item box */
XSetForeground(dpy, dc.gc, color[ColorBG]); XSetForeground(dpy, dc.gc, color[ColorBG]);
XFillRectangle(dpy, menu->win, dc.gc, 0, y, XFillRectangle(dpy, menu->win, dc.gc, 0, item->y,
geom.itemw, geom.itemh); geom.itemw, item->h);
/* continue if item is a separator */
if (item->label == NULL)
continue;
/* draw item label */ /* draw item label */
labellen = strlen(item->label); labellen = strlen(item->label);
labelx = 0 + dc.fonth; labelx = 0 + dc.fonth;
labely = y + dc.fonth + geom.itemb; labely = item->y + dc.fonth + geom.itemb;
XSetForeground(dpy, dc.gc, color[ColorFG]); XSetForeground(dpy, dc.gc, color[ColorFG]);
XDrawString(dpy, menu->win, dc.gc, labelx, labely, item->label, labellen); XDrawString(dpy, menu->win, dc.gc, labelx, labely, item->label, labellen);
/* draw triangle, if item contains a submenu */ /* draw triangle, if item contains a submenu */
if (item->submenu != NULL) { if (item->submenu != NULL) {
int trianglex = geom.itemw - (geom.itemb + dc.fonth); int trianglex = geom.itemw - (geom.itemb + dc.fonth);
int triangley = y + geom.itemb; int triangley = item->y + geom.itemb;
XPoint triangle[] = { XPoint triangle[] = {
{trianglex, triangley}, {trianglex, triangley},
@ -488,8 +494,6 @@ drawmenu(void)
XFillPolygon(dpy, menu->win, dc.gc, triangle, LEN(triangle), XFillPolygon(dpy, menu->win, dc.gc, triangle, LEN(triangle),
Convex, CoordModeOrigin); Convex, CoordModeOrigin);
} }
nitems++;
} }
} }
} }
@ -527,6 +531,8 @@ run(void)
case ButtonRelease: case ButtonRelease:
getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item); getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
if (menu != NULL && item != NULL) { if (menu != NULL && item != NULL) {
if (item->label == NULL)
break; /* ignore separators */
if (item->submenu != NULL) { if (item->submenu != NULL) {
setcurrmenu(item->submenu); setcurrmenu(item->submenu);
} else { } else {

14
xmenu.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/sh
cat <<EOF | ./xmenu | xargs sh -c
Applications
Web Browser firefox
Image editor gimp
Terminal (xterm) xterm
Terminal (urxvt) urxvt
Terminal (st) st
Shutdown poweroff
Reboot reboot
EOF