shithub: mez

ref: aca0f958f91ee7adaaae77eb85c8ca9b8467da87
dir: /guitest.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <cursor.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include "mez.h"

Mousectl *mc;
Keyboardctl *kc;
Image *cols[NCOL], *cmdcols[NCOL];
char buf[8192];
Rune lorem[8192];

enum
{
	MOUSE,
	KEYBD,
	RESIZE,
	NCHAN,

	Scrollwid = 12,		/* width of scroll bar */
	Scrollgap = 4,		/* gap right of scroll bar */
	Margin = 4,	/* margin around text */
};

void
error(char *s)
{
	fprint(2, "%s: error: %s: %r\n", argv0, s);
	threadexits(s);
}

void*
emallocz(ulong sz, int clr)
{
	void *p;

	if((p = mallocz(sz, clr)) == nil)
		error("Out of memory");
	setmalloctag(p, getcallerpc(&sz));
	return p;
}

uint
lines(Rune *text)
{
	Rune *e;
	uint l;

	e = text;
	for(l = 0; e != nil; l++)
		e = runestrchr(e+1, L'\n');
	return l;
}

#define max(a, b) ((a)>(b)?(a):(b))

Rune*
offset(Rune *origin, int line)
{
	int i;
	Rune *r;

	r = origin;
	for(i = 0; i < line; i++){
		origin = runestrchr(r, L'\n');
		if(origin == nil)
			break;
		r = origin+1;
	}
	return r;
}

Rectangle scrollr;

void
drawtext(Chan *chan, int resize)
{
	Rectangle scrollcur, bodyr;

	scrollr = screen->r;
	scrollr.max.x = screen->r.min.x + Scrollwid + 1;

	bodyr.min.x = screen->r.min.x + Scrollwid + Scrollgap + Margin;
	bodyr.min.y = screen->r.min.y;
	bodyr.max = screen->r.max;

	if(resize){
		frclear(chan, 0);
		frinit(chan, bodyr, display->defaultfont, screen, cols);
	}

	draw(screen, screen->r, cols[BACK], nil, ZP);
	frinsert(chan, offset(chan->body, chan->curline), chan->body + chan->bodylen, 0);
	draw(screen, scrollr, cols[BORD], nil, ZP);

	scrollcur = scrollr;
	scrollcur.min.y = screen->r.min.y + max(0, Dy(screen->r)*chan->curline/chan->totlines);
	scrollcur.max.y = scrollcur.min.y+Dy(screen->r)*chan->maxlines/chan->totlines;
	scrollcur = insetrect(scrollcur, 1);
	draw(screen, scrollcur, cols[BACK], nil, ZP);

	flushimage(display, 1);
}

void
setcurline(int y, Chan *chan)
{
	chan->curline = (y-scrollr.min.y)*chan->totlines/Dy(scrollr);
}

void
threadmain(int argc, char **argv)
{
	Rune r, *rs;
	Mouse m;
	Chan *chan;
	char *s;
	int len;

	len = readn(0, buf, sizeof(buf));
	buf[len] = '\0';
	for(s = buf, rs = lorem; *s != '\0'; s+=len, rs++)
		len = chartorune(rs, s);

	if(initdraw(nil, nil, "guitest") < 0)
		sysfatal("initdraw: %r");
	if((mc = initmouse(nil, screen)) == nil)
		sysfatal("initmouse failed: %r");
	if((kc = initkeyboard(nil)) == nil)
		sysfatal("initmouse failed: %r");
	cols[BACK] = allocimagemix(display, DPurpleblue, DWhite);
	cols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
	cols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
	cols[TEXT] = display->black;
	cols[HTEXT] = display->black;
	chan = emallocz(sizeof(*chan), 1);
	chan->body = lorem;
	chan->bodylen = runestrlen(chan->body);
	chan->totlines = lines(chan->body);

	Alt a[NCHAN+1] = {
		[MOUSE] = {mc->c, &m, CHANRCV},
		[RESIZE] = {mc->resizec, nil, CHANRCV},
		[KEYBD] = {kc->c, &r, CHANRCV},
		[NCHAN] = {nil, nil, CHANEND},
	};
	drawtext(chan, 1);
	for(;;){
		switch(alt(a)){
		default:
			break;
		case KEYBD:
			if(r == Kdel)
				goto end;
			break;
		case MOUSE:
			if(!ptinrect(m.xy, scrollr))
				break;
			while(m.buttons == 2){
				setcurline(m.xy.y, chan);
				drawtext(chan, 0);
				readmouse(mc);
				m = *mc;
			}
			break;
		case RESIZE:
			if(getwindow(display, Refnone) < 0)
				sysfatal("%s: %r", argv0);
			drawtext(chan, 1);
			break;
		}
	}
end:
	threadexitsall(0);
}