shithub: libnate

Download patch

ref: 228a7b0eeda3531eb0bf0efa2211e4532af69681
parent: a0b15f88e6274b4892061a7dfe790d85eb211078
author: sirjofri <sirjofri@sirjofri.de>
date: Sun Feb 9 10:32:47 EST 2025

next version of nate, reduce custom functions in favor of generic ones

future:
- get rid of getchildren function

--- a/n_box.c
+++ b/n_box.c
@@ -8,27 +8,30 @@
 #define N_TYPE NBox_Type
 char* NBox_Type = "NBox";
 
-Point
-box_calcsize(Nelem* nelem, Image* screen)
+Rectangle
+box_calcsize(Nelem* nelem, Image* screen, Rectangle r)
 {
 	NBox* b = (NBox*)nelem;
 	GUARD(b);
 	if (!b->sizetocontent) {
-		return b->size;
+		r.max = addpt(r.min, b->size);
+		b->r = r;
+		return r;
 	}
 	if (!lgetfirst(&b->child)) {
-		return Pt(0, 0);
+		b->r = r;
+		return r;
 	}
-	Point csize = ncallcalcsize(lgetfirst(&b->child), screen);
-	Point ssize = Pt(b->borderwidth, b->borderwidth);
-	csize = addpt(csize, addpt(ssize, ssize));
-	return csize;
+	Rectangle csize = ncallcalcsize(lgetfirst(&b->child), screen, r);
+	b->r = insetrect(csize, b->borderwidth);
+	return b->r;
 }
 
-void
-box_draw(Nelem* nelem, Image* img, Rectangle r)
+static void
+box_draw(Nelem* nelem, Image* img)
 {
 	Nelem* f;
+	Rectangle r;
 	NBox* b = (NBox*)nelem;
 	GUARD(b);
 	f = lgetfirst(&b->child);
@@ -35,46 +38,33 @@
 	if (!f)
 		return;
 
+	r = b->r;
 	if (b->sizetocontent) {
-		r.max = addpt(r.min, ncallcalcsize(nelem, img));
+		r = b->r;
 	} else {
+		r = b->r;
 		r.max = addpt(r.min, b->size);
 	}
 
 	border(img, r, b->borderwidth, b->bordercolor, ZP);
-	ncalldraw(f, img, insetrect(r, b->borderwidth));
+	ncalldraw(f, img);
+	
+	if (nateborders) {
+		border(img, r, 1, ncolor.red, ZP);
+	}
 }
 
-Nelem*
-box_checkhit(Nelem* nelem, Image* screen, Rectangle rect, Mouse m)
+static Nelem*
+box_checkhit(Nelem *nelem, Image *screen, Mouse m)
 {
-	NBox* b = (NBox*)nelem;
-	Nelem* r;
-	Nelem* ch;
-	Point size;
-	Rectangle cr;
+	NBox *b = (NBox*)nelem;
 	GUARD(b);
 	
-	if (!(ch = lgetfirst(&b->child))) {
-		if (m.buttons&1 && b->hitfunc) {
-			return b;
-		}
-		return nil;
-	}
+	if (!b->hitfunc)
+		return nd_checkhit(nelem, screen, m);
 	
-	size = ncallcalcsize(ch, screen);
-	cr.min = rect.min;
-	cr.max = addpt(cr.min, size);
-	
-	if (ptinrect(m.xy, cr)) {
-		r = ncallcheckhit(ch, screen, cr, m);
-		if (r)
-			return r;
-	}
-	
-	if (m.buttons&1 && b->hitfunc) {
+	if (ptinrect(m.xy, b->r))
 		return b;
-	}
 	return nil;
 }
 
@@ -110,6 +100,18 @@
 	return &b->child;
 }
 
+static char*
+box_getname(Nelem *nelem)
+{
+	Nelem *ch;
+	NBox *b = (NBox*)nelem;
+	GUARD(b);
+	ch = lgetfirst(&b->child);
+	if (!(ch && ch->funcs && ch->funcs->getname))
+		return b->name;
+	return ch->funcs->getname(ch);
+}
+
 static Nelemfunctions Nboxfunctions = {
 	.calcsize = box_calcsize,
 	.draw = box_draw,
@@ -117,6 +119,7 @@
 	.hit = box_hit,
 	.free = box_free,
 	.getchildren = box_getchildren,
+	.getname = box_getname,
 };
 
 NBox*
@@ -132,58 +135,23 @@
 	return b;
 }
 
-NBox*
-box_border(int bwidth, Image* col)
-{
-	NBox* b = (NBox*)nc_get();
-	GUARD(b);
-	b->borderwidth = bwidth;
-	if (col)
-		b->bordercolor = col;
-	return b;
-}
-NBox*
-box_sizetocontent(int stc)
-{
-	NBox* b = (NBox*)nc_get();
-	GUARD(b);
-	b->sizetocontent = stc;
-	return b;
-}
+DEF_ACCESSOR_TwoParams(NBox, box_border, int, borderwidth, Image*, bordercolor);
+DEF_ACCESSOR_OneParam(NBox, box_sizetocontent, int, sizetocontent);
+DEF_ACCESSOR_OneParam(NBox, box_size, Point, size);
+DEF_ACCESSOR_OneParam(NBox, box_padding, Nmargin, padding);
+DEF_ACCESSOR_TwoParams(NBox, box_onclick, OnclickHandler, hitfunc, void*, hitaux);
 
-static NBox*
-box_size(Point size)
-{
-	NBox *b = (NBox*)nc_get();
-	GUARD(b);
-	b->size = size;
-	return b;
-}
-
 NBox*
-box_onclick(int (*f)(Mouse, Nelem*, void*), void* aux)
+New_Box(char *name)
 {
-	NBox* b = (NBox*)nc_get();
-	GUARD(b);
-	b->hitfunc = f;
-	b->hitaux = aux;
-	return b;
-}
-
-NBox*
-New_Box(void)
-{
-	NBox* b = malloc(sizeof(NBox));
-	assert(b);
+	NBox *b = MakeNelem(NBox, NBox_Type, &Nboxfunctions, name);
 	
-	b->type = NBox_Type;
-	b->funcs = &Nboxfunctions;
-	
 	b->Slot = box_slot;
 	b->Border = box_border;
 	b->SizeToContent = box_sizetocontent;
 	b->Size = box_size;
 	b->OnClick = box_onclick;
+	b->Padding = box_padding;
 	
 	linit(&b->child);
 	b->sizetocontent = 0;
--- a/n_box.h
+++ b/n_box.h
@@ -7,7 +7,8 @@
 	DECL_ACCESSOR_TwoParams(NBox, Border, int, Image*);
 	DECL_ACCESSOR_OneParam(NBox, SizeToContent, int);
 	DECL_ACCESSOR_OneParam(NBox, Size, Point);
-	DECL_ACCESSOR_TwoParams(NBox, OnClick, int (*f)(Mouse, Nelem*, void*), void*);
+	DECL_ACCESSOR_TwoParams(NBox, OnClick, OnclickHandler, void*);
+	DECL_ACCESSOR_OneParam(NBox, Padding, Nmargin);
 	
 	// private members
 	Nlist child;
@@ -14,9 +15,10 @@
 	Point size;
 	int sizetocontent;
 	int borderwidth;
+	Nmargin padding;
 	Image* bordercolor;
 	int (*hitfunc)(Mouse, Nelem*, void*);
 	void* hitaux;
 };
 
-NBox* New_Box(void);
+NBox* New_Box(char*);
--- a/n_button.c
+++ b/n_button.c
@@ -10,28 +10,29 @@
 #define N_TYPE NButton_Type
 char *NButton_Type = "NButton";
 
-static Point
-button_calcsize(Nelem *nelem, Image *screen)
+static Rectangle
+button_calcsize(Nelem *nelem, Image *screen, Rectangle r)
 {
 	NButton *b = (NButton*)nelem;
 	GUARD(b);
-	return b->box->funcs->calcsize(b->box, screen);
+	b->r = ncallcalcsize(b->box, screen, r);
+	return b->r;
 }
 
 static void
-button_draw(Nelem *nelem, Image *img, Rectangle r)
+button_draw(Nelem *nelem, Image *img)
 {
 	NButton *b = (NButton*)nelem;
 	GUARD(b);
-	b->box->funcs->draw(b->box, img, r);
+	ncalldraw(b->box, img);
 }
 
 static Nelem*
-button_checkhit(Nelem *nelem, Image *screen, Rectangle r, Mouse m)
+button_checkhit(Nelem *nelem, Image *screen, Mouse m)
 {
 	NButton *b = (NButton*)nelem;
 	GUARD(b);
-	return b->box->funcs->checkhit(b->box, screen, r, m);
+	return ncallcheckhit(b->box, screen, m);
 }
 
 static void
@@ -97,7 +98,7 @@
 }
 
 static NButton*
-button_onclick(int (*f)(Mouse, Nelem*, void*), void *aux)
+button_onclick(OnclickHandler f, void *aux)
 {
 	NButton *c = (NButton*)nc_get();
 	GUARD(c);
@@ -119,7 +120,7 @@
 }
 
 static NButton*
-button_labelfunc(char* (*f)(void))
+button_labelfunc(StringGetter f)
 {
 	NButton *c = (NButton*)nc_get();
 	GUARD(c);
@@ -130,18 +131,14 @@
 }
 
 NButton*
-New_Button(void)
+New_Button(char *name)
 {
-	NButton* b = malloc(sizeof(NButton));
-	assert(b);
+	NButton *b = MakeNelem(NButton, NButton_Type, &Nbuttonfunctions, name);
 	
-	b->box = New_Box()
-		->Slot(NAssign(NLabel, &b->label, New_Label()));
+	b->box = New_Box(name)
+		->Slot(NAssign(NLabel, &b->label, New_Label(nil)));
 	/* pop box from stack */
 	nc_pop();
-	
-	b->type = NButton_Type;
-	b->funcs = &Nbuttonfunctions;
 	
 	b->Slot = button_slot;
 	b->Border = button_border;
--- a/n_button.h
+++ b/n_button.h
@@ -6,7 +6,7 @@
 	DECL_ACCESSOR_OneParam(NButton, Slot, Nelem*);
 	DECL_ACCESSOR_TwoParams(NButton, Border, int, Image*);
 	DECL_ACCESSOR_OneParam(NButton, SizeToContent, int);
-	DECL_ACCESSOR_TwoParams(NButton, OnClick, int (*f)(Mouse, Nelem*, void*), void*);
+	DECL_ACCESSOR_TwoParams(NButton, OnClick, OnclickHandler, void*);
 	
 	DECL_ACCESSOR_OneParam(NButton, Label, char*);
 	DECL_ACCESSOR_OneParam(NButton, LabelFunc, char* (*f)(void));
@@ -18,4 +18,4 @@
 	NLabel *label;
 };
 
-NButton* New_Button(void);
+NButton* New_Button(char*);
--- a/n_hbox.c
+++ b/n_hbox.c
@@ -8,110 +8,60 @@
 #define N_TYPE NHBox_Type
 char* NHBox_Type = "NHBox";
 
-Point currentsize;
-Image* currentscreen;
+typedef struct csizep csizep;
+struct csizep {
+	Rectangle crect;
+	Rectangle frect;
+	Image *screen;
+};
 
 void
-hbox_childsize(Nelem* nelem, int)
+hbox_childsize(Nelem* nelem, int, void *aux)
 {
-	Point p = ncallcalcsize(nelem, currentscreen);
-	currentsize.x += p.x;
-	currentsize.y = currentsize.y > p.y ? currentsize.y : p.y;
+	csizep *p = (csizep*)aux;
+	Rectangle r = ncallcalcsize(nelem, p->screen, p->crect);
+	combinerect(&p->frect, r);
+	p->crect.min.x = r.max.x;
 }
 
-Point
-hbox_calcsize(Nelem* nelem, Image* screen)
+static Rectangle
+hbox_calcsize(Nelem* nelem, Image* screen, Rectangle r)
 {
+	csizep params;
 	NHBox* b = (NHBox*)nelem;
 	GUARD(b);
 	
-	currentsize = Pt(0, 0);
-	currentscreen = screen;
+	params.screen = screen;
+	params.crect = r;
+	params.frect.min = r.min;
+	params.frect.max = r.min;
 	
-	lforeach(&b->children, hbox_childsize);
+	lforeach(&b->children, hbox_childsize, &params);
 
-	return currentsize;
+	nelem->r = params.frect;
+	return nelem->r;
 }
 
-Rectangle currentrect;
-Image* currentimg;
-
 void
-hbox_childdraw(Nelem* elem, int)
+hbox_childdraw(Nelem* elem, int, void *aux)
 {
-	Point p = ncallcalcsize(elem, currentimg);
-	currentrect.max.x = currentrect.min.x + p.x;
-	ncalldraw(elem, currentimg, currentrect);
-	currentrect.min.x = currentrect.max.x;
+	
+	ncalldraw(elem, (Image*)aux);
 }
 
 void
-hbox_draw(Nelem* nelem, Image* img, Rectangle r)
+hbox_draw(Nelem* nelem, Image* img)
 {
 	NHBox* b = (NHBox*)nelem;
 	GUARD(b);
-
-	if (b->sizetocontent) {
-		r.max = addpt(r.min, ncallcalcsize(b, img));
-	}
 	
-	currentrect = r;
-	currentimg = img;
-	lforeach(&b->children, hbox_childdraw);
-}
-
-Nelem* ch_ret;
-Image* ch_screen;
-Rectangle ch_rect;
-Mouse ch_mouse;
-
-void
-hbox_fe_checkhit(Nelem* nelem, int)
-{
-	Point s;
-	Nelem* e;
-	Rectangle r;
+	lforeach(&b->children, hbox_childdraw, img);
 	
-	s = ncallcalcsize(nelem, ch_screen);
-	r.min = ch_rect.min;
-	r.max = addpt(r.min, s);
-	
-	if (!ptinrect(ch_mouse.xy, r)) {
-		ch_rect.min.x += s.x;
-		return;
+	if (nateborders) {
+		border(img, b->r, 1, ncolor.red, ZP);
 	}
-	
-	e = ncallcheckhit(nelem, ch_screen, r, ch_mouse);
-	ch_rect.min.x += s.x;
-	if (e) {
-		ch_ret = e;
-	}
 }
 
-Nelem*
-hbox_checkhit(Nelem* nelem, Image* screen, Rectangle r, Mouse m)
-{
-	NHBox* b = (NHBox*)nelem;
-	GUARD(b);
-	
-	ch_ret = nil;
-	ch_screen = screen;
-	ch_rect = r;
-	ch_mouse = m;
-	
-	ch_rect.max.x = ch_rect.min.x;
-	
-	lforeach(&b->children, hbox_fe_checkhit);
-	return ch_ret;
-}
-
-int
-hbox_hit(Nelem* nelem, Mouse m)
-{
-	GUARD(nelem);
-	return -1;
-}
-
 void
 hbox_free(Nelem* nelem)
 {
@@ -134,8 +84,6 @@
 static Nelemfunctions Nhboxfunctions = {
 	.calcsize = hbox_calcsize,
 	.draw = hbox_draw,
-	.checkhit = hbox_checkhit,
-	.hit = hbox_hit,
 	.free = hbox_free,
 	.getchildren = hbox_getchildren,
 };
@@ -153,23 +101,12 @@
 	return b;
 }
 
-NHBox*
-hbox_sizetocontent(int stc)
-{
-	NHBox* b = (NHBox*)nc_get();
-	GUARD(b);
-	b->sizetocontent = stc;
-	return b;
-}
+DEF_ACCESSOR_OneParam(NHBox, hbox_sizetocontent, int, sizetocontent);
 
 NHBox*
-New_HBox(void)
+New_HBox(char *name)
 {
-	NHBox* b = malloc(sizeof(NHBox));
-	assert(b);
-	
-	b->type = NHBox_Type;
-	b->funcs = &Nhboxfunctions;
+	NHBox *b = MakeNelem(NHBox, NHBox_Type, &Nhboxfunctions, name);
 	
 	b->Slot = hbox_slot;
 	b->SizeToContent = hbox_sizetocontent;
--- a/n_hbox.h
+++ b/n_hbox.h
@@ -11,4 +11,4 @@
 	int sizetocontent;
 };
 
-NHBox* New_HBox(void);
+NHBox* New_HBox(char*);
--- a/n_image.c
+++ b/n_image.c
@@ -8,44 +8,39 @@
 #define N_TYPE NImage_Type
 char *NImage_Type = "NImage";
 
-static Point
-image_calcsize(Nelem *nelem, Image*)
+static Rectangle
+image_calcsize(Nelem *nelem, Image*, Rectangle r)
 {
+	Point p;
 	NImage *i = (NImage*)nelem;
 	GUARD(i);
 	
+	if (!i->autosize) {
+		i->r = r;
+		return i->r;
+	}
+	
+	p = ZP;
 	if (i->image)
-		return Pt(Dx(i->image->r), Dy(i->image->r));
-	return ZP;
+		p = Pt(Dx(i->image->r), Dy(i->image->r));
+	r.max = addpt(r.min, p);
+	i->r = r;
+	return i->r;
 }
 
 static void
-image_draw(Nelem *nelem, Image *img, Rectangle r)
+image_draw(Nelem *nelem, Image *img)
 {
 	NImage *i = (NImage*)nelem;
 	GUARD(i);
 	
 	if (!i->image) {
-		draw(img, r, display->white, nil, ZP);
+		draw(img, i->r, display->white, nil, ZP);
 		return;
 	}
-	draw(img, r, i->image, nil, i->offset);
+	draw(img, i->r, i->image, nil, i->offset);
 }
 
-static Nelem*
-image_checkhit(Nelem *nelem, Image *screen, Rectangle r, Mouse m)
-{
-	GUARD(nelem);
-	return nil;
-}
-
-static int
-image_hit(Nelem *nelem, Mouse m)
-{
-	GUARD(nelem);
-	return -1;
-}
-
 static void
 image_free(Nelem *nelem)
 {
@@ -64,43 +59,25 @@
 static Nelemfunctions Nimagefunctions = {
 	.calcsize = image_calcsize,
 	.draw = image_draw,
-	.checkhit = image_checkhit,
-	.hit = image_hit,
 	.free = image_free,
 	.getchildren = image_getchildren,
 };
 
-static NImage*
-setimage(Image *img)
-{
-	NImage *i = (NImage*)nc_get();
-	GUARD(i);
-	i->image = img;
-	return i;
-}
+DEF_ACCESSOR_OneParam(NImage, image_image, Image*, image);
+DEF_ACCESSOR_OneParam(NImage, image_offset, Point, offset);
+DEF_ACCESSOR_OneParam(NImage, image_autosize, int, autosize);
 
-static NImage*
-setimageoffset(Point p)
-{
-	NImage *i = (NImage*)nc_get();
-	GUARD(i);
-	i->offset = p;
-	return i;
-}
-
 NImage*
-New_Image()
+New_Image(char *name)
 {
-	NImage *i = malloc(sizeof(NImage));
-	assert(i);
-	i->type = NImage_Type;
-	i->funcs = &Nimagefunctions;
+	NImage *i = MakeNelem(NImage, NImage_Type, &Nimagefunctions, name);
 	
 	i->image = nil;
 	i->offset = ZP;
 	
-	i->Image = setimage;
-	i->ImageOffset = setimageoffset;
+	i->Image = image_image;
+	i->Offset = image_offset;
+	i->AutoSize = image_autosize;
 	nc_push(i);
 	return i;
 }
--- a/n_image.h
+++ b/n_image.h
@@ -4,11 +4,13 @@
 struct NImage {
 	Nelem;
 	DECL_ACCESSOR_OneParam(NImage, Image, Image*);
-	DECL_ACCESSOR_OneParam(NImage, ImageOffset, Point);
+	DECL_ACCESSOR_OneParam(NImage, Offset, Point);
+	DECL_ACCESSOR_OneParam(NImage, AutoSize, int);
 	
 	// private members
 	Image *image;
 	Point offset;
+	int autosize;
 };
 
-NImage* New_Image(void);
+NImage* New_Image(char*);
--- a/n_label.c
+++ b/n_label.c
@@ -8,7 +8,7 @@
 #define N_TYPE NLabel_Type
 char* NLabel_Type = "NLabel";
 
-char*
+static char*
 getlabelstr(NLabel* l)
 {
 	if (l->labelfunc)
@@ -18,41 +18,41 @@
 	return "";
 }
 
-Point
-label_calcsize(Nelem* nelem, Image*)
+static Rectangle
+label_calcsize(Nelem* nelem, Image*, Rectangle r)
 {
 	NLabel* l = (NLabel*)nelem;
 	GUARD(l);
 	
-	return stringsize(l->font, getlabelstr(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;
+	return r;
 }
 
-void
-label_draw(Nelem* nelem, Image* img, Rectangle r)
+static void
+label_draw(Nelem* nelem, Image* img)
 {
 	char* str;
+	Rectangle r;
 	NLabel* l = (NLabel*)nelem;
 	GUARD(l);
 	
 	str = getlabelstr(l);
 	
+	r = l->r;
+	r.min.x += l->margin.left;
+	r.min.y += l->margin.top;
+	
 	string(img, r.min, l->color, ZP, l->font, str);
+	
+	if (nateborders) {
+		border(img, l->r, 1, ncolor.blue, ZP);
+	}
 }
 
-Nelem*
-label_checkhit(Nelem* nelem, Image* screen, Rectangle r, Mouse m)
-{
-	GUARD(nelem);
-	return nil;
-}
-
-int
-label_hit(Nelem* nelem, Mouse m)
-{
-	GUARD(nelem);
-	return -1;
-}
-
 void
 label_free(Nelem* nelem)
 {
@@ -68,63 +68,38 @@
 	return nil;
 }
 
+static char*
+label_getname(Nelem *nelem)
+{
+	NLabel *l = (NLabel*)nelem;
+	GUARD(l);
+	return getlabelstr(l);
+}
+
 static Nelemfunctions Nlabelfunctions = {
 	.calcsize = label_calcsize,
 	.draw = label_draw,
-	.checkhit = label_checkhit,
-	.hit = label_hit,
 	.free = label_free,
 	.getchildren = label_getchildren,
+	.getname = label_getname,
 };
 
-NLabel*
-label(char* label)
-{
-	NLabel* l = (NLabel*)nc_get();
-	GUARD(l);
-	l->label = label;
-	return l;
-}
+DEF_ACCESSOR_OneParam(NLabel, label, char*, label);
+DEF_ACCESSOR_OneParam(NLabel, labelfunc, StringGetter, labelfunc);
+DEF_ACCESSOR_OneParam(NLabel, lfont, Font*, font);
+DEF_ACCESSOR_OneParam(NLabel, lcolor, Image*, color);
+DEF_ACCESSOR_OneParam(NLabel, lmargin, Nmargin, margin);
 
 NLabel*
-labelfunc(char* (*f)(void))
+New_Label(char *name)
 {
-	NLabel* l = (NLabel*)nc_get();
-	GUARD(l);
-	l->labelfunc = f;
-	return l;
-}
-
-NLabel*
-lfont(Font* newfont)
-{
-	NLabel* l = (NLabel*)nc_get();
-	GUARD(l);
-	l->font = newfont;
-	return l;
-}
-
-NLabel*
-lcolor(Image* img)
-{
-	NLabel* l = (NLabel*)nc_get();
-	GUARD(l);
-	l->color = img;
-	return l;
-}
-
-NLabel*
-New_Label()
-{
-	NLabel* e = malloc(sizeof(NLabel));
-	assert(e);
-	e->type = NLabel_Type;
-	e->funcs = &Nlabelfunctions;
+	NLabel *e = MakeNelem(NLabel, NLabel_Type, &Nlabelfunctions, name);
 	
 	e->Label = label;
 	e->LabelFunc = labelfunc;
 	e->Font = lfont;
 	e->Color = lcolor;
+	e->Margin = lmargin;
 	
 	e->label = nil;
 	e->labelfunc = nil;
--- a/n_label.h
+++ b/n_label.h
@@ -4,15 +4,17 @@
 struct NLabel {
 	Nelem;
 	DECL_ACCESSOR_OneParam(NLabel, Label, char*);
-	DECL_ACCESSOR_OneParam(NLabel, LabelFunc, char* (*f)(void));
+	DECL_ACCESSOR_OneParam(NLabel, LabelFunc, StringGetter);
 	DECL_ACCESSOR_OneParam(NLabel, Font, Font*);
 	DECL_ACCESSOR_OneParam(NLabel, Color, Image*);
+	DECL_ACCESSOR_OneParam(NLabel, Margin, Nmargin);
 	
 	// private members
 	char* label;
-	char* (*labelfunc)(void);
+	StringGetter labelfunc;
 	Font* font;
 	Image* color;
+	Nmargin margin;
 };
 
-NLabel* New_Label(void);
+NLabel* New_Label(char*);
--- a/n_vbox.c
+++ b/n_vbox.c
@@ -8,110 +8,55 @@
 #define N_TYPE NVBox_Type
 char* NVBox_Type = "NVBox";
 
-Point currentsize;
-Image* currentscreen;
+typedef struct csizep csizep;
+struct csizep {
+	Rectangle crect;
+	Rectangle frect;
+	Image *screen;
+};
 
 void
-vbox_childsize(Nelem* nelem, int)
+vbox_childsize(Nelem* nelem, int, void *aux)
 {
-	Point p = ncallcalcsize(nelem, currentscreen);
-	currentsize.x = currentsize.x > p.x ? currentsize.x : p.x;
-	currentsize.y += p.y;
+	csizep *p = (csizep*)aux;
+	Rectangle r = ncallcalcsize(nelem, p->screen, p->crect);
+	combinerect(&p->frect, r);
+	p->crect.min.y = r.max.y;
 }
 
-Point
-vbox_calcsize(Nelem* nelem, Image* screen)
+static Rectangle
+vbox_calcsize(Nelem* nelem, Image* img, Rectangle r)
 {
+	csizep params;
 	NVBox* b = (NVBox*)nelem;
 	GUARD(b);
 	
-	currentsize = Pt(0, 0);
-	currentscreen = screen;
+	params.crect = r;
+	params.screen = img;
+	params.frect.min = r.min;
+	params.frect.max = r.min;
 	
-	lforeach(&b->children, vbox_childsize);
-
-	return currentsize;
-}
-
-Rectangle currentrect;
-Image* currentimg;
-
-void
-vbox_childdraw(Nelem* elem, int)
-{
-	Point p = ncallcalcsize(elem, currentimg);
-	currentrect.max.y = currentrect.min.y + p.y;
-	ncalldraw(elem, currentimg, currentrect);
-	currentrect.min.y = currentrect.max.y;
-}
-
-void
-vbox_draw(Nelem* nelem, Image* img, Rectangle r)
-{
-	NVBox* b = (NVBox*)nelem;
-	GUARD(b);
-
-	if (b->sizetocontent) {
-		r.max = addpt(r.min, ncallcalcsize(b, img));
-	}
+	lforeach(&b->children, vbox_childsize, &params);
 	
-	currentrect = r;
-	currentimg = img;
-	lforeach(&b->children, vbox_childdraw);
+	b->r = params.frect;
+	return b->r;
 }
 
-Nelem* ch_ret;
-Image* ch_screen;
-Rectangle ch_rect;
-Mouse ch_mouse;
-
-void
-vbox_fe_checkhit(Nelem* nelem, int)
+static void
+vbox_childdraw(Nelem* elem, int, void *aux)
 {
-	Point s;
-	Nelem* e;
-	Rectangle r;
-	
-	s = ncallcalcsize(nelem, ch_screen);
-	r.min = ch_rect.min;
-	r.max = addpt(r.min, s);
-	
-	if (!ptinrect(ch_mouse.xy, r)) {
-		ch_rect.min.y += s.y;
-		return;
-	}
-	
-	e = ncallcheckhit(nelem, ch_screen, r, ch_mouse);
-	ch_rect.min.y += s.y;
-	if (e) {
-		ch_ret = e;
-	}
+	ncalldraw(elem, (Image*)aux);
 }
 
-Nelem*
-vbox_checkhit(Nelem* nelem, Image* screen, Rectangle r, Mouse m)
+static void
+vbox_draw(Nelem* nelem, Image* img)
 {
 	NVBox* b = (NVBox*)nelem;
 	GUARD(b);
 	
-	ch_ret = nil;
-	ch_screen = screen;
-	ch_rect = r;
-	ch_mouse = m;
-	
-	ch_rect.max.y = ch_rect.min.y;
-	
-	lforeach(&b->children, vbox_fe_checkhit);
-	return ch_ret;
+	lforeach(&b->children, vbox_childdraw, img);
 }
 
-int
-vbox_hit(Nelem* nelem, Mouse m)
-{
-	GUARD(nelem);
-	return -1;
-}
-
 void
 vbox_free(Nelem* nelem)
 {
@@ -134,8 +79,6 @@
 static Nelemfunctions Nvboxfunctions = {
 	.calcsize = vbox_calcsize,
 	.draw = vbox_draw,
-	.checkhit = vbox_checkhit,
-	.hit = vbox_hit,
 	.free = vbox_free,
 	.getchildren = vbox_getchildren,
 };
@@ -153,23 +96,12 @@
 	return b;
 }
 
-NVBox*
-vbox_sizetocontent(int stc)
-{
-	NVBox* b = (NVBox*)nc_get();
-	GUARD(b);
-	b->sizetocontent = stc;
-	return b;
-}
+DEF_ACCESSOR_OneParam(NVBox, vbox_sizetocontent, int, sizetocontent);
 
 NVBox*
-New_VBox(void)
+New_VBox(char *name)
 {
-	NVBox* b = malloc(sizeof(NVBox));
-	assert(b);
-	
-	b->type = NVBox_Type;
-	b->funcs = &Nvboxfunctions;
+	NVBox *b = MakeNelem(NVBox, NVBox_Type, &Nvboxfunctions, name);
 	
 	b->Slot = vbox_slot;
 	b->SizeToContent = vbox_sizetocontent;
--- a/n_vbox.h
+++ b/n_vbox.h
@@ -11,4 +11,4 @@
 	int sizetocontent;
 };
 
-NVBox* New_VBox(void);
+NVBox* New_VBox(char*);
--- a/n_window.c
+++ b/n_window.c
@@ -8,22 +8,28 @@
 #define N_TYPE NWindow_Type
 char* NWindow_Type = "NWindow";
 
-Point
-wcalcsize(Nelem* nelem, Image* screen)
+Rectangle
+wcalcsize(Nelem* nelem, Image* screen, Rectangle r)
 {
-	GUARD(nelem);
-	return subpt(screen->r.max, screen->r.min);
+	Nelem *f;
+	NWindow *w = (NWindow*)nelem;
+	GUARD(w);
+	f = lgetfirst(&w->child);
+	if (f)
+		ncallcalcsize(f, screen, screen->r);
+	nelem->r = r;
+	return screen->r;
 }
 
-void
-wdraw(Nelem* nelem, Image* img, Rectangle r)
+static void
+wdraw(Nelem* nelem, Image* img)
 {
-	Nelem* f;
-	NWindow* w = (NWindow*)nelem;
+	Nelem *f;
+	NWindow *w = (NWindow*)nelem;
 	GUARD(w);
 	f = lgetfirst(&w->child);
 	if (f)
-		ncalldraw(f, img, r);
+		ncalldraw(f, img);
 }
 
 void
@@ -79,13 +85,9 @@
 }
 
 NWindow*
-New_Window(void)
+New_Window(char *name)
 {
-	NWindow* e = malloc(sizeof(NWindow));
-	assert(e);
-	
-	e->type = NWindow_Type;
-	e->funcs = &Nwindowfunctions;
+	NWindow *e = MakeNelem(NWindow, NWindow_Type, &Nwindowfunctions, name);
 	
 	e->Slot = slot;
 	e->MakeRoot = makeroot;
--- a/n_window.h
+++ b/n_window.h
@@ -10,4 +10,4 @@
 	Nlist child;
 };
 
-NWindow* New_Window(void);
+NWindow* New_Window(char*);
--- a/nate.c
+++ b/nate.c
@@ -4,6 +4,9 @@
 #include <event.h>
 #include "nate_construct.h"
 
+int nateborders = 0;
+int natetracehit = 0;
+
 static Nlist rootchain = { nil };
 static Nelem* rootelem = nil;
 
@@ -37,9 +40,9 @@
 }
 
 void
-nforeachroot(void (*f)(Nelem*, int))
+nforeachroot(void (*f)(Nelem*, int, void*), void *aux)
 {
-	lforeach(&rootchain, f);
+	lforeach(&rootchain, f, aux);
 }
 
 int
@@ -118,11 +121,11 @@
 }
 
 void
-lforeach(Nlist* list, void (*f)(Nelem*, int))
+lforeach(Nlist* list, void (*f)(Nelem*, int, void*), void *aux)
 {
 	Nlistelem* l = list->first;
 	for (int i = 0; l; i++) {
-		f(l->item, i);
+		f(l->item, i, aux);
 		l = l->next;
 	}
 }
@@ -173,34 +176,45 @@
 		nelem->funcs->free(nelem);
 }
 
-Point
-ncallcalcsize(Nelem* nelem, Image* dst)
+Rectangle
+ncallcalcsize(Nelem* nelem, Image* dst, Rectangle r)
 {
 	assert(nelem);
 	
-	if (nelem->funcs && nelem->funcs->calcsize)
-		return nelem->funcs->calcsize(nelem, dst);
+	if (nelem->funcs && nelem->funcs->calcsize) {
+		r = nelem->funcs->calcsize(nelem, dst, r);
+		//fprint(2, "calcsize %s: %R\n", nelem->type, r);
+		return r;
+	}
 	
-	return Pt(0,0);
+	return r;
 }
 
 void
-ncalldraw(Nelem* nelem, Image* dst, Rectangle r)
+ncalldraw(Nelem* nelem, Image* dst)
 {
+	Rectangle or;
 	assert(nelem);
 	
-	if (nelem->funcs && nelem->funcs->draw)
-		nelem->funcs->draw(nelem, dst, r);
+	if (nelem->funcs && nelem->funcs->draw) {
+		or = dst->clipr;
+		replclipr(dst, 0, nelem->r);
+		nelem->funcs->draw(nelem, dst);
+		replclipr(dst, 0, or);
+	}
 }
 
 Nelem*
-ncallcheckhit(Nelem* nelem, Image* screen, Rectangle r, Mouse m)
+ncallcheckhit(Nelem* nelem, Image* screen, Mouse m)
 {
 	assert(nelem);
 	
-	if (nelem->funcs && nelem->funcs->checkhit)
-		return nelem->funcs->checkhit(nelem, screen, r, m);
-	return nil;
+	nctracehit(nelem, !nelem->funcs->hit);
+	nctracehitlevel++;
+	if (nelem->funcs && nelem->funcs->checkhit) {
+		return nelem->funcs->checkhit(nelem, screen, m);
+	}
+	return nd_checkhit(nelem, screen, m);
 }
 
 int
@@ -235,15 +249,22 @@
 nateinit()
 {
 	nc_init();
+#define A(col) allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, col)
+	ncolor.red = A(DRed);
+	ncolor.green = A(DGreen);
+	ncolor.blue = A(DBlue);
+#undef A
 }
 
 void
-nateredraw()
+nateredraw(int all)
 {
 	if (!rootelem)
 		return;
 	
-	ncalldraw(rootelem, screen, screen->r);
+	if (all)
+		ncallcalcsize(rootelem, screen, screen->r);
+	ncalldraw(rootelem, screen);
 }
 
 int
@@ -250,20 +271,18 @@
 natemouseevent(Mouse m)
 {
 	Nelem* el;
-	Point s;
 	Rectangle r;
 	
 	if (!rootelem)
 		return 0;
 	
-	s = ncallcalcsize(rootelem, screen);
-	r.min = screen->r.min;
-	r.max = addpt(r.min, s);
+	r = ncallcalcsize(rootelem, screen, screen->r);
 	
 	if (!ptinrect(m.xy, r))
 		return 0;
 	
-	el = ncallcheckhit(rootelem, screen, screen->r, m);
+	nctracehitlevel = natetracehit - 1;
+	el = ncallcheckhit(rootelem, screen, m);
 	if (!el)
 		return 0;
 	
--- a/nate.h
+++ b/nate.h
@@ -4,11 +4,30 @@
 typedef struct Nelemfunctions Nelemfunctions;
 typedef struct Nlist Nlist;
 
+
+/* common types
+ *****************/
+
+typedef int (*OnclickHandler)(Mouse, Nelem*, void*);
+typedef char* (*StringGetter)(void);
+
+typedef struct Nmargin Nmargin;
+
+struct Nmargin {
+	int left;
+	int top;
+	int right;
+	int bottom;
+};
+
+#define NMargin(l, t, r, b) ((Nmargin){l, t, r, b})
+#define NMargin2(x, y) ((Nmargin){x, y, x, y})
+
 /* user functions
  ******************/
 
 void nateinit(void);
-void nateredraw(void);
+void nateredraw(int all);
 
 int natemouseevent(Mouse);
 
@@ -15,22 +34,35 @@
 #define NAssign(T, A, B) ((T*)(nassign((Nelem**)A, B)))
 Nelem* nassign(Nelem**, Nelem*);
 
+extern int nateborders;
+extern int natetracehit;
 
+struct {
+	Image *red;
+	Image *green;
+	Image *blue;
+} ncolor;
+
+
 /* end user functions
  *********************/
 
 struct Nelem {
-	char* type;
+	char *type;
+	char *name;
+	Rectangle r;
 	Nelemfunctions* funcs;
 };
 struct Nelemfunctions {
-	Point (*calcsize)(Nelem*, Image*);
-	void (*draw)(Nelem*, Image*, Rectangle);
-	Nelem* (*checkhit)(Nelem*, Image*, Rectangle, Mouse);
+	Rectangle (*calcsize)(Nelem*, Image*, Rectangle);
+	void (*draw)(Nelem*, Image*);
+	Nelem* (*checkhit)(Nelem*, Image*, Mouse);
 	int (*hit)(Nelem*, Mouse);
 	
 	void (*free)(Nelem*);
 	Nlist* (*getchildren)(Nelem*);
+	
+	char* (*getname)(Nelem*);
 };
 
 /* nlist functions
@@ -51,7 +83,7 @@
 void lins(Nlist* list, Nelem* item);
 void ldel(Nlist* list, Nelem* item);
 void linsert(Nlist* list, Nelem* before, Nelem* item);
-void lforeach(Nlist* list, void (*f)(Nelem*, int));
+void lforeach(Nlist* list, void (*f)(Nelem*, int, void*), void*);
 void lfreelist(Nlist* list);
 
 Nelem* lgetfirst(Nlist* list);
@@ -62,9 +94,9 @@
  ********************/
 
 // calls on Nelem
-Point ncallcalcsize(Nelem*, Image*);
-void ncalldraw(Nelem*, Image*, Rectangle);
-Nelem* ncallcheckhit(Nelem*, Image*, Rectangle, Mouse);
+Rectangle ncallcalcsize(Nelem*, Image*, Rectangle);
+void ncalldraw(Nelem*, Image*);
+Nelem* ncallcheckhit(Nelem*, Image*, Mouse);
 int ncallhit(Nelem*, Mouse);
 void ncallfree(Nelem*);
 Nlist* ncallgetchildren(Nelem*);
--- a/nate_construct.c
+++ b/nate_construct.c
@@ -52,44 +52,77 @@
 	return nc_stack ? nc_stack->elem : nil;
 }
 
-Nelem* ch_retelem;
-Rectangle ch_rect;
-Image* ch_screen;
-Mouse ch_mouse;
+typedef struct chparams chparams;
+struct chparams {
+	Nelem *retelem;
+	Image *screen;
+	Mouse mouse;
+};
 
-void
-fe_checkhit(Nelem* elem, int)
+static void
+fe_checkhit(Nelem* elem, int, void *aux)
 {
+	chparams *p = (chparams*)aux;
 	Rectangle r;
 	Nelem* e;
-	Point s;
 	
-	s = ncallcalcsize(elem, ch_screen);
-	r.min = ch_rect.min;
-	r.max = addpt(r.min, s);
+	if (p->retelem)
+		return;
 	
-	if (!ptinrect(ch_mouse.xy, r))
+	border(p->screen, r, 1, ncolor.green, ZP);
+	
+	if (!ptinrect(p->mouse.xy, elem->r))
 		return;
 	
-	e = ncallcheckhit(elem, ch_screen, ch_rect, ch_mouse);
+	e = ncallcheckhit(elem, p->screen, p->mouse);
+	nctracehitlevel--;
 	if (e) {
-		ch_retelem = e;
+		p->retelem = e;
 	}
 }
 
 Nelem*
-nd_checkhit(Nelem* nelem, Image* screen, Rectangle r, Mouse m)
+nd_checkhit(Nelem* nelem, Image* screen, Mouse m)
 {
+	chparams params;
 	Nlist* l = ncallgetchildren(nelem);
 	
 	if (!l)
 		return nil;
 	
-	ch_retelem = nil;
-	ch_rect = r;
-	ch_screen = screen;
-	ch_mouse = m;
-	lforeach(l, fe_checkhit);
+	params.retelem = nil;
+	params.screen = screen;
+	params.mouse = m;
+	lforeach(l, fe_checkhit, &params);
 	
-	return ch_retelem;
+	return params.retelem;
+}
+
+Nelem*
+makenelem(long size, char *type, Nelemfunctions *funcs, char *name)
+{
+	Nelem *nel = mallocz(size, 1);
+	if (!nel)
+		sysfatal("oom: creating nelem %s", type);
+	nel->type = type;
+	nel->name = name;
+	nel->funcs = funcs;
+	return nel;
+}
+
+int nctracehitlevel = 0;
+void
+nctracehit(Nelem *el, int pt)
+{
+	char *name;
+	if (nctracehitlevel < 0)
+		return;
+	if (el->funcs && el->funcs->getname)
+		name = el->funcs->getname(el);
+	else
+		name = el->name;
+	fprint(2, "HIT: %*s%c%s%c %c%s%c\n",
+		nctracehitlevel * 3, nctracehitlevel?"↘":"", /* indent */
+		pt ? '(' : ' ', el->type, pt ? ')' : ' ', /* Nelem type */
+		name?'\"':' ', name ? name : "", name?'\"':' '); /* name */
 }
--- a/nate_construct.h
+++ b/nate_construct.h
@@ -8,9 +8,51 @@
 #define COND(N) (N->type == N_TYPE)
 #define GUARD(N) assert(COND(N))
 
+// create with benefits
+Nelem *makenelem(long size, char *type, Nelemfunctions *funcs, char *name);
+#define MakeNelem(Type, Char, F, N) ((Type*)makenelem(sizeof(Type), Char, F, N));
 
+
 // internal stuff
 void nc_init(void);
 
+// trace hits
+extern int nctracehitlevel;
+void nctracehit(Nelem *el, int passthrough);
+
 // default functionality
-Nelem* nd_checkhit(Nelem* nelem, Image* screen, Rectangle r, Mouse m);
+Nelem* nd_checkhit(Nelem* nelem, Image* screen, Mouse m);
+
+// syntactic sugar: default accessors
+#define DEF_ACCESSOR_OneParam(Type, Func, T1, N1) \
+	static Type* \
+	Func(T1 N1) \
+	{ \
+		Type *b = (Type*)nc_get(); \
+		GUARD(b); \
+		b->N1 = N1; \
+		return b; \
+	}
+
+#define DEF_ACCESSOR_TwoParams(Type, Func, T1, N1, T2, N2) \
+	static Type* \
+	Func(T1 N1, T2 N2) \
+	{ \
+		Type *b = (Type*)nc_get(); \
+		GUARD(b); \
+		b->N1 = N1; \
+		b->N2 = N2; \
+		return b; \
+	}
+
+#define DEF_ACCESSOR_ThreeParams(Type, Func, T1, N1, T2, N2, T3, N3) \
+	static Type* \
+	Func(T1 N1, T2 N2, T3 N3) \
+	{ \
+		Type *b = (Type*)nc_get(); \
+		GUARD(b); \
+		b->N1 = N1; \
+		b->N2 = N2; \
+		b->N3 = N3; \
+		return b; \
+	}
--- a/test/ntest.c
+++ b/test/ntest.c
@@ -5,8 +5,10 @@
 #include "../nate.h"
 #include "../n_window.h"
 #include "../n_hbox.h"
+#include "../n_vbox.h"
 #include "../n_box.h"
 #include "../n_label.h"
+#include "../n_image.h"
 
 char*
 getlabel(void)
@@ -35,7 +37,7 @@
 	if (new && getwindow(display, Refnone) < 0)
 		sysfatal("getwindow: %r");
 	
-	nateredraw();
+	nateredraw(1);
 }
 
 void
@@ -52,43 +54,73 @@
 	einit(Emouse);
 	nateinit();
 	
+	nateborders = 1;
+	natetracehit = 1;
+	
 	Image* red = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DRed);
 	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())
+	NAssign(NWindow, &mainwindow, New_Window(nil))
 	->MakeRoot()
 	->Slot(
-		New_Box()
+		New_VBox(nil)
 		->SizeToContent(1)
-		->Border(1, red)
 		->Slot(
-			New_HBox()
+			New_HBox("first")
 			->SizeToContent(1)
 			->Slot(
-				NAssign(NBox, &box1, New_Box())
+				NAssign(NBox, &box1, New_Box(nil))
 				->Border(1, green)
 				->SizeToContent(1)
+				->Padding(NMargin2(5, 3))
 				->OnClick(callclick, nil)
 				->Slot(
-					New_Label()
+					New_Label(nil)
 					->LabelFunc(getlabel)
+					->Margin(NMargin2(5, 5))
 				)
 			)
 			->Slot(
-				NAssign(NBox, &box2, New_Box())
+				NAssign(NBox, &box2, New_Box(nil))
 				->Border(1, blue)
 				->SizeToContent(1)
 				->OnClick(callclick, nil)
 				->Slot(
-					New_Label()
+					New_Label(nil)
 					->Label("DEF")
+					->Margin(NMargin2(5, 5))
 				)
 			)
+			->Slot(
+				New_Box("imagebox")
+				->Size(Pt(50, 50))
+				->Slot(
+					New_Image(nil)
+					->Image(display->black)
+				)
+			)
 		)
+		->Slot(
+			New_HBox("second")
+			->SizeToContent(1)
+			->Slot(
+				New_Label(nil)
+				->Label("abc")
+			)
+		)
 	);
 	
 	eresized(0);
+	
+	int p[2];
+	pipe(p);
+	dup(p[0], 2);
+	int fd = create("/srv/ntest", OWRITE|ORCLOSE, 0666);
+	if (fd < 0)
+		sysfatal("create: %r");
+	fprint(fd, "%d\n", p[1]);
+	close(p[1]);
 	
 	for (;;) {
 		e = event(&ev);