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;
+ }
+ }
+}