shithub: guifs

Download patch

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