summaryrefslogtreecommitdiff
path: root/libprefab/element.c
diff options
context:
space:
mode:
Diffstat (limited to 'libprefab/element.c')
-rw-r--r--libprefab/element.c723
1 files changed, 723 insertions, 0 deletions
diff --git a/libprefab/element.c b/libprefab/element.c
new file mode 100644
index 00000000..721a955c
--- /dev/null
+++ b/libprefab/element.c
@@ -0,0 +1,723 @@
+#include <lib9.h>
+#include <draw.h>
+#include <interp.h>
+#include <isa.h>
+#include "../libinterp/runt.h"
+#include <drawif.h>
+#include <prefab.h>
+
+
+void icondraw(Prefab_Element*, Image*, Rectangle, int, int);
+void textdraw(Prefab_Element*, Image*, Rectangle, int, int);
+void listdraw(Prefab_Element*, Image*, Rectangle, int, int);
+void outlinehighlight(Prefab_Element*, Image*, Prefab_Compound*, int);
+void texthighlight(Prefab_Element*, Image*, Prefab_Compound*, int);
+void simpleclip(Prefab_Element*, Rectangle);
+void horizontalclip(Prefab_Element*, Rectangle);
+void verticalclip(Prefab_Element*, Rectangle);
+void textscroll(Prefab_Element*, Point, int*);
+void horizontalscroll(Prefab_Element*, Point, int*);
+void verticalscroll(Prefab_Element*, Point, int*);
+void iconscroll(Prefab_Element*, Point, int*);
+
+struct
+{
+ void (*draw)(Prefab_Element*, Image*, Rectangle, int, int);
+ void (*highlight)(Prefab_Element*, Image*, Prefab_Compound*, int);
+ void (*clip)(Prefab_Element*, Rectangle);
+ void (*scroll)(Prefab_Element*, Point, int*);
+}elemfn[] = {
+ /* EIcon */ { icondraw, outlinehighlight, simpleclip, iconscroll, },
+ /* EText */ { textdraw, texthighlight, simpleclip, textscroll, },
+ /* ETitle */ { textdraw, outlinehighlight, simpleclip, textscroll, },
+ /* EHorizontal */ { listdraw, outlinehighlight, horizontalclip, horizontalscroll, },
+ /* EVertical */ { listdraw, outlinehighlight, verticalclip, verticalscroll, },
+ /* ESeparator */ { icondraw, outlinehighlight, simpleclip, iconscroll, },
+};
+
+Point
+iconsize(Image *image)
+{
+ Point dd;
+
+ if(image->repl){
+ dd.x = Dx(image->clipr);
+ dd.y = Dy(image->clipr);
+ }else{
+ dd.x = Dx(image->r);
+ dd.y = Dy(image->r);
+ }
+ return dd;
+}
+
+void
+icondraw(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
+{
+ Prefab_Style *style;
+ Rectangle r;
+ Point p;
+ PElement *pelem;
+ Image *image, *c;
+ Point size;
+
+ USED(highlight);
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ if(!rectclip(&clipr, i->clipr))
+ return;
+ R2R(r, elem->r);
+ if(!rectclip(&clipr, r))
+ return;
+ if(elem->image==H || elem->mask==H || badenviron(elem->environ, 0))
+ return;
+ style = elem->environ->style;
+ if(!clean){
+ c = lookupimage(style->elemcolor);
+ if(c != nil)
+ draw(i, clipr, c, nil, clipr.min);
+ }
+ r.min = pelem->drawpt;
+ image = lookupimage(elem->image);
+ if(image == nil)
+ return;
+ size = iconsize(image);
+ r.max.x = r.min.x+size.x;
+ r.max.y = r.min.y+size.y;
+ if(rectclip(&r, clipr)){
+ p = image->r.min;
+ p.x += r.min.x-pelem->drawpt.x;
+ p.y += r.min.y-pelem->drawpt.y;
+ c = lookupimage(elem->mask);
+ if(c != nil)
+ draw(i, r, image, c, p);
+ }
+}
+
+void
+textdraw(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
+{
+ Prefab_Style *style;
+ Rectangle r;
+ PElement *pelem;
+ Image *color, *c;
+ Font *font;
+
+ USED(highlight);
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ if(!rectclip(&clipr, i->clipr))
+ return;
+ R2R(r, elem->r);
+ if(!rectclip(&clipr, r))
+ return;
+ if(elem->str==H || badenviron(elem->environ, 0))
+ return;
+ style = elem->environ->style;
+ font = lookupfont(elem->font);
+ if(font == nil)
+ return;
+ if(highlight)
+ color = lookupimage(style->highlightcolor);
+ else
+ color = lookupimage(elem->image);
+ if(!clean){
+ c = lookupimage(style->elemcolor);
+ if(c != nil)
+ draw(i, clipr, c, nil, clipr.min);
+ }
+ if(color != nil)
+ _string(i, pelem->drawpt, color, pelem->drawpt, font, string2c(elem->str), nil, 1<<24, clipr, nil, pelem->drawpt, SoverD);
+}
+
+void
+listdraw(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
+{
+ Prefab_Style *style;
+ Prefab_Element *e;
+ List *l;
+ Rectangle r;
+ PElement *pelem;
+ Image *c;
+
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ if(!rectclip(&clipr, i->clipr))
+ return;
+ R2R(r, elem->r);
+ if(!rectclip(&clipr, r))
+ return;
+ if(elem->kids==H || badenviron(elem->environ, 0))
+ return;
+ if(pelem->first != elem->kids) /* error? */
+ return;
+ style = elem->environ->style;
+ if(!clean){
+ c = lookupimage(style->elemcolor);
+ if(c != nil)
+ draw(i, clipr, c, nil, clipr.min);
+ }
+ for(l=pelem->vfirst; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ R2R(r, e->r);
+ if(rectXrect(r, clipr))
+ drawelement(e, i, clipr, elem->environ==e->environ, highlight);
+ if(l == pelem->vlast)
+ break;
+ }
+}
+
+void
+drawelement(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
+{
+ PElement *pelem;
+
+ if(elem != H){
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ (*elemfn[elem->kind].draw)(elem, i, clipr, clean, highlight);
+ if(!highlight && pelem->highlight!=H)
+ (*elemfn[elem->kind].highlight)(elem, i, pelem->highlight, 1);
+ }
+}
+
+void
+translateelement(Prefab_Element *elem, Point delta)
+{
+ PElement *pelem;
+ List *l;
+
+ if(elem == H)
+ return;
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ elem->r.min.x += delta.x;
+ elem->r.min.y += delta.y;
+ elem->r.max.x += delta.x;
+ elem->r.max.y += delta.y;
+ pelem->drawpt.x += delta.x;
+ pelem->drawpt.y += delta.y;
+ switch(elem->kind){
+ case EHorizontal:
+ case EVertical:
+ if(pelem->first != elem->kids)
+ return;
+ for(l=elem->kids; l!=H; l=l->tail)
+ translateelement(*(Prefab_Element**)l->data, delta);
+ break;
+ }
+}
+
+int
+fitrect(Rectangle *r, Rectangle sr)
+{
+ if(r->max.x > sr.max.x){
+ r->min.x -= r->max.x-sr.max.x;
+ r->max.x = sr.max.x;
+ }
+ if(r->max.y > sr.max.y){
+ r->min.y -= r->max.y-sr.max.y;
+ r->max.y = sr.max.y;
+ }
+ if(r->min.x < sr.min.x){
+ r->max.x += sr.min.x-r->min.x;
+ r->min.x = sr.min.x;
+ }
+ if(r->min.y < sr.min.y){
+ r->max.y += sr.min.y-r->min.y;
+ r->min.y = sr.min.y;
+ }
+ return rectinrect(*r, sr);
+}
+
+void
+adjusthorizontal(Prefab_Element *elem, int spacing, int position)
+{
+ int edx, dx, i, x;
+ int nlist; /* BUG: should precompute */
+ List *l;
+ PElement *pelem;
+ Prefab_Element *e;
+ Point p;
+
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ if(pelem->first != elem->kids)
+ return;
+ p.y = 0;
+ switch(spacing){
+ default: /* shouldn't happen; protected by adjustelement */
+ case Adjpack:
+ x = elem->r.min.x;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ p.x = x - e->r.min.x;
+ translateelement(e, p);
+ x += Dx(e->r);
+ }
+ elem->r.max.x = x;
+ return;
+
+ case Adjequal:
+ dx = 0;
+ nlist = 0;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ if(dx < Dx(e->r))
+ dx = Dx(e->r);
+ nlist++;
+ }
+ elem->r.max.x = elem->r.min.x+nlist*dx;
+ break;
+
+ case Adjfill:
+ nlist = 0;
+ for(l=elem->kids; l!=H; l=l->tail)
+ nlist++;
+ dx = Dx(elem->r)/nlist;
+ break;
+ }
+ i = 0;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ edx = Dx(e->r);
+ if(position == Adjleft)
+ edx = 0;
+ else if(position == Adjcenter)
+ edx = (dx-edx)/2;
+ else /* right */
+ edx = dx-edx;
+ p.x = (elem->r.min.x+i*dx + edx) - e->r.min.x;
+ translateelement(e, p);
+ i++;
+ }
+}
+
+void
+adjustvertical(Prefab_Element *elem, int spacing, int position)
+{
+ int edy, dy, i, y;
+ int nlist; /* BUG: should precompute */
+ List *l;
+ PElement *pelem;
+ Prefab_Element *e;
+ Point p;
+
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ if(pelem->first != elem->kids)
+ return;
+ p.x = 0;
+ switch(spacing){
+ default: /* shouldn't happen; protected by adjustelement */
+ case Adjpack:
+ y = elem->r.min.y;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ p.y = y - e->r.min.y;
+ translateelement(e, p);
+ y += Dy(e->r);
+ }
+ elem->r.max.y = y;
+ return;
+
+ case Adjequal:
+ dy = 0;
+ nlist = 0;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ if(dy < Dy(e->r))
+ dy = Dy(e->r);
+ nlist++;
+ }
+ elem->r.max.y = elem->r.min.y+nlist*dy;
+ break;
+
+ case Adjfill:
+ nlist = 0;
+ for(l=elem->kids; l!=H; l=l->tail)
+ nlist++;
+ dy = Dy(elem->r)/nlist;
+ break;
+ }
+ i = 0;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ edy = Dy(e->r);
+ if(position == Adjup)
+ edy = 0;
+ else if(position == Adjcenter)
+ edy = (dy-edy)/2;
+ else /* down */
+ edy = dy-edy;
+ p.y = (elem->r.min.y+i*dy + edy) - e->r.min.y;
+ translateelement(e, p);
+ i++;
+ }
+}
+
+void
+adjustelement(Prefab_Element *elem, int spacing, int position)
+{
+ if(lookupelement(elem) == H)
+ return;
+ if(spacing<Adjpack || spacing>Adjfill || position<Adjleft || position>Adjdown)
+ return;
+ switch(elem->kind){
+ case EVertical:
+ adjustvertical(elem, spacing, position);
+ break;
+ case EHorizontal:
+ adjusthorizontal(elem, spacing, position);
+ break;
+ }
+}
+
+void
+highlightelement(Prefab_Element *elem, Image *i, Prefab_Compound *comp, int on)
+{
+ PElement *pelem;
+
+ pelem = lookupelement(elem);
+ if(pelem!=H && lookupcompound(comp)!=H){
+ if(on)
+ pelem->highlight = comp;
+ else
+ pelem->highlight = H;
+ (*elemfn[elem->kind].highlight)(elem, i, comp, on);
+ }
+}
+
+static
+int
+anytextelements(Prefab_Element *e)
+{
+ Prefab_Element *t;
+ List *l;
+
+ for(l=e->kids; l!=H; l=l->tail){
+ t = *(Prefab_Element**)l->data;
+ if(t->kind == EText)
+ return 1;
+ }
+ return 0;
+}
+
+void
+textlisthighlight(Prefab_Element *e, Image *i, Prefab_Compound *c, int on)
+{
+ Prefab_Element *t;
+ List *l;
+
+ for(l=e->kids; l!=H; l=l->tail){
+ t = *(Prefab_Element**)l->data;
+ if(t->kind == EText)
+ texthighlight(t, i, c, on);
+ }
+}
+
+void
+outlinehighlight(Prefab_Element *e, Image *i, Prefab_Compound *c, int on)
+{
+ List *l;
+ Prefab_Element *t;
+ Image *color;
+ Rectangle r, r1, r2;
+ Point dp;
+ int done;
+
+ /* see if we can do it by highlighting just a text element */
+ if((e->kind==EVertical || e->kind==EHorizontal) && e->kids!=H){
+ /* is any child a text element? */
+ if(anytextelements(e)){
+ textlisthighlight(e, i, c, on);
+ return;
+ }
+ /* grandchild? */
+ done = 0;
+ for(l=e->kids; l!=H; l=l->tail){
+ t = *(Prefab_Element**)l->data;
+ if(t->kind==EVertical || t->kind==EHorizontal)
+ if(anytextelements(t)){
+ textlisthighlight(t, i, c, on);
+ done = 1;
+ }
+ }
+ if(done)
+ return;
+ }
+ if(on){
+ color = lookupimage(e->environ->style->highlightcolor);
+ if(color == nil)
+ return;
+ R2R(r, e->r);
+ /* avoid outlining empty space around images */
+ dp = ((PElement*)e)->drawpt;
+ if(e->kind==EIcon && e->image->repl==0 && ptinrect(dp, r)){
+ R2R(r1, e->image->r);
+ R2R(r2, e->image->clipr);
+ if(rectclip(&r1, r2)){
+ dp.x += Dx(r1);
+ dp.y += Dy(r1);
+ if(ptinrect(dp, r))
+ r = Rpt(((PElement*)e)->drawpt, dp);
+ }
+ }
+ draw(i, r, color, nil, r.min);
+ drawelement(e, i, insetrect(r, 2), Dirty, 1);
+ }else{
+ drawelement(e, i, IRECT(e->r), Dirty, 0);
+ edge(c->environ, i, c->r, e->r);
+ }
+}
+
+void
+texthighlight(Prefab_Element *e, Image *i, Prefab_Compound *c, int on)
+{
+ drawelement(e, i, IRECT(e->r), Clean, on);
+ edge(c->environ, i, c->r, e->r);
+}
+
+void
+clipelement(Prefab_Element *elem, Rectangle r)
+{
+ if(lookupelement(elem) != H)
+ (*elemfn[elem->kind].clip)(elem, r);
+}
+
+void
+simpleclip(Prefab_Element *elem, Rectangle r)
+{
+ R2R(elem->r, r);
+}
+
+void
+horizontalclip(Prefab_Element *elem, Rectangle r)
+{
+ int x;
+ List *l;
+ Prefab_Element *e;
+ PElement *pelem;
+
+ x = r.min.x;
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ for(l=pelem->vfirst; l!=H && x<r.max.x; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ x += Dx(e->r);
+ }
+ pelem->vlast = l;
+ R2R(elem->r, r);
+}
+
+void
+verticalclip(Prefab_Element *elem, Rectangle r)
+{
+ int y;
+ List *l;
+ Prefab_Element *e;
+ PElement *pelem;
+
+ y = r.min.y;
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ for(l=pelem->vfirst; l!=H && y<r.max.y; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ y += Dy(e->r);
+ }
+ pelem->vlast = l;
+ R2R(elem->r, r);
+}
+
+void
+scrollelement(Prefab_Element *elem, Point d, int *moved)
+{
+ if(lookupelement(elem) != H)
+ (*elemfn[elem->kind].scroll)(elem, d, moved);
+}
+
+void
+textscroll(Prefab_Element *elem, Point d, int *moved)
+{
+ PElement *pelem;
+
+ pelem = lookupelement(elem);
+ if(pelem==H || (d.x==0 && d.y==0))
+ return;
+ pelem->drawpt = subpt(pelem->drawpt, d);
+ *moved = 1;
+}
+
+void
+iconscroll(Prefab_Element *elem, Point d, int *moved)
+{
+ Point p;
+ Image *i;
+ PElement *pelem;
+
+ pelem = lookupelement(elem);
+ if(pelem==H || elem->image==H || (d.x==0 && d.y==0))
+ return;
+ i = lookupimage(elem->image);
+ if(i == nil)
+ return;
+ p = subpt(pelem->drawpt, d);
+ if(i->repl == 0){
+ if(p.x+Dx(i->clipr) < elem->r.max.x)
+ p.x = elem->r.max.x - Dx(i->clipr);
+ if(p.y+Dy(i->clipr) < elem->r.max.y)
+ p.y = elem->r.max.y - Dy(i->clipr);
+ if(p.x > elem->r.min.x)
+ p.x = elem->r.min.x;
+ if(p.y > elem->r.min.y)
+ p.y = elem->r.min.y;
+ }
+ *moved = !eqpt(pelem->drawpt, p);
+ pelem->drawpt = p;
+}
+
+void
+horizontalscroll(Prefab_Element *elem, Point d, int *moved)
+{
+ List *l;
+ Prefab_Element *e;
+ PElement *pelem;
+
+ pelem = lookupelement(elem);
+ if(pelem==H || elem->kids==H || (d.x==0 && d.y==0))
+ return;
+ for(l=pelem->first; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ translateelement(e, d);
+ }
+ for(l=pelem->first; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ if(e->r.max.x > elem->r.min.x)
+ break;
+ }
+ pelem->vfirst = l;
+ pelem->vlast = l;
+ for(; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ pelem->vlast = l;
+ if(e->r.min.x >= elem->r.max.x)
+ break;
+ }
+ *moved = 1;
+}
+
+void
+verticalscroll(Prefab_Element *elem, Point d, int *moved)
+{
+ List *l;
+ Prefab_Element *e;
+ PElement *pelem;
+
+ pelem = lookupelement(elem);
+ if(pelem==H || elem->kids==H || (d.x==0 && d.y==0))
+ return;
+ for(l=pelem->first; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ translateelement(e, d);
+ }
+ for(l=pelem->first; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ if(e->r.max.y > elem->r.min.y)
+ break;
+ }
+ pelem->vfirst = l;
+ pelem->vlast = l;
+ for(; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ pelem->vlast = l;
+ if(e->r.min.y >= elem->r.max.y)
+ break;
+ }
+ *moved = 1;
+}
+
+/*
+ * Make e visible within list. Return value is whether any change was made;
+ * if so, must redraw (BUG: should probably do this here)
+ */
+int
+showelement(Prefab_Element *list, Prefab_Element *e)
+{
+ Point p;
+ Prefab_Element *h, *t;
+ PElement *plist;
+ int moved;
+
+ p.x = p.y = 0;
+ if(list->kids == H)
+ return 0;
+ plist = lookupelement(list);
+ if(plist == H)
+ return 0;
+ h = *(Prefab_Element**)plist->first->data;
+ t = *(Prefab_Element**)plist->last->data;
+ if(list->kind == EHorizontal){
+ p.x = (list->r.min.x+Dx(list->r)/2) - e->r.min.x;
+ if(e->r.min.x < list->r.min.x){ /* scroll to right */
+ if(e->r.max.x+p.x > list->r.max.x)
+ p.x = list->r.min.x-e->r.min.x;
+ if(h->r.min.x + p.x > list->r.min.x)
+ p.x = list->r.min.x-h->r.min.x;
+ }else if(e->r.max.x > list->r.max.x){ /* scroll to left */
+ if(e->r.min.x+p.x < list->r.min.x)
+ p.x = list->r.min.x-e->r.min.x;
+ if(t->r.max.x + p.x < list->r.max.x)
+ p.x = list->r.max.x-t->r.max.x;
+ }else
+ return 0;
+ }else if(list->kind == EVertical){
+ p.y = (list->r.min.y+Dy(list->r)/2) - e->r.min.y;
+ if(e->r.min.y < list->r.min.y){ /* scroll towards bottom */
+ if(e->r.max.y+p.y > list->r.max.y)
+ p.y = list->r.min.y-e->r.min.y;
+ if(h->r.min.y + p.y > list->r.min.y)
+ p.y = list->r.min.y-h->r.min.y;
+ }else if(e->r.max.y > list->r.max.y){ /* scroll towards top */
+ if(e->r.min.y+p.y < list->r.min.y)
+ p.y = list->r.min.y-e->r.min.y;
+ if(t->r.max.y + p.y < list->r.max.y)
+ p.y = list->r.max.y-t->r.max.y;
+ }else
+ return 0;
+ }else
+ return 0;
+ if(p.x!=0 || p.y!=0){
+ scrollelement(list, p, &moved);
+ return 1;
+ }
+ return 0;
+}
+
+PElement*
+mkelement(Prefab_Environ *env, enum Elementtype t)
+{
+ Heap *h;
+ PElement *p;
+
+ h = heapz(TElement);
+ p = H2D(PElement*, h);
+ p->highlight = H;
+ p->first = H;
+ p->last = H;
+ p->vfirst = H;
+ p->vlast = H;
+ p->nkids = 1;
+ p->pkind = t;
+ p->e.kind = t;
+ p->e.environ = env;
+ D2H(env)->ref++;
+ return p;
+}