diff options
| author | Peter Mikkelsen <petermikkelsen10@gmail.com> | 2024-02-17 16:42:00 +0000 | 
|---|---|---|
| committer | Peter Mikkelsen <petermikkelsen10@gmail.com> | 2024-02-17 16:42:00 +0000 | 
| commit | d9a15e56d42e45f021c01d527df67863100840a7 (patch) | |
| tree | 7e01ec814a1eae74db53522a2de40f1f81f3f43f | |
| parent | c51962a648b3f38fc378ad0081b1d1aa037142d5 (diff) | |
| -rw-r--r-- | event.c | 23 | ||||
| -rw-r--r-- | graphics.c | 98 | ||||
| -rw-r--r-- | guifs.h | 8 | ||||
| -rw-r--r-- | guispec.c | 2 | ||||
| -rw-r--r-- | main.c | 20 | ||||
| -rw-r--r-- | props.c | 77 | ||||
| -rwxr-xr-x | test.rc | 38 | 
7 files changed, 230 insertions, 36 deletions
@@ -117,6 +117,28 @@ mouseevent(Mouse m)  		return 0;  	wlock(&g->lock); + +	/* Handle scrolling first */ +	if(g->type == Gtextbox && m.buttons&24){ /* TODO: in the future it may apply to other things? */ +		PropVal p = getprop(g, Pscroll, 0); +		int lines = ((m.xy.y - g->content.min.y) / font->height); +		if(lines < 1) +			lines = 1; + +		if(m.buttons&8) +			p.scroll -= lines; +		else if(m.buttons&16) +			p.scroll += lines; + +		if(p.scroll < 0) +			p.scroll = 0; +		 +		setprop(g, Pscroll, p, 0); +		wunlock(&g->lock); +		return 1; +	} + +	/* Then handle menus */  	Event e;  	MenuSpec *ms = getprop(g, Pmenus, 0).menus;  	if(down >= 1 && down <= 3 && ms->menus[down-1] != nil){ @@ -139,6 +161,7 @@ mouseevent(Mouse m)  		return 1;  	} +	/* Now handle general events */  	if(!g->listening){  		wunlock(&g->lock);  		return 0; @@ -74,13 +74,105 @@ drawcontainer(GuiElement *g)  }  void +drawtext(Rectangle rect, Rune *text, Image *fg, int firstline, int *nlinesp, int *endlinep) +{ +	Point p = rect.min; +	int w; + +	int nlines = 1; +	int newlines = 0; +	int endline = 0; +	int ended = 0; +	int xoffset = 0; + +	for(Rune *r = text; *r; r++){ +		switch(*r){ +		case '\n': +			newlines++; +Lnewline:		if(ended && endline == 0) +				endline = nlines; +			p.x = rect.min.x; +			xoffset = 0; +			if((newlines)-(*r=='\n') >= firstline) +				p.y += font->height; +			nlines++; +			break; +		case '\t': +			w = 8-(xoffset%8); +			xoffset += w; + +			w *= stringnwidth(font, " ", 1); +			xoffset += w; +			if(p.x+w > rect.max.x){ +				r--; +				goto Lnewline; +			}else +				p.x += w; +			break; +		default: +			xoffset++; +			w = runestringnwidth(font, r, 1); +			if(p.x+w > rect.max.x){ +				r--; +				goto Lnewline; +			} +			if(!ended && (p.y+font->height) < rect.max.y){ +				if(newlines >= firstline) +					runestringn(screen, p, fg, ZP, font, r, 1); +			}else +				ended = 1; +			p.x += w; +			break; +		} +	} + +	if(endline == 0) +		endline = nlines; + +	if(nlinesp) +		*nlinesp = nlines; +	if(endlinep) +		*endlinep = endline; +} + +void  drawtextbox(GuiElement *g)  { -	Rune *text = getprop(g, Ptext, 1).text; +	Rune *text = getprop(g, Ptext, 0).text;  	Image *fg = getprop(g, Ptextcolour, 1).colour->image; -	 +	Image *scrollbg = getprop(g, Pbordercolour, 1).colour->image; +	Image *scrollfg = getprop(g, Pbackground, 1).colour->image; +	int firstline = getprop(g, Pscroll, 1).scroll; + +	int lines = 0; +	for(Rune *r = text; *r; r++) +		lines += (*r) == '\n'; + +	/* draw the scroll bar background here, then draw the actual bar later */ +	Rectangle scrollbar = Rect(g->rect.min.x, g->rect.min.y, g->rect.min.x+ScrollbarWidth, g->rect.max.y); +	Rectangle textrect = g->content; +	textrect.min.x += ScrollbarWidth; + +	draw(screen, scrollbar, scrollbg, nil, ZP); -	runestring(screen, g->content.min, fg, ZP, font, text); +	int nlines, endline; +	drawtext(textrect, text, fg, firstline, &nlines, &endline); + +	int height = Dy(scrollbar); +	scrollbar.max.x -= 1; +	scrollbar.min.y += (height * (firstline)) / nlines; +	scrollbar.max.y -= (height * (nlines-endline)) / nlines; + +	if(scrollbar.min.y > (g->rect.max.y - 2)) +		scrollbar.min.y = g->rect.max.y - 2; + +	draw(screen, scrollbar, scrollfg, nil, ZP); + +	if(firstline > nlines){ +		PropVal p; +		p.scroll = nlines - 1; +		setprop(g, Pscroll, p, 0); /* TODO: we don't have the write lock here :) */ +	}  }  Colour * @@ -8,6 +8,7 @@ enum {  	Ptext,  	Ptextcolour,  	Pmenus, +	Pscroll,  	Pmax,  }; @@ -38,6 +39,10 @@ enum {  	Xmax,  }; +enum { +	ScrollbarWidth = 12, +}; +  typedef struct Colour Colour;  typedef struct Spacing Spacing;  typedef struct MenuSpec MenuSpec; @@ -70,6 +75,7 @@ union PropVal {  	Spacing *spacing;  	MenuSpec *menus;  	int orientation; +	int scroll;  	Rune *text;  }; @@ -77,7 +83,7 @@ struct PropSpec {  	char *name;  	PropVal (*def)(int, int);  	char *(*print)(PropVal); -	char *(*parse)(char *, PropVal *); +	char *(*parse)(char *, int, PropVal *);  };  struct Prop { @@ -7,7 +7,7 @@  #include "guifs.h"  int containerprops[] = {Porientation}; -int textboxprops[] = {Ptext, Ptextcolour}; +int textboxprops[] = {Ptext, Ptextcolour, Pscroll};  GuiSpec guispecs[Gmax] = {  	[Gcontainer] = { "container",	drawcontainer,	layoutcontainer,	0,	nelem(containerprops),	containerprops}, @@ -322,6 +322,10 @@ Lend:  void  fsstat(Req *r)  { +	GuiElement *g = r->fid->aux; +	PropVal p; +	char *buf; +  	r->d.qid = r->fid->qid;  	r->d.uid = estrdup9p(username);  	r->d.gid = estrdup9p(username); @@ -335,6 +339,12 @@ fsstat(Req *r)  		r->d.name = estrdup9p("/");  		r->d.mode = 0555|DMDIR;  		break; +	case Qprop: +		p = getprop(g, QID_PROP(r->fid->qid), 1); +		buf = propspecs[QID_PROP(r->fid->qid)].print(p); +		r->d.length = strlen(buf); +		free(buf); +		break;  	}  	respond(r, nil); @@ -417,9 +427,14 @@ proptreegen(int n, Dir *d, void *aux)  	if(!done){  		PropSpec spec = propspecs[g->props[n].tag]; +		PropVal p = getprop(g, g->props[n].tag, 0); +		char *buf = spec.print(p); +  		d->mode = 0666;  		d->name = estrdup9p(spec.name);  		d->qid = g->props[n].qid; +		d->length = strlen(buf); +		free(buf);  	}  	runlock(&g->lock); @@ -459,6 +474,7 @@ Lgotevent:			currentsize = g->currentevents ? strlen(g->currentevents) : 0;  				eventsize = strlen(event);  				wlock(&g->lock); +				g->qevent.vers++;  				g->currentevents = erealloc(g->currentevents, currentsize+eventsize+1);  				memcpy(g->currentevents+currentsize, event, eventsize);  				g->currentevents[currentsize+eventsize] = 0; @@ -544,12 +560,12 @@ fswrite(Req *r)  		{  			int tag = QID_PROP(r->fid->qid);  			PropSpec spec = propspecs[tag]; -			PropVal val; +			PropVal val = getprop(g, tag, 1);  			char *buf = emalloc(r->ifcall.count + 1);  			buf[r->ifcall.count] = 0;  			memcpy(buf, r->ifcall.data, r->ifcall.count); -			err = spec.parse(buf, &val); +			err = spec.parse(buf, r->ifcall.offset, &val);  			if(err == nil)  				setprop(g, tag, val, 1);  			free(buf); @@ -10,6 +10,7 @@  #include "guifs.h"  #define Eparse "could not parse property" +#define Eoffset "cannot parse this property when writing at a non-zero offset"  int  allspace(char *r) @@ -120,6 +121,17 @@ defmenus(int gtag, int ptag)  	return v;  } +PropVal +defscroll(int gtag, int ptag) +{ +	USED(gtag); +	USED(ptag); + +	PropVal v; +	v.scroll = 0; +	return v; +} +  char *  printcolour(PropVal p)  { @@ -196,20 +208,31 @@ printmenus(PropVal p)  }  char * -parsecolour(char *str, PropVal *p) +printscroll(PropVal p)  { +	return smprint("%d\n", p.scroll); +} + +char * +parsecolour(char *str, int offset, PropVal *p) +{ +	if(offset != 0) +		return Eoffset; +  	char *r;  	ulong c = strtoul(str, &r, 16);  	if((r - str) != 8 || !allspace(r))  		return Eparse; -	(*p).colour = mkcolour(c); +	p->colour = mkcolour(c);  	return nil;  }  char * -parsespacing(char *str, PropVal *p) +parsespacing(char *str, int offset, PropVal *p)  { -	USED(p); +	if(offset != 0) +		return Eoffset; +  	char *fields[5];  	int spacings[4]; @@ -240,36 +263,50 @@ parsespacing(char *str, PropVal *p)  		s->left = spacings[3];  		break;  	} -	(*p).spacing = s; +	p->spacing = s;  	return nil;  }  char * -parseorientation(char *str, PropVal *p) +parseorientation(char *str, int offset, PropVal *p)  { +	if(offset != 0) +		return Eoffset; +  	if(strncmp(str, "horizontal", 10) == 0 && allspace(str+10)) -		(*p).orientation = Horizontal; +		p->orientation = Horizontal;  	else if(strncmp(str, "vertical", 8) == 0 && allspace(str+8)) -		(*p).orientation = Vertical; +		p->orientation = Vertical;  	else  		return Eparse;  	return nil;  }  char * -parsetext(char *str, PropVal *p) +parsetext(char *str, int offset, PropVal *p)  { -	Rune *rstr = runesmprint("%s", str); +	char *old = smprint("%S", p->text); +	long oldlen = strlen(old); + +	if(offset > oldlen) +		return Eparse; + +	old[offset] = 0; +	Rune *rstr = runesmprint("%s%s", old, str); +	free(old);  	if(rstr == nil)  		sysfatal("runesmprint failed"); -	(*p).text = rstr; +	p->text = rstr;  	return nil;  }  char * -parsemenus(char *str, PropVal *p) +parsemenus(char *str, int offset, PropVal *p)  { +	if(offset != 0) +		return Eoffset; +  	char *err = nil;  	int n = 0;  	for(int i = 0; str[i] != 0; i++) @@ -327,7 +364,6 @@ parsemenus(char *str, PropVal *p)  			}  		}  		spec->menus[which]->item[count] = nil; -		print("count: %d\n", count);  	}  Lend: @@ -335,6 +371,20 @@ Lend:  	return err;  } +char * +parsescroll(char *str, int offset, PropVal *p) +{ +	if(offset != 0) +		return Eoffset; + +	char *r; +	ulong s = strtoul(str, &r, 10); +	if(!allspace(r)) +		return Eparse; +	p->scroll = (int)s; +	return nil; +} +  PropVal  getprop(GuiElement *g, int tag, int lock)  { @@ -378,6 +428,7 @@ PropSpec propspecs[Pmax] = {  	[Ptext] = {"text",	deftext,	printtext,	parsetext},  	[Ptextcolour] = {"textcolour",	defcolour,	printcolour,	parsecolour},  	[Pmenus] = {"menus",	defmenus,	printmenus,	parsemenus}, +	[Pscroll] = {"scroll",	defscroll,	printscroll,	parsescroll},  };  int baseprops[nbaseprops] = {Pbackground, Pborder, Pmargin, Ppadding, Pbordercolour, Pmenus};
\ No newline at end of file @@ -5,12 +5,12 @@ delay=0.1  dir=/mnt/gui  # split the window vertically into two, and make the top one a textbox -echo vertical >> $dir/props/orientation +echo vertical > $dir/props/orientation  textdir=$dir/`{cat $dir/clone} -echo textbox >> $textdir/type +echo textbox > $textdir/type  fn show { -	echo -n $* >> $textdir/props/text +	echo $* >> $textdir/props/text  }  dir=$dir/`{cat $dir/clone} @@ -18,7 +18,7 @@ dir=$dir/`{cat $dir/clone}  colours=(FF0000 00FF00 0000FF FFFF00 00FFFF FF00FF FFFFFF 333333)  for(colour in $colours){  	dir=$dir/`{cat $dir/clone}			# Create a new sub element in the gui (for now, always a "container") -	echo $colour^FF >> $dir/props/background	# Set the background +	echo $colour^FF > $dir/props/background	# Set the background  	show 'setting background of '^$dir^' to 0x'^$colour  	sleep $delay					# Wait a bit  } @@ -26,39 +26,41 @@ for(colour in $colours){  # Now do the same, but don't nest the elements  for(colour in $colours){  	subdir=$dir/`{cat $dir/clone} -	echo $colour^FF >> $subdir/props/background +	echo $colour^FF > $subdir/props/background  	show 'setting background of '^$subdir^' to 0x'^$colour  	sleep $delay  }  # Add some padding to all elements  for(f in `{walk /mnt/gui/ | grep 'padding$'}){ -	echo 10 >> $f -	show 'echo 10 >> '^$f +	echo 10 > $f +	show 'echo 10 > '^$f  	sleep $delay  }  # Add a border to the innermost elements  for(f in `{walk /mnt/gui | grep $dir'/[0-9]+/props/border$'}){ -	echo 8888CCFF >> $f^colour -	echo 5 >> $f -	show 'echo 5 >> '^$f +	echo 8888CCFF > $f^colour +	echo 5 > $f +	show 'echo 5 > '^$f  	sleep $delay  }  # Add some margin to the innermost elements  for(f in `{walk /mnt/gui | grep $dir'/[0-9]+/props/margin'}){ -	echo 5 >> $f -	show 'echo 5 >> '^$f +	echo 5 > $f +	show 'echo 5 > '^$f  	sleep $delay  }  # Make the inner container vertical -echo vertical >> $dir/props/orientation -show 'echo vertical >> '^$dir/props/orientation +echo vertical > $dir/props/orientation +show 'echo vertical > '^$dir/props/orientation +nl=' +'  fn printevents { -	while(event = `''{read}){ +	while(event = `''{read | tr -d $nl}){  		show $1': '$event  	}  } @@ -70,7 +72,11 @@ for(f in `{walk /mnt/gui | grep $dir'/[0-9]+/event'}){  # 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 & +# printevents 'text field' </mnt/gui/0/event >[2]/dev/null & + +# Reset the textfield contents +echo -n > /mnt/gui/0/props/text  wait  |