xref: /inferno-os/libprefab/element.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1 #include <lib9.h>
2 #include <draw.h>
3 #include <interp.h>
4 #include <isa.h>
5 #include "../libinterp/runt.h"
6 #include <drawif.h>
7 #include <prefab.h>
8 
9 
10 void	icondraw(Prefab_Element*, Image*, Rectangle, int, int);
11 void	textdraw(Prefab_Element*, Image*, Rectangle, int, int);
12 void	listdraw(Prefab_Element*, Image*, Rectangle, int, int);
13 void	outlinehighlight(Prefab_Element*, Image*, Prefab_Compound*, int);
14 void	texthighlight(Prefab_Element*, Image*, Prefab_Compound*, int);
15 void	simpleclip(Prefab_Element*, Rectangle);
16 void	horizontalclip(Prefab_Element*, Rectangle);
17 void	verticalclip(Prefab_Element*, Rectangle);
18 void	textscroll(Prefab_Element*, Point, int*);
19 void	horizontalscroll(Prefab_Element*, Point, int*);
20 void	verticalscroll(Prefab_Element*, Point, int*);
21 void	iconscroll(Prefab_Element*, Point, int*);
22 
23 struct
24 {
25 	void	(*draw)(Prefab_Element*, Image*, Rectangle, int, int);
26 	void	(*highlight)(Prefab_Element*, Image*, Prefab_Compound*, int);
27 	void	(*clip)(Prefab_Element*, Rectangle);
28 	void	(*scroll)(Prefab_Element*, Point, int*);
29 }elemfn[] = {
30  /* EIcon */		{ icondraw, outlinehighlight, simpleclip, iconscroll, },
31  /* EText */		{ textdraw, texthighlight, simpleclip, textscroll, },
32  /* ETitle */		{ textdraw, outlinehighlight, simpleclip, textscroll, },
33  /* EHorizontal */	{ listdraw, outlinehighlight, horizontalclip, horizontalscroll, },
34  /* EVertical */	{ listdraw, outlinehighlight, verticalclip, verticalscroll, },
35  /* ESeparator */	{ icondraw, outlinehighlight, simpleclip, iconscroll, },
36 };
37 
38 Point
iconsize(Image * image)39 iconsize(Image *image)
40 {
41 	Point dd;
42 
43 	if(image->repl){
44 		dd.x = Dx(image->clipr);
45 		dd.y = Dy(image->clipr);
46 	}else{
47 		dd.x = Dx(image->r);
48 		dd.y = Dy(image->r);
49 	}
50 	return dd;
51 }
52 
53 void
icondraw(Prefab_Element * elem,Image * i,Rectangle clipr,int clean,int highlight)54 icondraw(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
55 {
56 	Prefab_Style *style;
57 	Rectangle r;
58 	Point p;
59 	PElement *pelem;
60 	Image *image, *c;
61 	Point size;
62 
63 	USED(highlight);
64 	pelem = lookupelement(elem);
65 	if(pelem == H)
66 		return;
67 	if(!rectclip(&clipr, i->clipr))
68 		return;
69 	R2R(r, elem->r);
70 	if(!rectclip(&clipr, r))
71 		return;
72 	if(elem->image==H || elem->mask==H || badenviron(elem->environ, 0))
73 		return;
74 	style = elem->environ->style;
75 	if(!clean){
76 		c = lookupimage(style->elemcolor);
77 		if(c != nil)
78 			draw(i, clipr, c, nil, clipr.min);
79 	}
80 	r.min = pelem->drawpt;
81 	image = lookupimage(elem->image);
82 	if(image == nil)
83 		return;
84 	size = iconsize(image);
85 	r.max.x = r.min.x+size.x;
86 	r.max.y = r.min.y+size.y;
87 	if(rectclip(&r, clipr)){
88 		p = image->r.min;
89 		p.x += r.min.x-pelem->drawpt.x;
90 		p.y += r.min.y-pelem->drawpt.y;
91 		c = lookupimage(elem->mask);
92 		if(c != nil)
93 			draw(i, r, image, c, p);
94 	}
95 }
96 
97 void
textdraw(Prefab_Element * elem,Image * i,Rectangle clipr,int clean,int highlight)98 textdraw(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
99 {
100 	Prefab_Style *style;
101 	Rectangle r;
102 	PElement *pelem;
103 	Image *color, *c;
104 	Font *font;
105 
106 	USED(highlight);
107 	pelem = lookupelement(elem);
108 	if(pelem == H)
109 		return;
110 	if(!rectclip(&clipr, i->clipr))
111 		return;
112 	R2R(r, elem->r);
113 	if(!rectclip(&clipr, r))
114 		return;
115 	if(elem->str==H || badenviron(elem->environ, 0))
116 		return;
117 	style = elem->environ->style;
118 	font = lookupfont(elem->font);
119 	if(font == nil)
120 		return;
121 	if(highlight)
122 		color = lookupimage(style->highlightcolor);
123 	else
124 		color = lookupimage(elem->image);
125 	if(!clean){
126 		c = lookupimage(style->elemcolor);
127 		if(c != nil)
128 			draw(i, clipr, c, nil, clipr.min);
129 	}
130 	if(color != nil)
131 		_string(i, pelem->drawpt, color, pelem->drawpt, font, string2c(elem->str), nil, 1<<24, clipr, nil, pelem->drawpt, SoverD);
132 }
133 
134 void
listdraw(Prefab_Element * elem,Image * i,Rectangle clipr,int clean,int highlight)135 listdraw(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
136 {
137 	Prefab_Style *style;
138 	Prefab_Element *e;
139 	List *l;
140 	Rectangle r;
141 	PElement *pelem;
142 	Image *c;
143 
144 	pelem = lookupelement(elem);
145 	if(pelem == H)
146 		return;
147 	if(!rectclip(&clipr, i->clipr))
148 		return;
149 	R2R(r, elem->r);
150 	if(!rectclip(&clipr, r))
151 		return;
152 	if(elem->kids==H || badenviron(elem->environ, 0))
153 		return;
154 	if(pelem->first != elem->kids)	/* error? */
155 		return;
156 	style = elem->environ->style;
157 	if(!clean){
158 		c = lookupimage(style->elemcolor);
159 		if(c != nil)
160 			draw(i, clipr, c, nil, clipr.min);
161 	}
162 	for(l=pelem->vfirst; l!=H; l=l->tail){
163 		e = *(Prefab_Element**)l->data;
164 		R2R(r, e->r);
165 		if(rectXrect(r, clipr))
166 			drawelement(e, i, clipr, elem->environ==e->environ, highlight);
167 		if(l == pelem->vlast)
168 			break;
169 	}
170 }
171 
172 void
drawelement(Prefab_Element * elem,Image * i,Rectangle clipr,int clean,int highlight)173 drawelement(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
174 {
175 	PElement *pelem;
176 
177 	if(elem != H){
178 		pelem = lookupelement(elem);
179 		if(pelem == H)
180 			return;
181 		(*elemfn[elem->kind].draw)(elem, i, clipr, clean, highlight);
182 		if(!highlight && pelem->highlight!=H)
183 			(*elemfn[elem->kind].highlight)(elem, i, pelem->highlight, 1);
184 	}
185 }
186 
187 void
translateelement(Prefab_Element * elem,Point delta)188 translateelement(Prefab_Element *elem, Point delta)
189 {
190 	PElement *pelem;
191 	List *l;
192 
193 	if(elem == H)
194 		return;
195 	pelem = lookupelement(elem);
196 	if(pelem == H)
197 		return;
198 	elem->r.min.x += delta.x;
199 	elem->r.min.y += delta.y;
200 	elem->r.max.x += delta.x;
201 	elem->r.max.y += delta.y;
202 	pelem->drawpt.x += delta.x;
203 	pelem->drawpt.y += delta.y;
204 	switch(elem->kind){
205 	case EHorizontal:
206 	case EVertical:
207 		if(pelem->first != elem->kids)
208 			return;
209 		for(l=elem->kids; l!=H; l=l->tail)
210 			translateelement(*(Prefab_Element**)l->data, delta);
211 		break;
212 	}
213 }
214 
215 int
fitrect(Rectangle * r,Rectangle sr)216 fitrect(Rectangle *r, Rectangle sr)
217 {
218 	if(r->max.x > sr.max.x){
219 		r->min.x -= r->max.x-sr.max.x;
220 		r->max.x = sr.max.x;
221 	}
222 	if(r->max.y > sr.max.y){
223 		r->min.y -= r->max.y-sr.max.y;
224 		r->max.y = sr.max.y;
225 	}
226 	if(r->min.x < sr.min.x){
227 		r->max.x += sr.min.x-r->min.x;
228 		r->min.x = sr.min.x;
229 	}
230 	if(r->min.y < sr.min.y){
231 		r->max.y += sr.min.y-r->min.y;
232 		r->min.y = sr.min.y;
233 	}
234 	return rectinrect(*r, sr);
235 }
236 
237 void
adjusthorizontal(Prefab_Element * elem,int spacing,int position)238 adjusthorizontal(Prefab_Element *elem, int spacing, int position)
239 {
240 	int edx, dx, i, x;
241 	int nlist;	/* BUG: should precompute */
242 	List *l;
243 	PElement *pelem;
244 	Prefab_Element *e;
245 	Point p;
246 
247 	pelem = lookupelement(elem);
248 	if(pelem == H)
249 		return;
250 	if(pelem->first != elem->kids)
251 		return;
252 	p.y = 0;
253 	switch(spacing){
254 	default:	/* shouldn't happen; protected by adjustelement */
255 	case Adjpack:
256 		x = elem->r.min.x;
257 		for(l=elem->kids; l!=H; l=l->tail){
258 			e = *(Prefab_Element**)l->data;
259 			p.x = x - e->r.min.x;
260 			translateelement(e, p);
261 			x += Dx(e->r);
262 		}
263 		elem->r.max.x = x;
264 		return;
265 
266 	case Adjequal:
267 		dx = 0;
268 		nlist = 0;
269 		for(l=elem->kids; l!=H; l=l->tail){
270 			e = *(Prefab_Element**)l->data;
271 			if(dx < Dx(e->r))
272 				dx = Dx(e->r);
273 			nlist++;
274 		}
275 		elem->r.max.x = elem->r.min.x+nlist*dx;
276 		break;
277 
278 	case Adjfill:
279 		nlist = 0;
280 		for(l=elem->kids; l!=H; l=l->tail)
281 			nlist++;
282 		dx = Dx(elem->r)/nlist;
283 		break;
284 	}
285 	i = 0;
286 	for(l=elem->kids; l!=H; l=l->tail){
287 		e = *(Prefab_Element**)l->data;
288 		edx = Dx(e->r);
289 		if(position == Adjleft)
290 			edx = 0;
291 		else if(position == Adjcenter)
292 			edx = (dx-edx)/2;
293 		else	/* right */
294 			edx = dx-edx;
295 		p.x = (elem->r.min.x+i*dx + edx) - e->r.min.x;
296 		translateelement(e, p);
297 		i++;
298 	}
299 }
300 
301 void
adjustvertical(Prefab_Element * elem,int spacing,int position)302 adjustvertical(Prefab_Element *elem, int spacing, int position)
303 {
304 	int edy, dy, i, y;
305 	int nlist;	/* BUG: should precompute */
306 	List *l;
307 	PElement *pelem;
308 	Prefab_Element *e;
309 	Point p;
310 
311 	pelem = lookupelement(elem);
312 	if(pelem == H)
313 		return;
314 	if(pelem->first != elem->kids)
315 		return;
316 	p.x = 0;
317 	switch(spacing){
318 	default:	/* shouldn't happen; protected by adjustelement */
319 	case Adjpack:
320 		y = elem->r.min.y;
321 		for(l=elem->kids; l!=H; l=l->tail){
322 			e = *(Prefab_Element**)l->data;
323 			p.y = y - e->r.min.y;
324 			translateelement(e, p);
325 			y += Dy(e->r);
326 		}
327 		elem->r.max.y = y;
328 		return;
329 
330 	case Adjequal:
331 		dy = 0;
332 		nlist = 0;
333 		for(l=elem->kids; l!=H; l=l->tail){
334 			e = *(Prefab_Element**)l->data;
335 			if(dy < Dy(e->r))
336 				dy = Dy(e->r);
337 			nlist++;
338 		}
339 		elem->r.max.y = elem->r.min.y+nlist*dy;
340 		break;
341 
342 	case Adjfill:
343 		nlist = 0;
344 		for(l=elem->kids; l!=H; l=l->tail)
345 			nlist++;
346 		dy = Dy(elem->r)/nlist;
347 		break;
348 	}
349 	i = 0;
350 	for(l=elem->kids; l!=H; l=l->tail){
351 		e = *(Prefab_Element**)l->data;
352 		edy = Dy(e->r);
353 		if(position == Adjup)
354 			edy = 0;
355 		else if(position == Adjcenter)
356 			edy = (dy-edy)/2;
357 		else	/* down */
358 			edy = dy-edy;
359 		p.y = (elem->r.min.y+i*dy + edy) - e->r.min.y;
360 		translateelement(e, p);
361 		i++;
362 	}
363 }
364 
365 void
adjustelement(Prefab_Element * elem,int spacing,int position)366 adjustelement(Prefab_Element *elem, int spacing, int position)
367 {
368 	if(lookupelement(elem) == H)
369 		return;
370 	if(spacing<Adjpack || spacing>Adjfill || position<Adjleft || position>Adjdown)
371 		return;
372 	switch(elem->kind){
373 	case EVertical:
374 		adjustvertical(elem, spacing, position);
375 		break;
376 	case EHorizontal:
377 		adjusthorizontal(elem, spacing, position);
378 		break;
379 	}
380 }
381 
382 void
highlightelement(Prefab_Element * elem,Image * i,Prefab_Compound * comp,int on)383 highlightelement(Prefab_Element *elem, Image *i, Prefab_Compound *comp, int on)
384 {
385 	PElement *pelem;
386 
387 	pelem = lookupelement(elem);
388 	if(pelem!=H && lookupcompound(comp)!=H){
389 		if(on)
390 			pelem->highlight = comp;
391 		else
392 			pelem->highlight = H;
393 		(*elemfn[elem->kind].highlight)(elem, i, comp, on);
394 	}
395 }
396 
397 static
398 int
anytextelements(Prefab_Element * e)399 anytextelements(Prefab_Element *e)
400 {
401 	Prefab_Element *t;
402 	List *l;
403 
404 	for(l=e->kids; l!=H; l=l->tail){
405 		t = *(Prefab_Element**)l->data;
406 		if(t->kind == EText)
407 			return 1;
408 	}
409 	return 0;
410 }
411 
412 void
textlisthighlight(Prefab_Element * e,Image * i,Prefab_Compound * c,int on)413 textlisthighlight(Prefab_Element *e, Image *i, Prefab_Compound *c, int on)
414 {
415 	Prefab_Element *t;
416 	List *l;
417 
418 	for(l=e->kids; l!=H; l=l->tail){
419 		t = *(Prefab_Element**)l->data;
420 		if(t->kind == EText)
421 			texthighlight(t, i, c, on);
422 	}
423 }
424 
425 void
outlinehighlight(Prefab_Element * e,Image * i,Prefab_Compound * c,int on)426 outlinehighlight(Prefab_Element *e, Image *i, Prefab_Compound *c, int on)
427 {
428 	List *l;
429 	Prefab_Element *t;
430 	Image *color;
431 	Rectangle r, r1, r2;
432 	Point dp;
433 	int done;
434 
435 	/* see if we can do it by highlighting just a text element */
436 	if((e->kind==EVertical || e->kind==EHorizontal) && e->kids!=H){
437 		/* is any child a text element? */
438 		if(anytextelements(e)){
439 			textlisthighlight(e, i, c, on);
440 			return;
441 		}
442 		/* grandchild? */
443 		done = 0;
444 		for(l=e->kids; l!=H; l=l->tail){
445 			t = *(Prefab_Element**)l->data;
446 			if(t->kind==EVertical || t->kind==EHorizontal)
447 				if(anytextelements(t)){
448 					textlisthighlight(t, i, c, on);
449 					done = 1;
450 				}
451 		}
452 		if(done)
453 			return;
454 	}
455 	if(on){
456 		color = lookupimage(e->environ->style->highlightcolor);
457 		if(color == nil)
458 			return;
459 		R2R(r, e->r);
460 		/* avoid outlining empty space around images */
461 		dp = ((PElement*)e)->drawpt;
462 		if(e->kind==EIcon && e->image->repl==0 && ptinrect(dp, r)){
463 			R2R(r1, e->image->r);
464 			R2R(r2, e->image->clipr);
465 			if(rectclip(&r1, r2)){
466 				dp.x += Dx(r1);
467 				dp.y += Dy(r1);
468 				if(ptinrect(dp, r))
469 					r = Rpt(((PElement*)e)->drawpt, dp);
470 			}
471 		}
472 		draw(i, r, color, nil, r.min);
473 		drawelement(e, i, insetrect(r, 2), Dirty, 1);
474 	}else{
475 		drawelement(e, i, IRECT(e->r), Dirty, 0);
476 		edge(c->environ, i, c->r, e->r);
477 	}
478 }
479 
480 void
texthighlight(Prefab_Element * e,Image * i,Prefab_Compound * c,int on)481 texthighlight(Prefab_Element *e, Image *i, Prefab_Compound *c, int on)
482 {
483 	drawelement(e, i, IRECT(e->r), Clean, on);
484 	edge(c->environ, i, c->r, e->r);
485 }
486 
487 void
clipelement(Prefab_Element * elem,Rectangle r)488 clipelement(Prefab_Element *elem, Rectangle r)
489 {
490 	if(lookupelement(elem) != H)
491 		(*elemfn[elem->kind].clip)(elem, r);
492 }
493 
494 void
simpleclip(Prefab_Element * elem,Rectangle r)495 simpleclip(Prefab_Element *elem, Rectangle r)
496 {
497 	R2R(elem->r, r);
498 }
499 
500 void
horizontalclip(Prefab_Element * elem,Rectangle r)501 horizontalclip(Prefab_Element *elem, Rectangle r)
502 {
503 	int x;
504 	List *l;
505 	Prefab_Element *e;
506 	PElement *pelem;
507 
508 	x = r.min.x;
509 	pelem = lookupelement(elem);
510 	if(pelem == H)
511 		return;
512 	for(l=pelem->vfirst; l!=H && x<r.max.x; l=l->tail){
513 		e = *(Prefab_Element**)l->data;
514 		x += Dx(e->r);
515 	}
516 	pelem->vlast = l;
517 	R2R(elem->r, r);
518 }
519 
520 void
verticalclip(Prefab_Element * elem,Rectangle r)521 verticalclip(Prefab_Element *elem, Rectangle r)
522 {
523 	int y;
524 	List *l;
525 	Prefab_Element *e;
526 	PElement *pelem;
527 
528 	y = r.min.y;
529 	pelem = lookupelement(elem);
530 	if(pelem == H)
531 		return;
532 	for(l=pelem->vfirst; l!=H && y<r.max.y; l=l->tail){
533 		e = *(Prefab_Element**)l->data;
534 		y += Dy(e->r);
535 	}
536 	pelem->vlast = l;
537 	R2R(elem->r, r);
538 }
539 
540 void
scrollelement(Prefab_Element * elem,Point d,int * moved)541 scrollelement(Prefab_Element *elem, Point d, int *moved)
542 {
543 	if(lookupelement(elem) != H)
544 		(*elemfn[elem->kind].scroll)(elem, d, moved);
545 }
546 
547 void
textscroll(Prefab_Element * elem,Point d,int * moved)548 textscroll(Prefab_Element *elem, Point d, int *moved)
549 {
550 	PElement *pelem;
551 
552 	pelem = lookupelement(elem);
553 	if(pelem==H || (d.x==0 && d.y==0))
554 		return;
555 	pelem->drawpt = subpt(pelem->drawpt, d);
556 	*moved = 1;
557 }
558 
559 void
iconscroll(Prefab_Element * elem,Point d,int * moved)560 iconscroll(Prefab_Element *elem, Point d, int *moved)
561 {
562 	Point p;
563 	Image *i;
564 	PElement *pelem;
565 
566 	pelem = lookupelement(elem);
567 	if(pelem==H || elem->image==H || (d.x==0 && d.y==0))
568 		return;
569 	i = lookupimage(elem->image);
570 	if(i == nil)
571 		return;
572 	p = subpt(pelem->drawpt, d);
573 	if(i->repl == 0){
574 		if(p.x+Dx(i->clipr) < elem->r.max.x)
575 			p.x = elem->r.max.x - Dx(i->clipr);
576 		if(p.y+Dy(i->clipr) < elem->r.max.y)
577 			p.y = elem->r.max.y - Dy(i->clipr);
578 		if(p.x > elem->r.min.x)
579 			p.x = elem->r.min.x;
580 		if(p.y > elem->r.min.y)
581 			p.y = elem->r.min.y;
582 	}
583 	*moved = !eqpt(pelem->drawpt, p);
584 	pelem->drawpt = p;
585 }
586 
587 void
horizontalscroll(Prefab_Element * elem,Point d,int * moved)588 horizontalscroll(Prefab_Element *elem, Point d, int *moved)
589 {
590 	List *l;
591 	Prefab_Element *e;
592 	PElement *pelem;
593 
594 	pelem = lookupelement(elem);
595 	if(pelem==H || elem->kids==H || (d.x==0 && d.y==0))
596 		return;
597 	for(l=pelem->first; l!=H; l=l->tail){
598 		e = *(Prefab_Element**)l->data;
599 		translateelement(e, d);
600 	}
601 	for(l=pelem->first; l!=H; l=l->tail){
602 		e = *(Prefab_Element**)l->data;
603 		if(e->r.max.x > elem->r.min.x)
604 			break;
605 	}
606 	pelem->vfirst = l;
607 	pelem->vlast = l;
608 	for(; l!=H; l=l->tail){
609 		e = *(Prefab_Element**)l->data;
610 		pelem->vlast = l;
611 		if(e->r.min.x >= elem->r.max.x)
612 			break;
613 	}
614 	*moved = 1;
615 }
616 
617 void
verticalscroll(Prefab_Element * elem,Point d,int * moved)618 verticalscroll(Prefab_Element *elem, Point d, int *moved)
619 {
620 	List *l;
621 	Prefab_Element *e;
622 	PElement *pelem;
623 
624 	pelem = lookupelement(elem);
625 	if(pelem==H || elem->kids==H || (d.x==0 && d.y==0))
626 		return;
627 	for(l=pelem->first; l!=H; l=l->tail){
628 		e = *(Prefab_Element**)l->data;
629 		translateelement(e, d);
630 	}
631 	for(l=pelem->first; l!=H; l=l->tail){
632 		e = *(Prefab_Element**)l->data;
633 		if(e->r.max.y > elem->r.min.y)
634 			break;
635 	}
636 	pelem->vfirst = l;
637 	pelem->vlast = l;
638 	for(; l!=H; l=l->tail){
639 		e = *(Prefab_Element**)l->data;
640 		pelem->vlast = l;
641 		if(e->r.min.y >= elem->r.max.y)
642 			break;
643 	}
644 	*moved = 1;
645 }
646 
647 /*
648  * Make e visible within list.  Return value is whether any change was made;
649  * if so, must redraw (BUG: should probably do this here)
650  */
651 int
showelement(Prefab_Element * list,Prefab_Element * e)652 showelement(Prefab_Element *list, Prefab_Element *e)
653 {
654 	Point p;
655 	Prefab_Element *h, *t;
656 	PElement *plist;
657 	int moved;
658 
659 	p.x = p.y = 0;
660 	if(list->kids == H)
661 		return 0;
662 	plist = lookupelement(list);
663 	if(plist == H)
664 		return 0;
665 	h = *(Prefab_Element**)plist->first->data;
666 	t = *(Prefab_Element**)plist->last->data;
667 	if(list->kind == EHorizontal){
668 		p.x = (list->r.min.x+Dx(list->r)/2) - e->r.min.x;
669 		if(e->r.min.x < list->r.min.x){	/* scroll to right */
670 			if(e->r.max.x+p.x > list->r.max.x)
671 				p.x = list->r.min.x-e->r.min.x;
672 			if(h->r.min.x + p.x > list->r.min.x)
673 				p.x = list->r.min.x-h->r.min.x;
674 		}else if(e->r.max.x > list->r.max.x){	/* scroll to left */
675 			if(e->r.min.x+p.x < list->r.min.x)
676 				p.x = list->r.min.x-e->r.min.x;
677 			if(t->r.max.x + p.x < list->r.max.x)
678 				p.x = list->r.max.x-t->r.max.x;
679 		}else
680 			return 0;
681 	}else if(list->kind == EVertical){
682 		p.y = (list->r.min.y+Dy(list->r)/2) - e->r.min.y;
683 		if(e->r.min.y < list->r.min.y){	/* scroll towards bottom */
684 			if(e->r.max.y+p.y > list->r.max.y)
685 				p.y = list->r.min.y-e->r.min.y;
686 			if(h->r.min.y + p.y > list->r.min.y)
687 				p.y = list->r.min.y-h->r.min.y;
688 		}else if(e->r.max.y > list->r.max.y){	/* scroll towards top */
689 			if(e->r.min.y+p.y < list->r.min.y)
690 				p.y = list->r.min.y-e->r.min.y;
691 			if(t->r.max.y + p.y < list->r.max.y)
692 				p.y = list->r.max.y-t->r.max.y;
693 		}else
694 			return 0;
695 	}else
696 		return 0;
697 	if(p.x!=0 || p.y!=0){
698 		scrollelement(list, p, &moved);
699 		return 1;
700 	}
701 	return 0;
702 }
703 
704 PElement*
mkelement(Prefab_Environ * env,enum Elementtype t)705 mkelement(Prefab_Environ *env, enum Elementtype t)
706 {
707 	Heap *h;
708 	PElement *p;
709 
710 	h = heapz(TElement);
711 	p = H2D(PElement*, h);
712 	p->highlight = H;
713 	p->first = H;
714 	p->last = H;
715 	p->vfirst = H;
716 	p->vlast = H;
717 	p->nkids = 1;
718 	p->pkind = t;
719 	p->e.kind = t;
720 	p->e.environ = env;
721 	D2H(env)->ref++;
722 	return p;
723 }
724