317 lines
7.2 KiB
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
|
|
}
|
|
};
|
|
}
|