jancity/src/gui/src/rounded_rect.c

317 lines
7.2 KiB
C

#include <gui/rounded_rect.h>
#include <gui.h>
#include <gui_private.h>
#include <gfx.h>
struct sprite gui_rounded_rect_sprites[MAX_GUI_ROUNDED_RECT_SPRITES];
/* Alias for readability. */
static const struct sprite *const refs = gui_rounded_rect_sprites;
static int render_top(const struct gui_rounded_rect *const r,
const short x, const short y)
{
{
sprite_get_or_ret(left, -1);
if (sprite_clone(&refs[GUI_ROUNDED_RECT_UP_LEFT], left))
return -1;
left->x = x;
left->y = y;
if (sprite_sort(left))
{
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return -1;
}
}
{
sprite_get_or_ret(right, -1);
const struct sprite *const ref =
&refs[GUI_ROUNDED_RECT_UP_RIGHT];
if (sprite_clone(ref, right))
return -1;
right->x = x + r->w - ref->w;
right->y = y;
if (sprite_sort(right))
{
fprintf(stderr, "%s: sprite_sort failed\n", __func__);
return -1;
}
}
return 0;
}
static int render_vert(const struct gui_rounded_rect *const r,
const short x, const short y)
{
const struct sprite *const vert = &refs[GUI_ROUNDED_RECT_MID_VERT];
const short top_h = refs[GUI_ROUNDED_RECT_UP_LEFT].h,
h = r->h - (top_h * 2),
rem_vert = h % vert->h,
whole_vert = h / vert->h,
n_vert = rem_vert ? whole_vert + 1 : whole_vert;
for (struct
{
size_t i;
short y;
} a = {.y = y + top_h}; a.i < n_vert; a.i++, a.y += vert->h)
{
sprite_get_or_ret(v, -1);
if (sprite_clone(vert, v))
return -1;
v->y = a.y;
v->x = x;
if (rem_vert && a.i + 1 == n_vert)
v->h = rem_vert;
else
v->h = vert->h;
sprite_sort(v);
}
return 0;
}
static int render_vertleft(const struct gui_rounded_rect *const r,
const short x, const short y)
{
return render_vert(r, x, y);
}
static int render_vertright(const struct gui_rounded_rect *const r,
const short x, const short y)
{
const struct sprite *const ref = &refs[GUI_ROUNDED_RECT_MID_VERT];
return render_vert(r, x + r->w - ref->w, y);
}
static int render_down(const struct gui_rounded_rect *const r,
const short x, const short y)
{
{
const struct sprite *const ref = &refs[GUI_ROUNDED_RECT_DOWN_LEFT];
sprite_get_or_ret(left, -1);
if (sprite_clone(ref, left))
return -1;
left->x = x;
left->y = y + r->h - ref->h;
sprite_sort(left);
}
{
const struct sprite *const ref = &refs[GUI_ROUNDED_RECT_DOWN_RIGHT];
sprite_get_or_ret(right, -1);
if (sprite_clone(ref, right))
return -1;
right->x = x + r->w - ref->w;
right->y = y + r->h - ref->h;
sprite_sort(right);
}
return 0;
}
static int render_rect(const struct gui_rounded_rect *const r,
const short x, const short y)
{
rect_get_or_ret(sel, -1);
semitrans_rect_init(sel);
const struct sprite *const mid = &refs[GUI_ROUNDED_RECT_MID_TOP],
*const vert = &refs[GUI_ROUNDED_RECT_MID_VERT];
sel->x = x + vert->w;
sel->y = y + mid->h;
sel->w = r->w - (vert->w * 2);
sel->h = r->h - (mid->h * 2);
sel->r = r->r;
sel->g = r->g;
sel->b = r->b;
rect_sort(sel);
return 0;
}
static int render_mid(const struct gui_rounded_rect *const r,
const short x, const short y, const struct sprite *const s)
{
const short mid_w = s->w,
top_w = refs[GUI_ROUNDED_RECT_UP_LEFT].w,
w = r->w - (top_w * 2),
rem_mid = w % mid_w,
whole_mid = w / mid_w,
n_mid = rem_mid ? whole_mid + 1 : whole_mid;
if (n_mid < 0)
{
fprintf(stderr, "%s: invalid dimensions\n", __func__);
return -1;
}
for (struct
{
size_t i;
short x;
} a = {.x = x + top_w}; a.i < n_mid; a.i++, a.x += mid_w)
{
sprite_get_or_ret(m, -1);
if (sprite_clone(s, m))
return -1;
m->x = a.x;
m->y = y;
if (rem_mid && a.i + 1 == n_mid)
m->w = rem_mid;
else
m->w = mid_w;
sprite_sort(m);
}
return 0;
}
static int render_midtop(const struct gui_rounded_rect *const r,
const short x, const short y)
{
return render_mid(r, x, y, &refs[GUI_ROUNDED_RECT_MID_TOP]);
}
static int render_middown(const struct gui_rounded_rect *const r,
const short x, const short y)
{
const struct sprite *const s = &refs[GUI_ROUNDED_RECT_MID_DOWN];
return render_mid(r, x, y + r->h - s->h, s);
}
static int render(const struct gui_common *const g)
{
const struct gui_rounded_rect *const r = (const struct gui_rounded_rect *)g;
short x, y;
gui_coords(&r->common, &x, &y);
static int (*const steps[])(const struct gui_rounded_rect *const r,
const short x, const short y) =
{
render_top,
render_rect,
render_midtop,
render_middown,
render_vertleft,
render_vertright,
render_down
};
for (size_t i = 0; i < sizeof steps / sizeof *steps; i++)
if (steps[i](r, x, y))
return -1;
return 0;
}
static void get_dim(const struct gui_common *const g, short *const w,
short *const h)
{
const struct gui_rounded_rect *const r =
(const struct gui_rounded_rect *)g;
*w = r->w;
*h = r->h;
}
static void add_child(struct gui_common *const parent,
struct gui_common *const child)
{
struct gui_rounded_rect *const r = (struct gui_rounded_rect *)parent;
if (!r->adjust || child->hidden || !child->cb || !child->cb->get_dim)
return;
short w, h;
const short ref_w = refs[GUI_ROUNDED_RECT_MID_VERT].w,
ref_h = refs[GUI_ROUNDED_RECT_MID_TOP].h,
min_w = ref_w * 2 + refs[GUI_ROUNDED_RECT_MID_TOP].w,
min_h = ref_h * 2 + refs[GUI_ROUNDED_RECT_MID_VERT].h;
child->cb->get_dim(child, &w, &h);
if (w > r->w - (ref_w * 2))
r->w = w + (ref_w * 2);
if (h > r->h - (ref_h * 2))
r->h = h + (ref_h * 2);
if (r->w < min_w)
r->w = min_w;
if (r->h < min_h)
r->h = min_h;
if (!child->hcentered)
child->xoff = ref_w;
if (!child->vcentered)
child->yoff = ref_h;
}
static int update(struct gui_common *const g,
const union peripheral *const p, const struct camera *const cam,
struct input *const in)
{
struct gui_rounded_rect *const r = (struct gui_rounded_rect *)g;
if (r->adjust)
{
r->w = r->h = 0;
struct gui_common *const child = r->common.child;
if (child && child->cb && child->cb->get_dim)
add_child(&r->common, child);
for (struct gui_common *s = child->sibling; s; s = s->sibling)
add_child(&r->common, s);
}
return 0;
}
void gui_rounded_rect_init(struct gui_rounded_rect *const r)
{
static const struct gui_common_cb cb =
{
.get_dim = get_dim,
.update = update,
.render = render
};
*r = (const struct gui_rounded_rect)
{
.common =
{
.cb = &cb
}
};
}