shithub: libnate

ref: a578dcbab74f736509dbe3e8fc1cefaa9919132c
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;
	Point childsize;
	NBox* b = (NBox*)nelem;
	GUARD(b);
	
	Nelem *child = lgetfirst(&b->children);
	cr.min = r.min;
	
	if (b->slot.fill&FILLX && b->slot.fill&FILLY) {
		b->slot.r = cr = r;
		if (child) {
			cr = insetmargin(insetrect(cr, b->borderwidth), b->padding);
			ncallcalcrect(child, screen, cr);
		}
		return b->slot.r;
	}
	
	if (b->slot.fill&FILLX)
		cr.max.x = r.max.x;
	if (b->slot.fill&FILLY)
		cr.max.y = r.max.y;
	
	if (child)
		childsize = ncalldesiredsize(child, screen);
	
	if (!(b->slot.fill&FILLX)) {
		if (b->size.x >= 0) {
			cr.max.x = cr.min.x + b->size.x;
		} else if (child) {
			cr.max.x = cr.min.x + childsize.x;
		} else {
			cr.max.x = cr.min.x;
		}
		cr.max.x += 2*b->borderwidth + b->padding.left + b->padding.right;
	}
	
	if (!(b->slot.fill&FILLY)) {
		if (b->size.y >= 0) {
			cr.max.y = cr.min.y + b->size.y;
		} else if (child) {
			cr.max.y = cr.min.y + childsize.y;
		} else {
			cr.max.y = cr.min.y;
		}
		cr.max.y += 2*b->borderwidth + b->padding.top + b->padding.bottom;
	}
	
	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) {
		pt = b->size;
		pt.x += 2*b->borderwidth + b->padding.left + b->padding.right;
		pt.y += 2*b->borderwidth + b->padding.top + b->padding.bottom;
		return pt;
	}
	
	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;
}