#include "paint.h" typedef struct Container { Widget *x; /* the scroll bar */ Widget *y; Widget *c; /* the widget being scrolled */ } Container; typedef struct Bar { int isx; Image *bg; Point (*getdim)(Widget *); Rectangle (*rect)(Widget *); void (*setcpos)(Widget *, Point); Widget *c; } Bar; typedef void (*Scroller)(Widget *c, Point pos); #define XX 0xAF, #define OO 0x20, static uchar ydarkbits[] = { OO OO OO XX OO OO OO XX OO OO OO OO OO OO OO OO OO OO OO XX OO OO OO XX OO OO OO OO OO OO OO OO OO OO OO OO }; static Bitmap ydarkbitmap = { 9, 4, GREY8, ydarkbits, }; static Image *ydark; static uchar xdarkbits[] = { OO OO OO OO OO OO OO OO XX OO OO OO OO OO OO OO OO OO XX OO OO OO OO OO XX OO OO OO OO OO OO OO OO OO XX OO OO OO OO OO }; static Bitmap xdarkbitmap = { 4, 10, GREY8, xdarkbits, }; static Image *xdark; static uchar outlinebits[] = { OO }; static Bitmap outlinebitmap = { 1, 1, GREY8, outlinebits, }; static Image *outline; static uchar litebits[] = { 0x7F, }; static Bitmap litebitmap = { 1, 1, GREY8, litebits, }; static Image *lite; static int init(void) { if(ydark != nil) return 0; if((ydark = loadbitmap(ydarkbitmap)) == nil || (xdark = loadbitmap(xdarkbitmap)) == nil || (lite = loadbitmap(litebitmap)) == nil || (outline = loadbitmap(outlinebitmap)) == nil ) return -1; /* return werrstr unmodified */ return 0; } static int mouse2(Widget *w, Mousectl *mctl, Bar *bar) { Point dim; Rectangle er; int g; /* the position relative to the elevator box where it was grabbed */ int bpos; /* the selected position on the scroll bar */ int bsz; /* the length in pixels of the scroll bar */ int csz; /* the length in pixels of the entirety of what is being scrolled */ int cpos; /* the resulting position selected on the canvas */ er = bar->rect(w); if(ptinrect(mctl->xy, er)) { if(bar->isx) g = mctl->xy.x - er.min.x; else g = mctl->xy.y - er.min.y; } else { g = 0; } while(mctl->buttons & 2) { dim = bar->getdim(bar->c); if(bar->isx) { bpos = max(0, mctl->xy.x - w->r.min.x - g); bsz = Dx(w->r); csz = dim.x; } else { bpos = max(0, mctl->xy.y - w->r.min.y - g); bsz = Dy(w->r); csz = dim.y; } cpos = clampint(0, bpos * csz / bsz, csz - bsz); //fprint(2, "mouse2: %d\n", bpos); if(bar->isx) bar->setcpos(bar->c, Pt(cpos, ~0)); else bar->setcpos(bar->c, Pt(~0, cpos)); /* this redraws even when the position hasn't changed */ bar->c->redraw(bar->c, RDall); w->redraw(w, RDinc); readmouse(mctl); } while(mctl->buttons) readmouse(mctl); return 0; } static int mouse(Widget *w, Mousectl *mctl) { Bar *bar; Point dim; int Δp; /* the distance in pixels between the current and selected positions */ int bsz; /* the length in pixels of the scroll bar */ int csz; /* the length in pixels of the entirety of what is being scrolled */ int cpos; /* the selected position on the canvas */ bar = w->aux; switch(mctl->buttons) { case 2: return mouse2(w, mctl, bar); case 4: case 1: dim = bar->getdim(bar->c); if(bar->isx) { Δp = max(0, mctl->xy.x - w->r.min.x); bsz = Dx(w->r); csz = dim.x; cpos = ((Canvas *)bar->c->aux)->pos.x; } else { Δp = max(0, mctl->xy.y - w->r.min.y ); bsz = Dy(w->r); csz = dim.y; cpos = ((Canvas *)bar->c->aux)->pos.y; } switch(mctl->buttons) { case 4: cpos += Δp; break; case 1: cpos -= Δp; break; } cpos = max(0, cpos); cpos = min(cpos, csz - bsz); if(bar->isx) bar->setcpos(bar->c, Pt(cpos, ~0)); else bar->setcpos(bar->c, Pt(~0, cpos)); bar->c->redraw(bar->c, RDall); w->redraw(w, RDinc); do { readmouse(mctl); } while(mctl->buttons); } return 0; } static Rectangle yrect(Widget *w) { Bar *bar; Point itmp; int csz, bsz, start, end; Rectangle r; bar = w->aux; itmp = bar->getdim(bar->c); csz = itmp.y; bsz = Dy(w->r); if(csz == 0) { start = 0; end = bsz; } else { start = max(0, ((Canvas *)bar->c->aux)->pos.y * bsz / csz); end = min(bsz * bsz / csz + start + 1, bsz); } //fprint(2, "yrect: %d\n", start); r = Rect(w->r.min.x, w->r.min.y + start, w->r.max.x, w->r.min.y + end); r.max.x--; return r; } static Rectangle xrect(Widget *w) { Bar *bar; Point itmp; int csz, bsz, start, end; Rectangle r; bar = w->aux; itmp = bar->getdim(bar->c); csz = itmp.x; bsz = Dx(w->r); if(csz == 0) { start = 0; end = bsz; } else { start = max(0, ((Canvas *)bar->c->aux)->pos.x * bsz / csz); end = min(bsz * bsz / csz + start + 1, bsz); } r = Rect(w->r.min.x + start, w->r.min.y, w->r.min.x + end, w->r.max.y); r.min.y++; return r; } static void redraw(Widget *w, RedrawMode) { Bar *bar; bar = w->aux; draw(w->screen, w->r, bar->bg, nil, ZP); draw(w->screen, bar->rect(w), lite, nil, ZP); } static void xyredraw(Widget *w, RedrawMode redrawmode) { Rectangle r; if(redrawmode == RDall) { r = Rect(0, Dy(w->r) - 10, 10, Dy(w->r)); r = rectaddpt(r, w->r.min); draw(w->screen, r, outline, nil, ZP); r.min.y++; r.max.x--; draw(w->screen, r, lite, nil, ZP); } widgetredrawdescend(w, redrawmode); } static Rdims ydims(Widget *w) { Container *container; container = w->aux; return adddims(container->c->dims(container->c), (Rdims){Pt(10, 0), Pt(10, 0), Pt(10, 0)}); } static void yresize(Widget *w, Point dim) { Container *container; container = w->aux; container->c->resize(container->c, subpt(dim, Pt(10, 0))); container->y->r = Rect(0, 0, 10, dim.y); movewidget(container->y, ZP, container->y->screen); w->r = rectaddpt(Rpt(ZP, dim), w->r.min); } static Rdims xdims(Widget *w) { Container *container; container = w->aux; return adddims(container->c->dims(container->c), (Rdims){Pt(0, 10), Pt(0, 10), Pt(0, 10)}); } static void xresize(Widget *w, Point dim) { Container *container; container = w->aux; container->c->resize(container->c, subpt(dim, Pt(0, 10))); container->x->r = Rect(0, 0, dim.x, 10); movewidget(container->x, Pt(0, dim.y - 10), container->x->screen); w->r = rectaddpt(Rpt(ZP, dim), w->r.min); } static Rdims xydims(Widget *w) { Container *container; container = w->aux; return adddims(container->c->dims(container->c), (Rdims){Pt(10, 10), Pt(10, 10), Pt(10, 10)}); } static void xyresize(Widget *w, Point dim) { Container *container; container = w->aux; container->c->resize(container->c, subpt(dim, Pt(10, 10))); container->x->r = Rect(0, 0, dim.x - 10, 10); movewidget(container->x, Pt(10, dim.y - 10), container->x->screen); container->y->r = Rect(0, 0, 10, dim.y - 10); movewidget(container->y, ZP, container->y->screen); w->r = rectaddpt(Rpt(ZP, dim), w->r.min); } static void wfree(Widget *w) { free(w->aux); freewidget(w); } static Widget * _mkyscrollbar(Widget *c, Point (*getdim)(Widget *), void (*scroll)(Widget *, Point)) { Bar *aux; aux = emalloc(sizeof(*aux)); *aux = (Bar){0, ydark, getdim, yrect, scroll, c}; return mkwidget(ZR, mouse, widgetsyncnop, redraw, widgetdimsnop, widgetresizenop, wfree, aux); } static Widget * _mkxscrollbar(Widget *c, Point (*getdim)(Widget *), void (*scroll)(Widget *, Point)) { Bar *aux; aux = emalloc(sizeof(*aux)); *aux = (Bar){1, xdark, getdim, xrect, scroll, c}; return mkwidget(ZR, mouse, widgetsyncnop, redraw, widgetdimsnop, widgetresizenop, wfree, aux); } Widget * mkyscrollbar(Widget *c, Point (*getdim)(Widget *), Scroller scroll) { Container *container; Widget *w, *y; if(init() < 0) { werrstr("mkyscrollbar: init: %r"); return nil; } container = emalloc(sizeof(*container)); if((w = mkwidget(ZR, widgetmousedescend, widgetsyncnop, widgetredrawdescend, ydims, yresize, freewidget, container )) == nil) sysfatal("mkyscrollbar: %r"); if((y = _mkyscrollbar(c, getdim, scroll)) == nil ) sysfatal("mkyscrollbar: %r"); container->y = y; container->c = c; addchildwidget(w, c, Pt(10, 0)); addchildwidget(w, y, ZP); return w; } Widget * mkxscrollbar(Widget *c, Point (*getdim)(Widget *), Scroller scroll) { Container *container; Widget *w, *x; if(init() < 0) { werrstr("mkxscrollbar: init: %r"); return nil; } container = emalloc(sizeof(*container)); if((w = mkwidget(ZR, widgetmousedescend, widgetsyncnop, widgetredrawdescend, xdims, xresize, freewidget, container )) == nil) sysfatal("mkxscrollbar: %r"); if((x = _mkxscrollbar(c, getdim, scroll)) == nil) sysfatal("mkxscrollbar: %r"); container->x = x; container->c = c; addchildwidget(w, c, ZP); addchildwidget(w, x, Pt(0, Dy(c->r))); return w; } Widget * mkscrollbars(Widget *c, Point (*getdim)(Widget *), Scroller scroll) { Container *container; Widget *w, *x, *y; if(init() < 0) { werrstr("mkscrollbars: init: %r"); return nil; } container = emalloc(sizeof(*container)); if((w = mkwidget(ZR, widgetmousedescend, widgetsyncnop, xyredraw, xydims, xyresize, wfree, container)) == nil) sysfatal("mkscrollbars: %r"); if((x = _mkxscrollbar(c, getdim, scroll)) == nil) sysfatal("mkscrollbars: %r"); if((y = _mkyscrollbar(c, getdim, scroll)) == nil) sysfatal("mkscrollbars: %r"); container->x = x; container->y = y; container->c = c; addchildwidget(w, c, Pt(10, 0)); addchildwidget(w, x, Pt(10, Dy(c->r))); addchildwidget(w, y, ZP); return w; }