ref: 2b38105bb4f24e9990844be297b990307dc98a01
dir: /libprefab/element.c/
#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; }