shithub: libnate

ref: aab184987c79fc7a8e5467200f9ac448700b797f
dir: /n_box.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include "nate.h"
#include "nate_construct.h"
#include "n_box.h"

#define N_TYPE NBox_Type
char* NBox_Type = "NBox";

static Rectangle
box_calcrect(Nelem* nelem, Image* screen, Rectangle r)
{
	Rectangle cr;
	NBox* b = (NBox*)nelem;
	GUARD(b);
	
	Nelem *child = lgetfirst(&b->children);
	cr.min = r.min;
	if (b->size.x >= 0 && b->size.y >= 0) {
		cr.max = addpt(cr.min, Pt(b->size.x, b->size.y));
	} else
	if (child) {
		cr.max = addpt(cr.min, ncalldesiredsize(child, screen));
		cr.max.x += 2*b->borderwidth;
		cr.max.y += 2*b->borderwidth;
	} else {
		cr.max = addpt(r.min, Pt(2*b->borderwidth, 2*b->borderwidth));
	}
	cr = extendmargin(cr, b->padding);
	
	if (b->slot.fill&FILLX)
		cr.max.x = r.max.x;
	if (b->slot.fill&FILLY)
		cr.max.y = r.max.y;
	
	b->slot.r = cr;
	
	/* tell child its size (important!) */
	if (child) {
		cr = insetmargin(insetrect(cr, b->borderwidth), b->padding);
		ncallcalcrect(child, screen, cr);
	}
	return b->slot.r;
}

static Point
box_desiredsize(Nelem *nelem, Image *screen)
{
	Point pt;
	Nelem *child;
	NBox *b = (NBox*)nelem;
	GUARD(b);
	
	if (b->size.x >= 0 && b->size.y >= 0)
		return b->size;
	
	child = lgetfirst(&b->children);
	if (child)
		pt = ncalldesiredsize(child, screen);
	pt.x += 2*b->borderwidth + b->padding.left + b->padding.right;
	pt.y += 2*b->borderwidth + b->padding.top + b->padding.bottom;
	return pt;
}

static void
box_draw(Nelem* nelem, Image* img)
{
	Nelem* f;
	Rectangle r;
	NBox* b = (NBox*)nelem;
	GUARD(b);
	f = lgetfirst(&b->children);
	if (!f)
		return;

	r = b->slot.r;

	if (b->borderwidth > 0)
		border(img, r, b->borderwidth, b->bordercolor, ZP);
	ncalldraw(f, img);
}

static Nelem*
box_checkhit(Nelem *nelem, Image *screen, Mouse m)
{
	NBox *b = (NBox*)nelem;
	GUARD(b);
	
	if (!b->hitfunc)
		return nd_checkhit(nelem, screen, m);
	
	if (ptinrect(m.xy, b->slot.r))
		return b;
	return nil;
}

static int
box_hit(Nelem* nelem, Mouse m)
{
	NBox* b = (NBox*)nelem;
	GUARD(b);
	int b1 = m.buttons&1;
	
	// TODO: behaviour when pressed down and releasing on another box?
	// TODO:   drag-and-drop?
	
	if (b->ishit == b1) {
		/* no state change */
		return 0;
	}
	if (b->ishit && !b1) {
		/* released */
		b->ishit = 0;
		return 0;
	}
	if (!b->ishit && b1) {
		/* pressed */
		b->ishit = 1;
		if (b->hitfunc)
			return b->hitfunc(m, b, b->hitaux);
		return 0;
	}
	/* cannot happen */
	assert(0);
	return 1;
}

static char*
box_getname(Nelem *nelem)
{
	Nelem *ch;
	NBox *b = (NBox*)nelem;
	GUARD(b);
	ch = lgetfirst(&b->children);
	if (!(ch && ch->funcs && ch->funcs->getname))
		return b->name;
	return ch->funcs->getname(ch);
}

static Nelemfunctions Nboxfunctions = {
	.calcrect = box_calcrect,
	.desiredsize = box_desiredsize,
	.draw = box_draw,
	.checkhit = box_checkhit,
	.hit = box_hit,
	.getname = box_getname,
};

#define NTYPE NBox
#define NACCS NBoxAccessors

DEF_SLOTFUNC(box_slot);
DEF_ACCESSOR_TwoParams(box_border, int, borderwidth, Image*, bordercolor);
DEF_ACCESSOR_OneParam(box_autosize, int, autosize);
DEF_ACCESSOR_OneParam(box_size, Point, size);
DEF_ACCESSOR_TwoParams(box_onclick, OnclickHandler, hitfunc, void*, hitaux);
DEF_ACCESSOR_OneParam(box_padding, Nmargin, padding);

static NBoxAccessors accs = {
	.Slot = box_slot,
	.Border = box_border,
	.AutoSize = box_autosize,
	.Size = box_size,
	.OnClick = box_onclick,
	.Padding = box_padding,
};

NBoxAccessors*
New_Box(char *name)
{
	NBox *b = MakeNelem(NBox, NBox_Type, &Nboxfunctions, &accs, name, 1);
	
	b->autosize = 0;
	b->size = Pt(-1, -1);
	b->hitfunc = nil;
	b->hitaux = nil;
	b->borderwidth = 0;
	b->bordercolor = display->black;
	nc_push(b);
	return &accs;
}