ref: aca0f958f91ee7adaaae77eb85c8ca9b8467da87
dir: /guitest.c/
#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);
}