shithub: libnate

Download patch

ref: 69e287559e1faed50092d64659c5bc02f9bf395c
parent: 27a143cd75a588c1bcd9a2f104e912d65db62a66
author: sirjofri <sirjofri@sirjofri.de>
date: Tue Feb 11 14:30:35 EST 2025

better size and scaling handling, makes everything "greedy"

--- a/n_box.c
+++ b/n_box.c
@@ -9,26 +9,47 @@
 char* NBox_Type = "NBox";
 
 static Rectangle
-box_calcsize(Nelem* nelem, Image* screen, Rectangle r)
+box_calcrect(Nelem* nelem, Image* screen, Rectangle r)
 {
+	Rectangle cr;
 	NBox* b = (NBox*)nelem;
 	GUARD(b);
-	Nelem *child = lgetfirst(&b->children);
-	if (!b->autosize) {
+	
+	if (!b->autosize && b->size.x >= 0 && b->size.y >= 0)
 		r.max = addpt(r.min, b->size);
+	
+	Nelem *child = lgetfirst(&b->children);
+	if (child) {
+		if (b->autosize)
+			r.max = addpt(r.min, ncalldesiredsize(child, screen));
+		
 		/* tell child its size (important!) */
-		ncallcalcsize(child, screen, r);
-		b->r = r;
-		return r;
+		cr = insetrect(r, b->borderwidth);
+		ncallcalcrect(child, screen, cr);
 	}
-	if (!child) {
-		/* greedy */
-		b->r = r;
-		return r;
+	b->slot.r = r;
+	return b->slot.r;
+}
+
+static Point
+box_desiredsize(Nelem *nelem, Image *screen)
+{
+	Point pt;
+	NBox *b = (NBox*)nelem;
+	GUARD(b);
+	
+	if (b->autosize) {
+		Nelem *child = lgetfirst(&b->children);
+		if (child)
+			pt = ncalldesiredsize(child, screen);
+		pt.x += 2*b->borderwidth;
+		pt.y += 2*b->borderwidth;
+		return pt;
 	}
-	Rectangle csize = ncallcalcsize(child, screen, r);
-	b->r = insetrect(csize, b->borderwidth);
-	return b->r;
+	if (b->size.x < 0 || b->size.y < 0)
+		sysfatal("error: box size < 0: %s %s", b->type, b->name);
+	
+	return b->size;
 }
 
 static void
@@ -42,20 +63,11 @@
 	if (!f)
 		return;
 
-	r = b->r;
-	if (b->autosize) {
-		r = b->r;
-	} else {
-		r = b->r;
-		r.max = addpt(r.min, b->size);
-	}
+	r = b->slot.r;
 
-	border(img, r, b->borderwidth, b->bordercolor, ZP);
+	if (b->borderwidth > 0)
+		border(img, r, b->borderwidth, b->bordercolor, ZP);
 	ncalldraw(f, img);
-	
-	if (nateborders) {
-		border(img, r, 1, ncolor.red, ZP);
-	}
 }
 
 static Nelem*
@@ -67,7 +79,7 @@
 	if (!b->hitfunc)
 		return nd_checkhit(nelem, screen, m);
 	
-	if (ptinrect(m.xy, b->r))
+	if (ptinrect(m.xy, b->slot.r))
 		return b;
 	return nil;
 }
@@ -116,7 +128,8 @@
 }
 
 static Nelemfunctions Nboxfunctions = {
-	.calcsize = box_calcsize,
+	.calcrect = box_calcrect,
+	.desiredsize = box_desiredsize,
 	.draw = box_draw,
 	.checkhit = box_checkhit,
 	.hit = box_hit,
@@ -144,7 +157,7 @@
 	b->Padding = box_padding;
 	
 	b->autosize = 0;
-	b->size = ZP;
+	b->size = Pt(-1, -1);
 	b->hitfunc = nil;
 	b->hitaux = nil;
 	b->borderwidth = 0;
--- a/n_box.h
+++ b/n_box.h
@@ -3,7 +3,7 @@
 typedef struct NBox NBox;
 struct NBox {
 	Nelem;
-	DECL_ACCESSOR_OneParam(NBox, Slot, Nelem*);
+	DECL_SLOTFUNC(NBox, Slot);
 	DECL_ACCESSOR_TwoParams(NBox, Border, int, Image*);
 	DECL_ACCESSOR_OneParam(NBox, AutoSize, int);
 	DECL_ACCESSOR_OneParam(NBox, Size, Point);
--- a/n_button.c
+++ b/n_button.c
@@ -11,14 +11,22 @@
 char *NButton_Type = "NButton";
 
 static Rectangle
-button_calcsize(Nelem *nelem, Image *screen, Rectangle r)
+button_calcrect(Nelem *nelem, Image *screen, Rectangle r)
 {
 	NButton *b = (NButton*)nelem;
 	GUARD(b);
-	b->r = ncallcalcsize(b->box, screen, r);
-	return b->r;
+	b->slot.r = ncallcalcrect(b->box, screen, r);
+	return b->slot.r;
 }
 
+static Point
+button_desiredsize(Nelem *nelem, Image *screen)
+{
+	NButton *b = (NButton*)nelem;
+	GUARD(b);
+	return ncalldesiredsize(b->box, screen);
+}
+
 static void
 button_draw(Nelem *nelem, Image *img)
 {
@@ -55,7 +63,8 @@
 }
 
 static Nelemfunctions Nbuttonfunctions = {
-	.calcsize = button_calcsize,
+	.calcrect = button_calcrect,
+	.desiredsize = button_desiredsize,
 	.draw = button_draw,
 	.checkhit = button_checkhit,
 	.free = button_free,
@@ -63,7 +72,7 @@
 };
 
 static NButton*
-button_slot(Nelem* child)
+button_slot(Nslot info, Nelem* child)
 {
 	if (child == nc_get())
 		nc_pop();
@@ -70,7 +79,7 @@
 	NButton *c = (NButton*)nc_get();
 	GUARD(c);
 	nc_push(c->box);
-	c->box->Slot(child);
+	c->box->Slot(info, child);
 	nc_pop();
 	return c;
 }
@@ -136,7 +145,7 @@
 	NButton *b = MakeNelem(NButton, NButton_Type, &Nbuttonfunctions, name, 0);
 	
 	b->box = New_Box(name)
-		->Slot(
+		->Slot(NSlot(),
 			NAssign(NLabel, &b->label, New_Label(nil))
 			->Margin(NMargin2(5, 2))
 		);
--- a/n_button.h
+++ b/n_button.h
@@ -3,7 +3,7 @@
 typedef struct NButton NButton;
 struct NButton {
 	Nelem;
-	DECL_ACCESSOR_OneParam(NButton, Slot, Nelem*);
+	DECL_SLOTFUNC(NButton, Slot);
 	DECL_ACCESSOR_TwoParams(NButton, Border, int, Image*);
 	DECL_ACCESSOR_OneParam(NButton, AutoSize, int);
 	DECL_ACCESSOR_TwoParams(NButton, OnClick, OnclickHandler, void*);
--- a/n_hbox.c
+++ b/n_hbox.c
@@ -11,39 +11,110 @@
 typedef struct csizep csizep;
 struct csizep {
 	Rectangle crect;
-	Rectangle frect;
 	Image *screen;
+	int autowidth;
+	int fillheight;
 };
 
 static void
-hbox_childsize(Nelem* nelem, int, void *aux)
+childsize(Nelem* nelem, int, void *aux)
 {
+	Point pt;
+	Rectangle r;
 	csizep *p = (csizep*)aux;
-	Rectangle r = ncallcalcsize(nelem, p->screen, p->crect);
-	combinerect(&p->frect, r);
+	
+	/* only call if no fill at all */
+	if (!nelem->slot.fill)
+		pt = ncalldesiredsize(nelem, p->screen);
+	
+	if (nelem->slot.fill&FILLX)
+		pt.x = p->autowidth;
+	if (nelem->slot.fill&FILLY)
+		pt.y = p->fillheight;
+	
+	r = p->crect;
+	r.max = addpt(r.min, pt);
+	ncallcalcrect(nelem, p->screen, r);
 	p->crect.min.x = r.max.x;
 }
 
+typedef struct dprepassp dprepassp;
+struct dprepassp {
+	Image *screen;
+	int numfill;
+	int fixedwidth;
+};
+
+static void
+sizeprepass(Nelem *nelem, int, void *aux)
+{
+	Point pt;
+	dprepassp *p = (dprepassp*)aux;
+	if (nelem->slot.fill & FILLX) {
+		p->numfill++;
+		return;
+	}
+	pt = ncalldesiredsize(nelem, p->screen);
+	p->fixedwidth += pt.x;
+}
+
 static Rectangle
-hbox_calcsize(Nelem* nelem, Image* screen, Rectangle r)
+hbox_calcrect(Nelem* nelem, Image* screen, Rectangle r)
 {
-	csizep params;
+	csizep cp;
+	dprepassp pp;
 	NHBox* b = (NHBox*)nelem;
 	GUARD(b);
 	
-	params.screen = screen;
-	params.crect = r;
-	params.frect.min = r.min;
-	params.frect.max.x = r.min.x;
-	params.frect.max.y = b->autoheight ? r.min.y : r.max.y;
+	nelem->slot.r = r;
 	
-	lforeach(&b->children, hbox_childsize, &params);
+	pp.screen = screen;
+	pp.numfill = 0;
+	pp.fixedwidth = 0;
+	
+	lforeach(&b->children, sizeprepass, &pp);
+	
+	cp.screen = screen;
+	cp.crect = r;
+	cp.fillheight = Dy(r);
+	cp.autowidth = (Dx(r) - pp.fixedwidth) / pp.numfill;
+	
+	lforeach(&b->children, childsize, &cp);
 
-	nelem->r = params.frect;
-	return nelem->r;
+	return nelem->slot.r;
 }
 
+typedef struct cdsizep cdsizep;
+struct cdsizep {
+	Image *screen;
+	Point size;
+};
+
 static void
+dsizechild(Nelem *el, int, void *aux)
+{
+	Point t;
+	cdsizep *p = (cdsizep*)aux;
+	t = ncalldesiredsize(el, p->screen);
+	if (t.y > p->size.y)
+		p->size.y = t.y;
+	p->size.x += t.x;
+}
+
+static Point
+hbox_desiredsize(Nelem *nelem, Image *screen)
+{
+	cdsizep p;
+	NHBox *b = (NHBox*)nelem;
+	GUARD(b);
+	
+	p.screen = screen;
+	p.size = ZP;
+	lforeach(&b->children, dsizechild, &p);
+	return p.size;
+}
+
+static void
 hbox_childdraw(Nelem* elem, int, void *aux)
 {
 	ncalldraw(elem, (Image*)aux);
@@ -59,13 +130,25 @@
 }
 
 static Nelemfunctions Nhboxfunctions = {
-	.calcsize = hbox_calcsize,
+	.calcrect = hbox_calcrect,
+	.desiredsize = hbox_desiredsize,
 	.draw = hbox_draw,
 };
 
 DEF_SLOTFUNC(NHBox, hbox_slot);
 
-DEF_ACCESSOR_OneParam(NHBox, hbox_autoheight, int, autoheight);
+static NHBox*
+hbox_autoheight(int h)
+{
+	NHBox *b = (NHBox*)nc_get();
+	GUARD(b);
+	if (h)
+		b->slot.fill |= FILLX;
+	else
+		if (b->slot.fill & FILLX)
+			b->slot.fill -= FILLX;
+	return b;
+}
 
 NHBox*
 New_HBox(char *name)
--- a/n_hbox.h
+++ b/n_hbox.h
@@ -3,7 +3,7 @@
 typedef struct NHBox NHBox;
 struct NHBox {
 	Nelem;
-	DECL_ACCESSOR_OneParam(NHBox, Slot, Nelem*);
+	DECL_SLOTFUNC(NHBox, Slot);
 	DECL_ACCESSOR_OneParam(NHBox, AutoHeight, int);
 	
 	// private members
--- a/n_image.c
+++ b/n_image.c
@@ -9,7 +9,7 @@
 char *NImage_Type = "NImage";
 
 static Rectangle
-image_calcsize(Nelem *nelem, Image*, Rectangle r)
+image_calcrect(Nelem *nelem, Image*, Rectangle r)
 {
 	Point p;
 	NImage *i = (NImage*)nelem;
@@ -16,8 +16,8 @@
 	GUARD(i);
 	
 	if (!i->autosize) {
-		i->r = r;
-		return i->r;
+		i->slot.r = r;
+		return i->slot.r;
 	}
 	
 	p = ZP;
@@ -24,10 +24,21 @@
 	if (i->image)
 		p = Pt(Dx(i->image->r), Dy(i->image->r));
 	r.max = addpt(r.min, p);
-	i->r = r;
-	return i->r;
+	i->slot.r = r;
+	return i->slot.r;
 }
 
+static Point
+image_desiredsize(Nelem *nelem, Image*)
+{
+	NImage *i = (NImage*)nelem;
+	GUARD(i);
+	
+	if (!i->image)
+		return ZP;
+	return subpt(i->image->r.max, i->image->r.min);
+}
+
 static void
 image_draw(Nelem *nelem, Image *img)
 {
@@ -35,14 +46,15 @@
 	GUARD(i);
 	
 	if (!i->image) {
-		draw(img, i->r, display->white, nil, ZP);
+		draw(img, i->slot.r, display->white, nil, ZP);
 		return;
 	}
-	draw(img, i->r, i->image, nil, i->offset);
+	draw(img, i->slot.r, i->image, nil, i->offset);
 }
 
 static Nelemfunctions Nimagefunctions = {
-	.calcsize = image_calcsize,
+	.calcrect = image_calcrect,
+	.desiredsize = image_desiredsize,
 	.draw = image_draw,
 };
 
--- a/n_label.c
+++ b/n_label.c
@@ -19,38 +19,82 @@
 }
 
 static Rectangle
-label_calcsize(Nelem* nelem, Image*, Rectangle r)
+label_calcrect(Nelem* nelem, Image*, Rectangle r)
 {
 	NLabel* l = (NLabel*)nelem;
 	GUARD(l);
 	
-	r.max = stringsize(l->font, getlabelstr(l));
-	r.max.x += l->margin.left + l->margin.right;
-	r.max.y += l->margin.top + l->margin.bottom;
-	r.max = addpt(r.min, r.max);
-	l->r = r;
+	l->slot.r = r;
 	return r;
 }
 
+static Point
+label_desiredsize(Nelem *nelem, Image*)
+{
+	Point pt;
+	NLabel *l = (NLabel*)nelem;
+	GUARD(l);
+	
+	pt = stringsize(l->font, getlabelstr(l));
+	pt.x += l->margin.left + l->margin.right;
+	pt.y += l->margin.top + l->margin.bottom;
+	return pt;
+}
+
 static void
 label_draw(Nelem* nelem, Image* img)
 {
 	char* str;
 	Rectangle r;
+	Point sz, p;
 	NLabel* l = (NLabel*)nelem;
 	GUARD(l);
 	
 	str = getlabelstr(l);
 	
-	r = l->r;
-	r.min.x += l->margin.left;
-	r.min.y += l->margin.top;
+	r = l->slot.r;
+	sz = stringsize(l->font, str);
 	
-	string(img, r.min, l->color, ZP, l->font, str);
-	
-	if (nateborders) {
-		border(img, l->r, 1, ncolor.blue, ZP);
+	switch (l->align) {
+	case TOPLEFT:
+		p.x = r.min.x + l->margin.left;
+		p.y = r.min.y + l->margin.top;
+		break;
+	case TOP:
+		p.y = r.min.y + l->margin.top;
+		p.x = r.min.x + (Dx(r) - sz.x)/2;
+		break;
+	case TOPRIGHT:
+		p.x = r.max.x - l->margin.right - sz.x;
+		p.y = r.min.y + l->margin.top;
+		break;
+	case LEFT:
+		p.x = r.min.x + l->margin.left;
+		p.y = r.min.y + (Dy(r) - sz.y)/2;
+		break;
+	case CENTER:
+		p.x = r.min.x + (Dx(r) - sz.x)/2;
+		p.y = r.min.y + (Dy(r) - sz.y)/2;
+		break;
+	case RIGHT:
+		p.x = r.max.x - l->margin.right - sz.x;
+		p.y = r.min.y + (Dy(r) - sz.y)/2;
+		break;
+	case BOTLEFT:
+		p.x = r.min.x + l->margin.left;
+		p.y = r.max.y - l->margin.bottom - sz.y;
+		break;
+	case BOTTOM:
+		p.x = r.min.x + (Dx(r) - sz.x)/2;
+		p.y = r.max.y - l->margin.bottom - sz.y;
+		break;
+	case BOTRIGHT:
+		p.x = r.max.x - l->margin.right - sz.x;
+		p.y = r.max.y - l->margin.bottom - sz.y;
+		break;
 	}
+	
+	string(img, p, l->color, ZP, l->font, str);
 }
 
 static char*
@@ -62,7 +106,8 @@
 }
 
 static Nelemfunctions Nlabelfunctions = {
-	.calcsize = label_calcsize,
+	.calcrect = label_calcrect,
+	.desiredsize = label_desiredsize,
 	.draw = label_draw,
 	.getname = label_getname,
 };
@@ -72,6 +117,7 @@
 DEF_ACCESSOR_OneParam(NLabel, lfont, Font*, font);
 DEF_ACCESSOR_OneParam(NLabel, lcolor, Image*, color);
 DEF_ACCESSOR_OneParam(NLabel, lmargin, Nmargin, margin);
+DEF_ACCESSOR_OneParam(NLabel, lalign, Nalign, align);
 
 NLabel*
 New_Label(char *name)
@@ -83,6 +129,7 @@
 	e->Font = lfont;
 	e->Color = lcolor;
 	e->Margin = lmargin;
+	e->Align = lalign;
 	
 	e->label = nil;
 	e->labelfunc = nil;
--- a/n_label.h
+++ b/n_label.h
@@ -8,6 +8,7 @@
 	DECL_ACCESSOR_OneParam(NLabel, Font, Font*);
 	DECL_ACCESSOR_OneParam(NLabel, Color, Image*);
 	DECL_ACCESSOR_OneParam(NLabel, Margin, Nmargin);
+	DECL_ACCESSOR_OneParam(NLabel, Align, Nalign);
 	
 	// private members
 	char* label;
@@ -15,6 +16,7 @@
 	Font* font;
 	Image* color;
 	Nmargin margin;
+	Nalign align;
 };
 
 NLabel* New_Label(char*);
--- a/n_vbox.c
+++ b/n_vbox.c
@@ -11,39 +11,109 @@
 typedef struct csizep csizep;
 struct csizep {
 	Rectangle crect;
-	Rectangle frect;
 	Image *screen;
+	int autoheight;
+	int fillwidth;
 };
 
 static void
-vbox_childsize(Nelem* nelem, int, void *aux)
+childsize(Nelem* nelem, int, void *aux)
 {
+	Point pt;
+	Rectangle r;
 	csizep *p = (csizep*)aux;
-	Rectangle r = ncallcalcsize(nelem, p->screen, p->crect);
-	combinerect(&p->frect, r);
+	
+	/* only call if no fill at all */
+	if (!nelem->slot.fill)
+		pt = ncalldesiredsize(nelem, p->screen);
+	
+	if (nelem->slot.fill&FILLX)
+		pt.x = p->fillwidth;
+	if (nelem->slot.fill&FILLY)
+		pt.y = p->autoheight;
+	
+	r = p->crect;
+	r.max = addpt(r.min, pt);
+	ncallcalcrect(nelem, p->screen, r);
 	p->crect.min.y = r.max.y;
 }
 
+typedef struct dprepassp dprepassp;
+struct dprepassp {
+	Image *screen;
+	int numfill;
+	int fixedheight;
+};
+
+static void
+sizeprepass(Nelem *nelem, int, void *aux)
+{
+	dprepassp *p = (dprepassp*)aux;
+	if (nelem->slot.fill & FILLY) {
+		p->numfill++;
+		return;
+	}
+	Rectangle r = ncalldesiredsize(nelem, p->screen);
+	p->fixedheight += Dy(r);
+}
+
 static Rectangle
-vbox_calcsize(Nelem* nelem, Image* img, Rectangle r)
+vbox_calcrect(Nelem* nelem, Image* img, Rectangle r)
 {
-	csizep params;
+	csizep cp;
+	dprepassp pp;
 	NVBox* b = (NVBox*)nelem;
 	GUARD(b);
 	
-	params.crect = r;
-	params.screen = img;
-	params.frect.min = r.min;
-	params.frect.max.x = b->autowidth ? r.min.x : r.max.x;
-	params.frect.max.y = r.min.y;
+	b->slot.r = r;
 	
-	lforeach(&b->children, vbox_childsize, &params);
+	pp.screen = screen;
+	pp.numfill = 0;
+	pp.fixedheight = 0;
 	
-	b->r = params.frect;
-	return b->r;
+	lforeach(&b->children, sizeprepass, &pp);
+	
+	cp.crect = r;
+	cp.screen = img;
+	cp.fillwidth = Dx(r);
+	cp.autoheight = (Dy(r) - pp.fixedheight) / pp.numfill;
+	
+	lforeach(&b->children, childsize, &cp);
+	
+	return b->slot.r;
 }
 
+typedef struct cdsizep cdsizep;
+struct cdsizep {
+	Image *screen;
+	Point size;
+};
+
 static void
+dsizechild(Nelem *el, int, void *aux)
+{
+	Point t;
+	cdsizep *p = (cdsizep*)aux;
+	t = ncalldesiredsize(el, p->screen);
+	if (t.x > p->size.x)
+		p->size.x = t.x;
+	p->size.y += t.y;
+}
+
+static Point
+vbox_desiredsize(Nelem *nelem, Image *screen)
+{
+	cdsizep p;
+	NVBox *b = (NVBox*)nelem;
+	GUARD(b);
+	
+	p.screen = screen;
+	p.size = ZP;
+	lforeach(&b->children, dsizechild, &p);
+	return p.size;
+}
+
+static void
 vbox_childdraw(Nelem* elem, int, void *aux)
 {
 	ncalldraw(elem, (Image*)aux);
@@ -59,7 +129,8 @@
 }
 
 static Nelemfunctions Nvboxfunctions = {
-	.calcsize = vbox_calcsize,
+	.calcrect = vbox_calcrect,
+	.desiredsize = vbox_desiredsize,
 	.draw = vbox_draw,
 };
 
--- a/n_vbox.h
+++ b/n_vbox.h
@@ -3,7 +3,7 @@
 typedef struct NVBox NVBox;
 struct NVBox {
 	Nelem;
-	DECL_ACCESSOR_OneParam(NVBox, Slot, Nelem*);
+	DECL_SLOTFUNC(NVBox, Slot);
 	DECL_ACCESSOR_OneParam(NVBox, AutoWidth, int);
 	
 	// private members
--- a/n_window.c
+++ b/n_window.c
@@ -9,7 +9,7 @@
 char* NWindow_Type = "NWindow";
 
 static Rectangle
-wcalcsize(Nelem* nelem, Image* screen, Rectangle r)
+wcalcrect(Nelem* nelem, Image* screen, Rectangle r)
 {
 	Nelem *f;
 	NWindow *w = (NWindow*)nelem;
@@ -16,8 +16,8 @@
 	GUARD(w);
 	f = lgetfirst(&w->children);
 	if (f)
-		ncallcalcsize(f, screen, screen->r);
-	nelem->r = r;
+		ncallcalcrect(f, screen, screen->r);
+	nelem->slot.r = r;
 	return screen->r;
 }
 
@@ -40,7 +40,7 @@
 }
 
 static Nelemfunctions Nwindowfunctions = {
-	.calcsize = wcalcsize,
+	.calcrect = wcalcrect,
 	.draw = wdraw,
 	.checkhit = nd_checkhit,
 	.free = wfree,
--- a/n_window.h
+++ b/n_window.h
@@ -3,7 +3,7 @@
 typedef struct NWindow NWindow;
 struct NWindow {
 	Nelem;
-	DECL_ACCESSOR_OneParam(NWindow, Slot, Nelem*);
+	DECL_SLOTFUNC(NWindow, Slot);
 	DECL_ACCESSOR(NWindow, MakeRoot);
 };
 
--- a/nate.c
+++ b/nate.c
@@ -184,13 +184,13 @@
 }
 
 Rectangle
-ncallcalcsize(Nelem* nelem, Image* dst, Rectangle r)
+ncallcalcrect(Nelem* nelem, Image* dst, Rectangle r)
 {
 	assert(nelem);
 	
-	if (nelem->funcs && nelem->funcs->calcsize) {
-		r = nelem->funcs->calcsize(nelem, dst, r);
-		//fprint(2, "calcsize %s: %R\n", nelem->type, r);
+	if (nelem->funcs && nelem->funcs->calcrect) {
+		r = nelem->funcs->calcrect(nelem, dst, r);
+		ndebugprint(nelem, "calcrect %P ← %R\n", subpt(r.max, r.min), r);
 		return r;
 	}
 	
@@ -197,6 +197,19 @@
 	return r;
 }
 
+Point
+ncalldesiredsize(Nelem *nelem, Image *dst)
+{
+	Point p;
+	assert(nelem);
+	if (nelem->funcs && nelem->funcs->desiredsize) {
+		p = nelem->funcs->desiredsize(nelem, dst);
+		ndebugprint(nelem, "desired: %P\n", p);
+		return p;
+	}
+	return ZP;
+}
+
 void
 ncalldraw(Nelem* nelem, Image* dst)
 {
@@ -205,13 +218,13 @@
 	
 	if (nelem->funcs && nelem->funcs->draw) {
 		if (natetracedraw && natedebugfd >= 0)
-			fprint(natedebugfd, "DRAW: %s\n", nelem->type);
+			ndebugprint(nelem, "draw: %R\n", nelem->slot.r);
 		or = dst->clipr;
-		replclipr(dst, 0, nelem->r);
+		replclipr(dst, 0, nelem->slot.r);
 		nelem->funcs->draw(nelem, dst);
 		replclipr(dst, 0, or);
 		if (nateborders)
-			border(dst, nelem->r, 1, ncolor.red, ZP);
+			border(dst, nelem->slot.r, 1, ncolor.red, ZP);
 	}
 }
 
@@ -278,10 +291,23 @@
 		return;
 	
 	if (all)
-		ncallcalcsize(rootelem, screen, screen->r);
+		ncallcalcrect(rootelem, screen, screen->r);
 	ncalldraw(rootelem, screen);
 }
 
+void
+ndebugprint(Nelem *el, char *fmt, ...)
+{
+	if (natedebugfd < 0)
+		return;
+	fprint(natedebugfd, "%15s %10s  ", el->name, el->type);
+	
+	va_list args;
+	va_start(args, fmt);
+	vfprint(natedebugfd, fmt, args);
+	va_end(args);
+}
+
 int
 natemouseevent(Mouse m)
 {
@@ -291,9 +317,7 @@
 	if (!rootelem)
 		return 0;
 	
-	r = ncallcalcsize(rootelem, screen, screen->r);
-	
-	if (!ptinrect(m.xy, r))
+	if (!ptinrect(m.xy, rootelem->slot.r))
 		return 0;
 	
 	nctracehitlevel = natetracehit - 1;
@@ -302,4 +326,12 @@
 		return 0;
 	
 	return ncallhit(el, m);
+}
+
+Nslot
+NSlot()
+{
+	Nslot s;
+	s.fill = FILLX|FILLY;
+	return s;
 }
--- a/nate.h
+++ b/nate.h
@@ -1,6 +1,7 @@
 #pragma lib "libnate.a"
 
 typedef struct Nelem Nelem;
+typedef struct Nslot Nslot;
 typedef struct Nelemfunctions Nelemfunctions;
 typedef struct Nlist Nlist;
 typedef struct Nlistelem Nlistelem;
@@ -34,6 +35,18 @@
 	int bottom;
 };
 
+typedef enum {
+	TOPLEFT,
+	TOP,
+	TOPRIGHT,
+	LEFT,
+	CENTER,
+	RIGHT,
+	BOTLEFT,
+	BOTTOM,
+	BOTRIGHT,
+} Nalign;
+
 #define NMargin(l, t, r, b) ((Nmargin){l, t, r, b})
 #define NMargin2(x, y) ((Nmargin){x, y, x, y})
 
@@ -43,6 +56,7 @@
 
 void nateinit(void);
 void nateredraw(int all);
+void ndebugprint(Nelem*, char*, ...);
 
 int natemouseevent(Mouse);
 
@@ -65,16 +79,28 @@
 /* end user functions */
 /**********************/
 
+#define FILLX (1<<0)
+#define FILLY (1<<1)
+
+struct Nslot {
+	Rectangle r;
+	Nalign align;
+	int fill;
+};
+
+Nslot NSlot(void);
+
 struct Nelem {
 	char *type;
 	char *name;
-	Rectangle r;
+	Nslot slot;
 	Nlist children;
 	int nchildren; /* -1=inf, 0=none, n otherwise */
 	Nelemfunctions* funcs;
 };
 struct Nelemfunctions {
-	Rectangle (*calcsize)(Nelem*, Image*, Rectangle);
+	Rectangle (*calcrect)(Nelem*, Image*, Rectangle);
+	Point (*desiredsize)(Nelem*, Image*);
 	void (*draw)(Nelem*, Image*);
 	Nelem* (*checkhit)(Nelem*, Image*, Mouse);
 	int (*hit)(Nelem*, Mouse);
@@ -107,7 +133,8 @@
 /********************/
 
 // calls on Nelem
-Rectangle ncallcalcsize(Nelem*, Image*, Rectangle);
+Rectangle ncallcalcrect(Nelem*, Image*, Rectangle);
+Point ncalldesiredsize(Nelem*, Image*);
 void ncalldraw(Nelem*, Image*);
 Nelem* ncallcheckhit(Nelem*, Image*, Mouse);
 int ncallhit(Nelem*, Mouse);
@@ -134,6 +161,7 @@
 /* more syntactic sugar */
 /************************/
 
+#define DECL_SLOTFUNC(Type, Acc) Type* (*Acc)(Nslot, Nelem*)
 #define DECL_ACCESSOR(Type, Acc) Type* (*Acc)(void)
 #define DECL_ACCESSOR_OneParam(Type, Acc, T1) Type* (*Acc)(T1)
 #define DECL_ACCESSOR_TwoParams(Type, Acc, T1, T2) Type* (*Acc)(T1, T2)
--- a/nate_construct.c
+++ b/nate_construct.c
@@ -71,7 +71,7 @@
 	
 	border(p->screen, r, 1, ncolor.green, ZP);
 	
-	if (!ptinrect(p->mouse.xy, elem->r))
+	if (!ptinrect(p->mouse.xy, elem->slot.r))
 		return;
 	
 	e = ncallcheckhit(elem, p->screen, p->mouse);
--- a/nate_construct.h
+++ b/nate_construct.h
@@ -28,12 +28,13 @@
 // syntactic sugar: default slot
 #define DEF_SLOTFUNC(Type, Func) \
 	static Type* \
-	Func(Nelem *el) \
+	Func(Nslot info, Nelem *el) \
 	{ \
 		if (el == nc_get()) \
 			nc_pop(); \
 		Type *b = (Type*)nc_get(); \
 		GUARD(b); \
+		el->slot = info; \
 		nc_addchild(b, el); \
 		return b; \
 	}
--- a/test/ntest.c
+++ b/test/ntest.c
@@ -65,7 +65,7 @@
 	
 	/* debug nate */
 	nateborders = 1;
-	natetracehit = 1;
+	//natetracehit = 1;
 	natetracedraw = 1;
 	natedebugfd = 2;
 	
@@ -76,51 +76,48 @@
 	Image* green = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DGreen);
 	Image* blue = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DBlue);
 	
-	NAssign(NWindow, &mainwindow, New_Window(nil))
+	NAssign(NWindow, &mainwindow, New_Window("window"))
 	->MakeRoot()
-	->Slot(
-		New_VBox(nil)
-		->AutoWidth(1)
-		->Slot(
-			New_HBox("first")
-			->AutoHeight(1)
-			->Slot(
-				NAssign(NBox, &box1, New_Box(nil))
+	->Slot(NSlot(),
+		New_VBox("outer_vbox")
+		->Slot(NSlot(),
+			New_HBox("first_hbox")
+			->Slot(NSlot(),
+				NAssign(NBox, &box1, New_Box("first_box"))
 				->Border(1, green)
 				->AutoSize(1)
 				->Padding(NMargin2(5, 3))
 				->OnClick(callclick, nil)
-				->Slot(
-					New_Label(nil)
+				->Slot(NSlot(),
+					New_Label("first_label")
 					->LabelFunc(getlabel)
 					->Margin(NMargin2(5, 5))
 				)
 			)
-			->Slot(
-				NAssign(NBox, &box2, New_Box(nil))
+			->Slot(NSlot(),
+				NAssign(NBox, &box2, New_Box("second_box"))
 				->Border(1, blue)
-				->AutoSize(1)
 				->OnClick(callclick, nil)
-				->Slot(
-					New_Label(nil)
+				->Slot(NSlot(),
+					New_Label("second_label")
 					->Label("DEF")
+					->Align(BOTRIGHT)
 					->Margin(NMargin2(5, 5))
 				)
 			)
-			->Slot(
-				New_Box("imagebox")
+			->Slot(NSlot(),
+				New_Box("image_box")
 				->Size(Pt(50, 50))
-				->Slot(
-					New_Image(nil)
+				->Slot(NSlot(),
+					New_Image("image")
 					->Image(display->black)
 				)
 			)
 		)
-		->Slot(
-			New_HBox("second")
-			->AutoHeight(1)
-			->Slot(
-				New_Label(nil)
+		->Slot(NSlot(),
+			New_HBox("second_hbox")
+			->Slot(NSlot(),
+				New_Label("third_label")
 				->Label("abc")
 			)
 		)