shithub: libnate

Download patch

ref: c04fd4ce67a03c04b342c38185523bb834f4e0b7
author: sirjofri <sirjofri@sirjofri.de>
date: Fri Nov 15 10:42:40 EST 2024

adds first bunch of files with working demo test

--- /dev/null
+++ b/mkfile
@@ -1,0 +1,20 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libnate.a
+
+OFILES=\
+	nate.$O \
+	nate_construct.$O \
+	n_window.$O \
+	n_label.$O \
+	n_box.$O \
+	n_hbox.$O \
+
+HFILES=nate.h \
+	nate_construct.h \
+	n_window.h \
+	n_label.h \
+	n_box.h \
+	n_hbox.h \
+
+</sys/src/cmd/mksyslib
--- /dev/null
+++ b/n_box.c
@@ -1,0 +1,181 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+#include "nate_construct.h"
+#include "n_box.h"
+
+#define N_TYPE NBox_Type
+char* NBox_Type = "NBox";
+
+Point
+box_calcsize(Nelem* nelem, Image* screen)
+{
+	NBox* b = (NBox*)nelem;
+	GUARD(b);
+	if (!lgetfirst(&b->child)) {
+		return Pt(0, 0);
+	}
+	Point csize = ncallcalcsize(lgetfirst(&b->child), screen);
+	Point ssize = Pt(b->borderwidth, b->borderwidth);
+	csize = addpt(csize, addpt(ssize, ssize));
+	return csize;
+}
+
+void
+box_draw(Nelem* nelem, Image* img, Rectangle r)
+{
+	Nelem* f;
+	NBox* b = (NBox*)nelem;
+	GUARD(b);
+	f = lgetfirst(&b->child);
+	if (!f)
+		return;
+
+	if (b->sizetocontent) {
+		r.max = addpt(r.min, ncallcalcsize(nelem, img));
+	}
+
+	border(img, r, b->borderwidth, b->bordercolor, ZP);
+	ncalldraw(f, img, insetrect(r, b->borderwidth));
+}
+
+Nelem*
+box_checkhit(Nelem* nelem, Image* screen, Rectangle rect, Mouse m)
+{
+	NBox* b = (NBox*)nelem;
+	Nelem* r;
+	Nelem* ch;
+	Point size;
+	Rectangle cr;
+	GUARD(b);
+	
+	if (!(ch = lgetfirst(&b->child))) {
+		if (m.buttons&1 && b->hitfunc) {
+			return b;
+		}
+		return nil;
+	}
+	
+	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) {
+		return b;
+	}
+	return nil;
+}
+
+int
+box_hit(Nelem* nelem, Mouse m)
+{
+	NBox* b = (NBox*)nelem;
+	GUARD(b);
+	
+	if (b->hitfunc)
+		return b->hitfunc(m, b, b->hitaux);
+	return 0;
+}
+
+void
+box_free(Nelem* nelem)
+{
+	Nelem* ch;
+	NBox* b = (NBox*)nelem;
+	if (nisroot(b))
+		return;
+	
+	if ((ch = lgetfirst(&b->child)))
+		ncallfree(ch);
+	free(b);
+}
+
+Nlist*
+box_getchildren(Nelem* nelem)
+{
+	NBox* b = (NBox*)nelem;
+	GUARD(b);
+	return &b->child;
+}
+
+static Nelemfunctions Nboxfunctions = {
+	.calcsize = box_calcsize,
+	.draw = box_draw,
+	.checkhit = box_checkhit,
+	.hit = box_hit,
+	.free = box_free,
+	.getchildren = box_getchildren,
+};
+
+NBox*
+box_slot(Nelem* child)
+{
+	if (child == nc_get()) {
+		nc_pop();
+	}
+	NBox* b = (NBox*)nc_get();
+	GUARD(b);
+	// TODO: potential leak!
+	lsetfirst(&b->child, child);
+	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;
+}
+
+NBox*
+box_onclick(int (*f)(Mouse, Nelem*, void*), void* aux)
+{
+	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);
+	
+	b->type = NBox_Type;
+	b->funcs = &Nboxfunctions;
+	
+	b->Slot = box_slot;
+	b->Border = box_border;
+	b->SizeToContent = box_sizetocontent;
+	b->OnClick = box_onclick;
+	
+	linit(&b->child);
+	b->sizetocontent = 0;
+	b->hitfunc = nil;
+	b->hitaux = nil;
+	b->borderwidth = 0;
+	b->bordercolor = display->black;
+	nc_push(b);
+	return b;
+}
--- /dev/null
+++ b/n_box.h
@@ -1,0 +1,20 @@
+extern char* NBox_Type;
+
+typedef struct NBox NBox;
+struct NBox {
+	Nelem;
+	DECL_ACCESSOR_OneParam(NBox, Slot, Nelem*);
+	DECL_ACCESSOR_TwoParams(NBox, Border, int, Image*);
+	DECL_ACCESSOR_OneParam(NBox, SizeToContent, int);
+	DECL_ACCESSOR_TwoParams(NBox, OnClick, int (*f)(Mouse, Nelem*, void*), void*);
+	
+	// private members
+	Nlist child;
+	int sizetocontent;
+	int borderwidth;
+	Image* bordercolor;
+	int (*hitfunc)(Mouse, Nelem*, void*);
+	void* hitaux;
+};
+
+NBox* New_Box(void);
--- /dev/null
+++ b/n_hbox.c
@@ -1,0 +1,181 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+#include "nate_construct.h"
+#include "n_hbox.h"
+
+#define N_TYPE NHBox_Type
+char* NHBox_Type = "NHBox";
+
+Point currentsize;
+Image* currentscreen;
+
+void
+hbox_childsize(Nelem* nelem, int)
+{
+	Point p = ncallcalcsize(nelem, currentscreen);
+	currentsize.x += p.x;
+	currentsize.y = currentsize.y > p.y ? currentsize.y : p.y;
+}
+
+Point
+hbox_calcsize(Nelem* nelem, Image* screen)
+{
+	NHBox* b = (NHBox*)nelem;
+	GUARD(b);
+	
+	currentsize = Pt(0, 0);
+	currentscreen = screen;
+	
+	lforeach(&b->children, hbox_childsize);
+
+	return currentsize;
+}
+
+Rectangle currentrect;
+Image* currentimg;
+
+void
+hbox_childdraw(Nelem* elem, int)
+{
+	Point p = ncallcalcsize(elem, currentimg);
+	currentrect.max.x = currentrect.min.x + p.x;
+	ncalldraw(elem, currentimg, currentrect);
+	currentrect.min.x = currentrect.max.x;
+}
+
+void
+hbox_draw(Nelem* nelem, Image* img, Rectangle r)
+{
+	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;
+	
+	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;
+	}
+	
+	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)
+{
+	NHBox* b = (NHBox*)nelem;
+	if (nisroot(b))
+		return;
+	
+	lfreelist(&b->children);
+	free(b);
+}
+
+Nlist*
+hbox_getchildren(Nelem* nelem)
+{
+	NHBox* b = (NHBox*)nelem;
+	GUARD(b);
+	return &b->children;
+}
+
+static Nelemfunctions Nhboxfunctions = {
+	.calcsize = hbox_calcsize,
+	.draw = hbox_draw,
+	.checkhit = hbox_checkhit,
+	.hit = hbox_hit,
+	.free = hbox_free,
+	.getchildren = hbox_getchildren,
+};
+
+NHBox*
+hbox_slot(Nelem* child)
+{
+	if (child == nc_get()) {
+		nc_pop();
+	}
+	NHBox* b = (NHBox*)nc_get();
+	GUARD(b);
+	
+	ladd(&b->children, child);
+	return b;
+}
+
+NHBox*
+hbox_sizetocontent(int stc)
+{
+	NHBox* b = (NHBox*)nc_get();
+	GUARD(b);
+	b->sizetocontent = stc;
+	return b;
+}
+
+NHBox*
+New_HBox(void)
+{
+	NHBox* b = malloc(sizeof(NHBox));
+	assert(b);
+	
+	b->type = NHBox_Type;
+	b->funcs = &Nhboxfunctions;
+	
+	b->Slot = hbox_slot;
+	b->SizeToContent = hbox_sizetocontent;
+	
+	linit(&b->children);
+	b->sizetocontent = 0;
+	nc_push(b);
+	return b;
+}
--- /dev/null
+++ b/n_hbox.h
@@ -1,0 +1,14 @@
+extern char* NHBox_Type;
+
+typedef struct NHBox NHBox;
+struct NHBox {
+	Nelem;
+	DECL_ACCESSOR_OneParam(NHBox, Slot, Nelem*);
+	DECL_ACCESSOR_OneParam(NHBox, SizeToContent, int);
+	
+	// private members
+	Nlist children;
+	int sizetocontent;
+};
+
+NHBox* New_HBox(void);
--- /dev/null
+++ b/n_label.c
@@ -1,0 +1,135 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+#include "nate_construct.h"
+#include "n_label.h"
+
+#define N_TYPE NLabel_Type
+char* NLabel_Type = "NLabel";
+
+char*
+getlabelstr(NLabel* l)
+{
+	if (l->labelfunc)
+		return l->labelfunc();
+	if (l->label)
+		return l->label;
+	return "";
+}
+
+Point
+label_calcsize(Nelem* nelem, Image*)
+{
+	NLabel* l = (NLabel*)nelem;
+	GUARD(l);
+	
+	return stringsize(l->font, getlabelstr(l));
+}
+
+void
+label_draw(Nelem* nelem, Image* img, Rectangle r)
+{
+	char* str;
+	NLabel* l = (NLabel*)nelem;
+	GUARD(l);
+	
+	str = getlabelstr(l);
+	
+	string(img, r.min, l->color, ZP, l->font, str);
+}
+
+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)
+{
+	GUARD(nelem);
+	if (nisroot(nelem))
+		return;
+	free(nelem);
+}
+
+Nlist*
+label_getchildren(Nelem* nelem)
+{
+	return nil;
+}
+
+static Nelemfunctions Nlabelfunctions = {
+	.calcsize = label_calcsize,
+	.draw = label_draw,
+	.checkhit = label_checkhit,
+	.hit = label_hit,
+	.free = label_free,
+	.getchildren = label_getchildren,
+};
+
+NLabel*
+label(char* label)
+{
+	NLabel* l = (NLabel*)nc_get();
+	GUARD(l);
+	l->label = label;
+	return l;
+}
+
+NLabel*
+labelfunc(char* (*f)(void))
+{
+	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;
+	
+	e->Label = label;
+	e->LabelFunc = labelfunc;
+	e->Font = lfont;
+	e->Color = lcolor;
+	
+	e->label = nil;
+	e->labelfunc = nil;
+	e->font = display->defaultfont;
+	e->color = display->black;
+	nc_push(e);
+	return e;
+}
--- /dev/null
+++ b/n_label.h
@@ -1,0 +1,18 @@
+extern char* NLabel_Type;
+
+typedef struct NLabel NLabel;
+struct NLabel {
+	Nelem;
+	DECL_ACCESSOR_OneParam(NLabel, Label, char*);
+	DECL_ACCESSOR_OneParam(NLabel, LabelFunc, char* (*f)(void));
+	DECL_ACCESSOR_OneParam(NLabel, Font, Font*);
+	DECL_ACCESSOR_OneParam(NLabel, Color, Image*);
+	
+	// private members
+	char* label;
+	char* (*labelfunc)(void);
+	Font* font;
+	Image* color;
+};
+
+NLabel* New_Label(void);
--- /dev/null
+++ b/n_window.c
@@ -1,0 +1,96 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+#include "nate_construct.h"
+#include "n_window.h"
+
+#define N_TYPE NWindow_Type
+char* NWindow_Type = "NWindow";
+
+Point
+wcalcsize(Nelem* nelem, Image* screen)
+{
+	GUARD(nelem);
+	return subpt(screen->r.max, screen->r.min);
+}
+
+void
+wdraw(Nelem* nelem, Image* img, Rectangle r)
+{
+	Nelem* f;
+	NWindow* w = (NWindow*)nelem;
+	GUARD(w);
+	f = lgetfirst(&w->child);
+	if (f)
+		ncalldraw(f, img, r);
+}
+
+void
+wfree(Nelem* nelem)
+{
+	Nelem* f;
+	NWindow* w = (NWindow*)nelem;
+	if (nisroot(w))
+		return;
+	
+	if ((f = lgetfirst(&w->child)))
+		ncallfree(f);
+	free(w);
+}
+
+Nlist*
+wgetchildren(Nelem* nelem)
+{
+	NWindow* w = (NWindow*)nelem;
+	GUARD(w);
+	return &w->child;
+}
+
+static Nelemfunctions Nwindowfunctions = {
+	.calcsize = wcalcsize,
+	.draw = wdraw,
+	.checkhit = nd_checkhit,
+	.hit = nil,
+	.free = wfree,
+	.getchildren = wgetchildren,
+};
+
+NWindow*
+slot(Nelem* child)
+{
+	if (child == nc_get()) {
+		nc_pop();
+	}
+	NWindow* w = (NWindow*)nc_get();
+	GUARD(w);
+	// old child is potential leak!
+	lsetfirst(&w->child, child);
+	return w;
+}
+
+NWindow*
+makeroot(void)
+{
+	NWindow* w = (NWindow*)nc_get();
+	GUARD(w);
+	nregroot(w);
+	return w;
+}
+
+NWindow*
+New_Window(void)
+{
+	NWindow* e = malloc(sizeof(NWindow));
+	assert(e);
+	
+	e->type = NWindow_Type;
+	e->funcs = &Nwindowfunctions;
+	
+	e->Slot = slot;
+	e->MakeRoot = makeroot;
+	
+	linit(&e->child);
+	nc_push(e);
+	return e;
+}
--- /dev/null
+++ b/n_window.h
@@ -1,0 +1,13 @@
+extern char* NWindow_Type;
+
+typedef struct NWindow NWindow;
+struct NWindow {
+	Nelem;
+	DECL_ACCESSOR_OneParam(NWindow, Slot, Nelem*);
+	DECL_ACCESSOR(NWindow, MakeRoot);
+	
+	// private members
+	Nlist child;
+};
+
+NWindow* New_Window(void);
--- /dev/null
+++ b/nate.c
@@ -1,0 +1,271 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+#include "nate_construct.h"
+
+static Nlist rootchain = { nil };
+static Nelem* rootelem = nil;
+
+void
+nregroot(Nelem* nelem)
+{
+	assert(nelem);
+
+	// root must be inside rootchain
+	nregister(nelem);
+
+	if (rootelem)
+		ncallfree(rootelem);
+
+	rootelem = nelem;
+}
+
+void
+nregister(Nelem* nelem)
+{
+	if (lhas(&rootchain, nelem))
+		return;
+		
+	ladd(&rootchain, nelem);
+}
+
+void
+nderegister(Nelem* nelem)
+{
+	ldel(&rootchain, nelem);
+}
+
+void
+nforeachroot(void (*f)(Nelem*, int))
+{
+	lforeach(&rootchain, f);
+}
+
+int
+nisroot(Nelem* nelem)
+{
+	return lhas(&rootchain, nelem);
+}
+
+void
+linit(Nlist* list)
+{
+	list->first = nil;
+}
+
+int
+lhas(Nlist* list, Nelem* item)
+{
+	Nlistelem* l = list->first;
+	while (l) {
+		if (l->item == item)
+			return 1;
+		l = l->next;
+	}
+	return 0;
+}
+
+void
+ladd(Nlist* list, Nelem* item)
+{
+	Nlistelem* l;
+	if (!list->first) {
+		list->first = malloc(sizeof(Nlist));
+		assert(list->first);
+		list->first->item = item;
+		list->first->next = nil;
+		return;
+	}
+	
+	l = list->first;
+	while (l->next)
+		l = l->next;
+	
+	l->next = malloc(sizeof(Nlist));
+	assert(l->next);
+	l->next->item = item;
+	l->next->next = nil;
+}
+
+void
+ldel(Nlist* list, Nelem* item)
+{
+	Nlistelem* l, *a;
+	if (!list->first)
+		return;
+	
+	l = list->first;
+	while (l->next && l->next->item != item)
+		l = l->next;
+	
+	a = l->next;
+	l->next = a->next;
+	free(a);
+}
+
+void
+linsert(Nlist* list, Nelem* before, Nelem* item)
+{
+	Nlistelem* l = list->first, *a;
+	while (l->next && l->item != before)
+		l = l->next;
+	
+	a = l->next;
+	l->next = malloc(sizeof(Nlist));
+	l->next->next = a;
+	l->next->item = item;
+}
+
+void
+lforeach(Nlist* list, void (*f)(Nelem*, int))
+{
+	Nlistelem* l = list->first;
+	for (int i = 0; l; i++) {
+		f(l->item, i);
+		l = l->next;
+	}
+}
+
+void
+lfreelist(Nlist* list)
+{
+	Nlistelem* a;
+	Nlistelem* l = list->first;
+	while (l) {
+		a = l->next;
+		if (l->item)
+			free(l->item);
+		free(l);
+		l = a;
+	}
+}
+
+Nelem*
+lgetfirst(Nlist* list)
+{
+	return list->first ? list->first->item : nil;
+}
+
+Nelem*
+lsetfirst(Nlist* list, Nelem* item)
+{
+	Nelem* f = lgetfirst(list);
+	if (list->first) {
+		list->first->item = item;
+		return f;
+	}
+	list->first = malloc(sizeof(Nlistelem));
+	list->first->next = nil;
+	list->first->item = item;
+	return f;
+}
+
+void
+ncallfree(Nelem* nelem)
+{
+	assert(nelem);
+	
+	if (nisroot(nelem))
+		return;
+	
+	if (nelem->funcs && nelem->funcs->free)
+		nelem->funcs->free(nelem);
+}
+
+Point
+ncallcalcsize(Nelem* nelem, Image* dst)
+{
+	assert(nelem);
+	
+	if (nelem->funcs && nelem->funcs->calcsize)
+		return nelem->funcs->calcsize(nelem, dst);
+	
+	return Pt(0,0);
+}
+
+void
+ncalldraw(Nelem* nelem, Image* dst, Rectangle r)
+{
+	assert(nelem);
+	
+	if (nelem->funcs && nelem->funcs->draw)
+		nelem->funcs->draw(nelem, dst, r);
+}
+
+Nelem*
+ncallcheckhit(Nelem* nelem, Image* screen, Rectangle r, Mouse m)
+{
+	assert(nelem);
+	
+	if (nelem->funcs && nelem->funcs->checkhit)
+		return nelem->funcs->checkhit(nelem, screen, r, m);
+	return nil;
+}
+
+int
+ncallhit(Nelem* nelem, Mouse m)
+{
+	assert(nelem);
+	
+	if (nelem->funcs && nelem->funcs->hit)
+		return nelem->funcs->hit(nelem, m);
+	return 0;
+}
+
+Nlist*
+ncallgetchildren(Nelem* nelem)
+{
+	assert(nelem);
+	
+	if (nelem->funcs && nelem->funcs->getchildren)
+		return nelem->funcs->getchildren(nelem);
+	return nil;
+}
+
+Nelem*
+nassign(Nelem** dst, Nelem* src)
+{
+	assert(dst && src);
+	*dst = src;
+	return src;
+}
+
+void
+nateinit()
+{
+	nc_init();
+}
+
+void
+nateredraw()
+{
+	if (!rootelem)
+		return;
+	
+	ncalldraw(rootelem, screen, screen->r);
+}
+
+int
+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);
+	
+	if (!ptinrect(m.xy, r))
+		return 0;
+	
+	el = ncallcheckhit(rootelem, screen, screen->r, m);
+	if (!el)
+		return 0;
+	
+	return ncallhit(el, m);
+}
--- /dev/null
+++ b/nate.h
@@ -1,0 +1,93 @@
+#pragma lib "libnate.a"
+
+typedef struct Nelem Nelem;
+typedef struct Nelemfunctions Nelemfunctions;
+typedef struct Nlist Nlist;
+
+/* user functions
+ ******************/
+
+void nateinit(void);
+void nateredraw(void);
+
+int natemouseevent(Mouse);
+
+#define NAssign(T, A, B) ((T*)(nassign((Nelem**)A, B)))
+Nelem* nassign(Nelem**, Nelem*);
+
+
+/* end user functions
+ *********************/
+
+struct Nelem {
+	char* type;
+	Nelemfunctions* funcs;
+};
+struct Nelemfunctions {
+	Point (*calcsize)(Nelem*, Image*);
+	void (*draw)(Nelem*, Image*, Rectangle);
+	Nelem* (*checkhit)(Nelem*, Image*, Rectangle, Mouse);
+	int (*hit)(Nelem*, Mouse);
+	
+	void (*free)(Nelem*);
+	Nlist* (*getchildren)(Nelem*);
+};
+
+/* nlist functions
+ ******************/
+
+typedef struct Nlistelem Nlistelem;
+struct Nlist {
+	Nlistelem* first;
+};
+struct Nlistelem {
+	Nelem* item;
+	Nlistelem* next;
+};
+
+void linit(Nlist* list);
+int  lhas(Nlist* list, Nelem* item);
+void ladd(Nlist* list, Nelem* item);
+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 lfreelist(Nlist* list);
+
+Nelem* lgetfirst(Nlist* list);
+Nelem* lsetfirst(Nlist* list, Nelem* item);
+
+
+/* helper functions
+ ********************/
+
+// calls on Nelem
+Point ncallcalcsize(Nelem*, Image*);
+void ncalldraw(Nelem*, Image*, Rectangle);
+Nelem* ncallcheckhit(Nelem*, Image*, Rectangle, Mouse);
+int ncallhit(Nelem*, Mouse);
+void ncallfree(Nelem*);
+Nlist* ncallgetchildren(Nelem*);
+
+
+/* root set management
+ ***********************/
+
+// register Nelem as root
+void nregister(Nelem*);
+// deregister Nelem as root (free for collection)
+void nderegister(Nelem*);
+// true if Nelem is root
+int nisroot(Nelem*);
+
+// register root element
+void nregroot(Nelem*);
+
+
+/* more syntactic sugar
+ ***********************/
+
+#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)
+#define DECL_ACCESSOR_ThreeParams(Type, Acc, T1, T2, T3) Type* (*Acc)(T1, T2, T3)
--- /dev/null
+++ b/nate_construct.c
@@ -1,0 +1,95 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+#include "nate_construct.h"
+
+typedef struct NCelem NCelem;
+struct NCelem {
+	Nelem* elem;
+	NCelem* next;
+};
+
+static NCelem* nc_stack = nil;
+
+void
+nc_init()
+{
+	nc_stack = nil;
+}
+
+void
+nc_push(Nelem* nelem)
+{
+	NCelem *n;
+	if (!nc_stack) {
+		nc_stack = malloc(sizeof(NCelem));
+		assert(nc_stack);
+		nc_stack->next = nil;
+		nc_stack->elem = nelem;
+	} else {
+		n = malloc(sizeof(NCelem));
+		assert(n);
+		n->next = nc_stack;
+		n->elem = nelem;
+		nc_stack = n;
+	}
+}
+
+void
+nc_pop()
+{
+	if (!nc_stack)
+		return;
+	NCelem *nc = nc_stack;
+	nc_stack = nc->next;
+	free(nc);
+}
+
+Nelem*
+nc_get()
+{
+	return nc_stack ? nc_stack->elem : nil;
+}
+
+Nelem* ch_retelem;
+Rectangle ch_rect;
+Image* ch_screen;
+Mouse ch_mouse;
+
+void
+fe_checkhit(Nelem* elem, int)
+{
+	Rectangle r;
+	Nelem* e;
+	Point s;
+	
+	s = ncallcalcsize(elem, ch_screen);
+	r.min = ch_rect.min;
+	r.max = addpt(r.min, s);
+	
+	if (!ptinrect(ch_mouse.xy, r))
+		return;
+	
+	e = ncallcheckhit(elem, ch_screen, ch_rect, ch_mouse);
+	if (e) {
+		ch_retelem = e;
+	}
+}
+
+Nelem*
+nd_checkhit(Nelem* nelem, Image* screen, Rectangle r, Mouse m)
+{
+	Nlist* l = ncallgetchildren(nelem);
+	
+	if (!l)
+		return nil;
+	
+	ch_retelem = nil;
+	ch_rect = r;
+	ch_screen = screen;
+	ch_mouse = m;
+	lforeach(l, fe_checkhit);
+	
+	return ch_retelem;
+}
--- /dev/null
+++ b/nate_construct.h
@@ -1,0 +1,16 @@
+#include "nate.h"
+
+void nc_push(Nelem*);
+void nc_pop(void);
+Nelem* nc_get(void);
+
+// #define N_TYPE in your implementation
+#define COND(N) (N->type == N_TYPE)
+#define GUARD(N) assert(COND(N))
+
+
+// internal stuff
+void nc_init(void);
+
+// default functionality
+Nelem* nd_checkhit(Nelem* nelem, Image* screen, Rectangle r, Mouse m);
--- /dev/null
+++ b/test/mkfile
@@ -1,0 +1,6 @@
+</$objtype/mkfile
+
+TARG=ntest
+OFILES=ntest.$O
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/test/ntest.c
@@ -1,0 +1,106 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+#include "../nate.h"
+#include "../n_window.h"
+#include "../n_hbox.h"
+#include "../n_box.h"
+#include "../n_label.h"
+
+char*
+getlabel(void)
+{
+	return "ABC";
+}
+
+NBox *box1;
+NBox *box2;
+
+int
+callclick(Mouse, Nelem* el, void*)
+{
+	int id = 0;
+	if (el == box1)
+		id = 1;
+	if (el == box2)
+		id = 2;
+	fprint(2, "click: %s (%d)\n", el->type, id);
+	return 1;
+}
+
+void
+eresized(int new)
+{
+	if (new && getwindow(display, Refnone) < 0)
+		sysfatal("getwindow: %r");
+	
+	nateredraw();
+}
+
+void
+main(int argc, char **argv)
+{
+	USED(argc, argv);
+	Nelem* mainwindow;
+	Event ev;
+	int e;
+	
+	if (initdraw(nil, nil, "nate test") < 0)
+		sysfatal("initdraw: %r");
+	
+	einit(Emouse);
+	nateinit();
+	
+	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())
+	->MakeRoot()
+	->Slot(
+		New_Box()
+		->SizeToContent(1)
+		->Border(1, red)
+		->Slot(
+			New_HBox()
+			->SizeToContent(1)
+			->Slot(
+				NAssign(NBox, &box1, New_Box())
+				->Border(1, green)
+				->SizeToContent(1)
+				->Slot(
+					New_Label()
+					->LabelFunc(getlabel)
+				)
+			)
+			->Slot(
+				NAssign(NBox, &box2, New_Box())
+				->Border(1, blue)
+				->SizeToContent(1)
+				->OnClick(callclick, nil) // TODO: somehow on wrong element!
+				->Slot(
+					New_Label()
+					->Label("DEF")
+				)
+			)
+		)
+	);
+	
+	eresized(0);
+	
+	for (;;) {
+		e = event(&ev);
+		
+		switch (e) {
+		case Emouse:
+			if (ev.mouse.buttons & 4)
+				exits(nil);
+			if (ev.mouse.buttons & 1)
+				natemouseevent(ev.mouse);
+			break;
+		default:
+			break;
+		}
+	}
+}