ref: c51962a648b3f38fc378ad0081b1d1aa037142d5
parent: ab90a480eb5233abc4d7122739c24656dd42cc94
author: Peter Mikkelsen <petermikkelsen10@gmail.com>
date: Fri Feb 16 18:26:32 EST 2024
Implement mouse menus Setup a menu/some menus by writing to an elements props/menus file
--- a/event.c
+++ b/event.c
@@ -71,6 +71,9 @@
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 @@
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 @@
b = g->buttons ^ m.buttons;
g->buttons = m.buttons;
- Event e;
if(b&16 && m.buttons&16){
e.type = Xmousescroll;
e.direction = Down;
--- 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 @@
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 @@
[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 @@
}
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:
--- a/guifs.h
+++ b/guifs.h
@@ -7,11 +7,12 @@
Pbordercolour,
Ptext,
Ptextcolour,
+ Pmenus,
Pmax,
};
enum {
- nbaseprops = 5
+ nbaseprops = 6
};
enum {
@@ -32,6 +33,7 @@
Xmouseup,
Xmouseclick,
Xmousescroll,
+ Xmenuhit,
Xkeyboard,
Xmax,
};
@@ -38,6 +40,7 @@
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 @@
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 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 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);
--- a/main.c
+++ b/main.c
@@ -160,6 +160,8 @@
g->events = chancreate(sizeof(char *), 0);
+ settype(g, Gcontainer);
+
if(parent){
g->id = parent->nchildren;
wlock(&parent->lock);
@@ -168,8 +170,6 @@
parent->children[g->id] = g;
wunlock(&parent->lock);
}
-
- settype(g, Gcontainer);
return g;
}
--- a/props.c
+++ b/props.c
@@ -109,6 +109,17 @@
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)
{
@@ -155,6 +166,36 @@
}
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)
{
char *r;
@@ -226,6 +267,74 @@
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 @@
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 @@
[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
--- a/test.rc
+++ b/test.rc
@@ -68,4 +68,9 @@
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' </mnt/gui/0/event >[2]/dev/null &
+
wait