~pmikkelsen/guifs

c51962a648b3f38fc378ad0081b1d1aa037142d5 — Peter Mikkelsen 9 months ago ab90a48
Implement mouse menus

Setup a menu/some menus by writing to an elements props/menus file
6 files changed, 182 insertions(+), 18 deletions(-)

M event.c
M graphics.c
M guifs.h
M main.c
M props.c
M test.rc
M event.c => event.c +36 -4
@@ 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;

M graphics.c => graphics.c +9 -9
@@ 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:

M guifs.h => guifs.h +18 -1
@@ 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);

M main.c => main.c +2 -2
@@ 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;
}


M props.c => props.c +112 -2
@@ 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)
{


@@ 155,6 166,36 @@ printtext(PropVal p)
}

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 @@ 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

M test.rc => test.rc +5 -0
@@ 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' </mnt/gui/0/event >[2]/dev/null &

wait