From c51962a648b3f38fc378ad0081b1d1aa037142d5 Mon Sep 17 00:00:00 2001 From: Peter Mikkelsen Date: Fri, 16 Feb 2024 23:26:32 +0000 Subject: Implement mouse menus Setup a menu/some menus by writing to an elements props/menus file --- event.c | 40 +++++++++++++++++++--- graphics.c | 18 +++++----- guifs.h | 19 ++++++++++- main.c | 4 +-- props.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- test.rc | 5 +++ 6 files changed, 182 insertions(+), 18 deletions(-) diff --git a/event.c b/event.c index 878d41a..4182d1c 100644 --- a/event.c +++ b/event.c @@ -71,6 +71,9 @@ sendevent(GuiElement *g, Event event) case Xmousescroll: s->event = smprint("mousescroll %s\n", e->direction == Up ? "up" : "down"); break; + case Xmenuhit: + s->event = smprint("menuhit %c %d %s\n", e->hit.button, e->hit.which, e->hit.text); + break; case Xkeyboard: s->event = smprint("key %C\n", e->r); break; @@ -93,19 +96,49 @@ mouseevent(Mouse m) static GuiElement *lastM = nil; static GuiElement *lastR = nil; + int down = 0; + int b = lastbuttons ^ m.buttons; lastbuttons = m.buttons; - if(b&4 && m.buttons&4) + if(b&4 && m.buttons&4){ lastR = g; - if(b&2 && m.buttons&2) + down = 3; + } + if(b&2 && m.buttons&2){ lastM = g; - if(b&1 && m.buttons&1) + down = 2; + } + if(b&1 && m.buttons&1){ lastL = g; + down = 1; + } if(!g) return 0; wlock(&g->lock); + Event e; + MenuSpec *ms = getprop(g, Pmenus, 0).menus; + if(down >= 1 && down <= 3 && ms->menus[down-1] != nil){ + int which = menuhit(down, mousectl, ms->menus[down-1], nil); + e.type = Xmenuhit; + e.hit.button = (down == 1) ? 'L' : (down == 2) ? 'M' : 'R'; + e.hit.which = which; + e.hit.text = ms->menus[down-1]->item[which]; + if(g->listening && which != -1) + sendevent(g, e); + wunlock(&g->lock); + + switch(down){ + case 1: lastL = nil; break; + case 2: lastM = nil; break; + case 3: lastR = nil; break; + } + lastbuttons = lastbuttons ^ (1<<(down-1)); + + return 1; + } + if(!g->listening){ wunlock(&g->lock); return 0; @@ -114,7 +147,6 @@ mouseevent(Mouse m) b = g->buttons ^ m.buttons; g->buttons = m.buttons; - Event e; if(b&16 && m.buttons&16){ e.type = Xmousescroll; e.direction = Down; diff --git a/graphics.c b/graphics.c index 64641ba..9f14058 100644 --- a/graphics.c +++ b/graphics.c @@ -8,8 +8,8 @@ #include "guifs.h" Point mousexy; -Mousectl *mouse; -Keyboardctl *keyboard; +Mousectl *mousectl; +Keyboardctl *keyboardctl; Channel *updatechan; Channel *mkcolourchan; Channel *newcolourchan; @@ -122,9 +122,9 @@ guiproc(void *) if(initdraw(nil, nil, "guifs") < 0) sysfatal("initdraw failed"); - if((mouse = initmouse(nil, screen)) == nil) + if((mousectl = initmouse(nil, screen)) == nil) sysfatal("initmouse failed"); - if((keyboard = initkeyboard(nil)) == nil) + if((keyboardctl = initkeyboard(nil)) == nil) sysfatal("initkeyboard failed"); enum { @@ -139,13 +139,13 @@ guiproc(void *) [Aupdategui] = {updatechan, &i, CHANRCV}, [Aresize] = - {mouse->resizec, nil, CHANRCV}, + {mousectl->resizec, nil, CHANRCV}, [Amkcolour] = {mkcolourchan, &c, CHANRCV}, [Amouse] = - {mouse->c, &mouse->Mouse, CHANRCV}, + {mousectl->c, &mousectl->Mouse, CHANRCV}, [Akeyboard] = - {keyboard->c, &r, CHANRCV}, + {keyboardctl->c, &r, CHANRCV}, [Aaltend] = {nil, nil, CHANEND}, }; @@ -169,10 +169,10 @@ guiproc(void *) } break; case Amouse: - mousexy = mouse->Mouse.xy; + mousexy = mousectl->Mouse.xy; if(!root) break; - if(mouseevent(mouse->Mouse)) + if(mouseevent(mousectl->Mouse)) resized(0); break; case Akeyboard: diff --git a/guifs.h b/guifs.h index 1121c3c..d66c58d 100644 --- a/guifs.h +++ b/guifs.h @@ -7,11 +7,12 @@ enum { Pbordercolour, Ptext, Ptextcolour, + Pmenus, Pmax, }; enum { - nbaseprops = 5 + nbaseprops = 6 }; enum { @@ -32,12 +33,14 @@ enum { Xmouseup, Xmouseclick, Xmousescroll, + Xmenuhit, Xkeyboard, Xmax, }; typedef struct Colour Colour; typedef struct Spacing Spacing; +typedef struct MenuSpec MenuSpec; typedef union PropVal PropVal; typedef struct PropSpec PropSpec; typedef struct Prop Prop; @@ -57,9 +60,15 @@ struct Spacing { int left; }; +struct MenuSpec { + char seps[3]; + Menu *menus[3]; +}; + union PropVal { Colour *colour; Spacing *spacing; + MenuSpec *menus; int orientation; Rune *text; }; @@ -80,9 +89,15 @@ struct Prop { struct Event { int type; union { + int i; Mouse m; Rune r; int direction; + struct { + char button; + int which; + char *text; + } hit; }; }; @@ -130,8 +145,10 @@ extern GuiElement *root; extern PropSpec propspecs[Pmax]; extern GuiSpec guispecs[Gmax]; extern int baseprops[nbaseprops]; +extern Mousectl *mousectl; void *emalloc(ulong); +void *erealloc(void *, ulong); int allspace(char *); Colour *mkcolour(ulong); diff --git a/main.c b/main.c index dda6af1..aba1b70 100644 --- a/main.c +++ b/main.c @@ -160,6 +160,8 @@ newgui(GuiElement *parent) g->events = chancreate(sizeof(char *), 0); + settype(g, Gcontainer); + if(parent){ g->id = parent->nchildren; wlock(&parent->lock); @@ -169,8 +171,6 @@ newgui(GuiElement *parent) wunlock(&parent->lock); } - settype(g, Gcontainer); - return g; } diff --git a/props.c b/props.c index fecf9ef..df61782 100644 --- a/props.c +++ b/props.c @@ -109,6 +109,17 @@ deftext(int gtag, int ptag) return v; } +PropVal +defmenus(int gtag, int ptag) +{ + USED(gtag); + USED(ptag); + + PropVal v; + v.menus = emalloc(sizeof(MenuSpec)); + return v; +} + char * printcolour(PropVal p) { @@ -154,6 +165,36 @@ printtext(PropVal p) return str; } +char * +printmenus(PropVal p) +{ + MenuSpec *spec = p.menus; + + char which[3] = "LMR"; + char *str = smprint(""); + for(int i = 0; i < 3; i++){ + if(spec->menus[i] == nil) + continue; + + char sep = spec->seps[i]; + char *tmp = str; + str = smprint("%s%c", tmp, which[i]); + free(tmp); + + char **items = spec->menus[i]->item; + for(int j = 0; items[j] != nil; j++){ + tmp = str; + str = smprint("%s%c%s", tmp, sep, items[j]); + free(tmp); + } + + tmp = str; + str = smprint("%s\n", tmp); + free(tmp); + } + return str; +} + char * parsecolour(char *str, PropVal *p) { @@ -226,6 +267,74 @@ parsetext(char *str, PropVal *p) return nil; } +char * +parsemenus(char *str, PropVal *p) +{ + char *err = nil; + int n = 0; + for(int i = 0; str[i] != 0; i++) + if(str[i] == '\n') + n++; + + char **lines = emalloc(sizeof(char *) * (n+1)); + n = getfields(str, lines, n, 1, "\n"); + + p->menus = emalloc(sizeof(MenuSpec)); + MenuSpec *spec = p->menus; + for(int i = 0; i < n; i++){ + char *line = lines[i]; + if(strlen(line) == 0) + continue; + + if(strlen(line) <= 2) + return Eparse; + + int which; + switch(line[0]){ + case 'L': which = 0; break; + case 'M': which = 1; break; + case 'R': which = 2; break; + default: + err = Eparse; + goto Lend; + } + + if(spec->menus[which] == nil) + spec->menus[which] = emalloc(sizeof(Menu)); + + int count = 0; + char sep = line[1]; + spec->seps[which] = sep; + spec->menus[which]->item = emalloc(sizeof(char *)); + + char *start = line+2; + char *end; + while(*start != 0){ + count++; + spec->menus[which]->item = erealloc(spec->menus[which]->item, sizeof(char *) * count); + for(end = start; *end != 0 && *end != sep && *end != '\n'; end++); + int len = end-start; + char *buf = emalloc(len+1); + memcpy(buf, start, len); + buf[len] = 0; + + spec->menus[which]->item[count-1] = buf; + start = end; + if(*start != 0){ + do{ + start++; + }while(*start == '\n'); + } + } + spec->menus[which]->item[count] = nil; + print("count: %d\n", count); + } + +Lend: + free(lines); + return err; +} + PropVal getprop(GuiElement *g, int tag, int lock) { @@ -239,7 +348,7 @@ getprop(GuiElement *g, int tag, int lock) runlock(&g->lock); if(v == nil) - sysfatal("invalid prop for this gui element"); + sysfatal("invalid prop %d for this gui element", tag); else return *v; } @@ -268,6 +377,7 @@ PropSpec propspecs[Pmax] = { [Pbordercolour] = {"bordercolour", defcolour, printcolour, parsecolour}, [Ptext] = {"text", deftext, printtext, parsetext}, [Ptextcolour] = {"textcolour", defcolour, printcolour, parsecolour}, + [Pmenus] = {"menus", defmenus, printmenus, parsemenus}, }; -int baseprops[nbaseprops] = {Pbackground, Pborder, Pmargin, Ppadding, Pbordercolour}; \ No newline at end of file +int baseprops[nbaseprops] = {Pbackground, Pborder, Pmargin, Ppadding, Pbordercolour, Pmenus}; \ No newline at end of file diff --git a/test.rc b/test.rc index 46710f8..9c04f75 100755 --- a/test.rc +++ b/test.rc @@ -68,4 +68,9 @@ for(f in `{walk /mnt/gui | grep $dir'/[0-9]+/event'}){ printevents $f <$f >[2]/dev/null & } +# Create a right-click menu on the text field +echo 'R/this is a/right click/menu' >> /mnt/gui/0/props/menus +# Also attach an event printer to the text field +printevents 'text field' [2]/dev/null & + wait -- cgit v1.2.3