Compare commits

...

10 Commits

Author SHA1 Message Date
Xavier Del Campo Romero bebf6d6b5f font.c: set null dimensions on null str 2022-07-02 04:50:50 +02:00
Xavier Del Campo Romero 7bc58ab0e6 human_player_gui.c: use gui_container for top gui_bar 2022-07-02 04:47:50 +02:00
Xavier Del Campo Romero 7325906351 gui/container: support spacing between GUI elements 2022-07-02 04:42:41 +02:00
Xavier Del Campo Romero 93d2c39b1b gui/bar: support arbitrary x/y and width 2022-07-02 04:41:22 +02:00
Xavier Del Campo Romero b0eb562b9b gui: reuse callback data
There is no need to allocate memory for these callbacks for each single
GUI element. Instead, a single, statically-allocated instance can be
shared among all GUI elements of a given type.
2022-07-02 04:08:35 +02:00
Xavier Del Campo Romero a71ce37929 menu: implement main menu using new GUI improvements 2022-07-02 00:58:50 +02:00
Xavier Del Campo Romero e68c2fb4be gui: implement container
Containers, somewhat inspired by GtkBox, allow to packed other GUI
elements into a single row or column.
2022-07-02 00:53:03 +02:00
Xavier Del Campo Romero 04b9219ee5 button.c: h/v-center child label by default 2022-07-02 00:50:26 +02:00
Xavier Del Campo Romero 033ed5fb94 gui.c: fix wrong logic on gui_coords
The older logic would iterate for all parents, where each parent would
again iterate for all of its parents, until no more parents found.
This is however not needed, since only inspecting the closest parent
will already cause the (recursive) algorithm to iterate for all parents.
2022-07-02 00:49:36 +02:00
Xavier Del Campo Romero ec9f41f1ab gui: allow custom add_child callback 2022-07-02 00:45:51 +02:00
14 changed files with 250 additions and 56 deletions

View File

@ -9,6 +9,17 @@ static int renderstr(const enum font f, const short x, short y,
const char *str, const bool render, short *const max_x,
short *const max_y)
{
if (!str)
{
if (max_x)
*max_x = 0;
if (max_y)
*max_y = 0;
return 0;
}
static const struct cfg
{
const struct sprite *s;

View File

@ -1,6 +1,7 @@
add_library(gui
"src/bar.c"
"src/button.c"
"src/container.c"
"src/gui.c"
"src/label.c"
"src/progress_bar.c"

View File

@ -12,10 +12,15 @@ extern "C"
struct gui_common
{
int (*update)(struct gui_common *, const union peripheral *,
const struct camera *);
int (*render)(const struct gui_common *);
void (*get_dim)(const struct gui_common *, short *w, short *h);
const struct gui_common_cb
{
void (*add_child)(struct gui_common *parent, struct gui_common *child);
int (*update)(struct gui_common *, const union peripheral *,
const struct camera *);
int (*render)(const struct gui_common *);
void (*get_dim)(const struct gui_common *, short *w, short *h);
} *cb;
short x, y;
bool hcentered, vcentered;
struct gui_common *parent, *child, *sibling;

View File

@ -14,11 +14,9 @@ extern "C"
struct gui_bar
{
struct gui_common common;
short w;
};
UTIL_STATIC_ASSERT(!offsetof(struct gui_bar, common),
"unexpected offset for struct gui_bar");
void gui_bar_init(struct gui_bar *b);
enum
@ -30,6 +28,9 @@ enum
MAX_GUI_BAR_SPRITES
};
UTIL_STATIC_ASSERT(!offsetof(struct gui_bar, common),
"unexpected offset for struct gui_bar");
extern struct sprite gui_bar_sprites[MAX_GUI_BAR_SPRITES];
#ifdef __cplusplus

View File

@ -0,0 +1,34 @@
#ifndef GUI_CONTAINER_H
#define GUI_CONTAINER_H
#include <gui.h>
#include <util.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif
struct gui_container
{
struct gui_common common;
short w, h, spacing;
enum
{
GUI_CONTAINER_MODE_H,
GUI_CONTAINER_MODE_V
} mode;
};
UTIL_STATIC_ASSERT(!offsetof(struct gui_container, common),
"unexpected offset for struct gui_container");
void gui_container_init(struct gui_container *c);
#ifdef __cplusplus
}
#endif
#endif /* GUI_CONTAINER_H */

View File

@ -1,48 +1,57 @@
#include <gui/bar.h>
#include <gui_private.h>
#include <gui.h>
#include <gfx.h>
struct sprite gui_bar_sprites[MAX_GUI_BAR_SPRITES];
static int render_topleft(void)
static int render_topleft(const struct gui_bar *const b, const short x,
const short y)
{
sprite_get_or_ret(s, -1);
if (sprite_clone(&gui_bar_sprites[GUI_BAR_LEFT], s))
return -1;
s->x = x;
s->y = y;
sprite_sort(s);
return 0;
}
static int render_topright(void)
static int render_topright(const struct gui_bar *const b, const short x,
const short y)
{
sprite_get_or_ret(s, -1);
if (sprite_clone(&gui_bar_sprites[GUI_BAR_RIGHT], s))
return -1;
s->x = screen_w - s->w;
s->x = x + b->w - s->w;
s->y = y;
sprite_sort(s);
return 0;
}
static int render_topmid(void)
static int render_topmid(const struct gui_bar *const b, const short x,
const short y)
{
const uint16_t mid_w = gui_bar_sprites[GUI_BAR_MID].w;
const uint16_t lw = gui_bar_sprites[GUI_BAR_LEFT].w;
const size_t w = screen_w - lw - lw;
const size_t rem_mid = w % mid_w;
const size_t whole_mid = w / mid_w;
const size_t n_mid = rem_mid ? whole_mid + 1 : whole_mid;
const short mid_w = gui_bar_sprites[GUI_BAR_MID].w,
lw = gui_bar_sprites[GUI_BAR_LEFT].w,
w = b->w - lw - lw;
if (w <= 0)
return -1;
const short rem_mid = w % mid_w,
whole_mid = w / mid_w,
n_mid = rem_mid ? whole_mid + 1 : whole_mid;
for (struct
{
size_t i;
short x;
} a = {.i = 0, .x = lw};
a.i < n_mid;
a.i++, a.x += mid_w)
} a = {.x = x + lw}; a.i < n_mid; a.i++, a.x += mid_w)
{
sprite_get_or_ret(m, -1);
@ -50,6 +59,7 @@ static int render_topmid(void)
return -1;
m->x = a.x;
m->y = y;
if (rem_mid && a.i + 1 == n_mid)
m->w = rem_mid;
@ -64,21 +74,41 @@ static int render_topmid(void)
static int render(const struct gui_common *const g)
{
if (render_topleft()
|| render_topright()
|| render_topmid())
const struct gui_bar *const b = (const struct gui_bar *)g;
short x, y;
gui_coords(&b->common, &x, &y);
if (render_topleft(b, x, y)
|| render_topright(b, x, y)
|| render_topmid(b, 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_bar *const b = (const struct gui_bar *)g;
*w = b->w;
*h = gui_bar_sprites[GUI_BAR_MID].h;
}
void gui_bar_init(struct gui_bar *const b)
{
static const struct gui_common_cb cb =
{
.render = render,
.get_dim = get_dim
};
*b = (const struct gui_bar)
{
.common =
{
.render = render
.cb = &cb
}
};
}

View File

@ -164,16 +164,23 @@ static void get_dim(const struct gui_common *const g,
void gui_button_init(struct gui_button *const b)
{
static const struct gui_common_cb cb =
{
.get_dim = get_dim,
.update = update,
.render = render
};
*b = (const struct gui_button)
{
.common =
{
.get_dim = get_dim,
.update = update,
.render = render
.cb = &cb
}
};
gui_label_init(&b->label);
b->label.common.hcentered = true;
b->label.common.vcentered = true;
gui_add_child(&b->common, &b->label.common);
}

67
src/gui/src/container.c Normal file
View File

@ -0,0 +1,67 @@
#include <gui/container.h>
#include <gui.h>
static void add_child(struct gui_common *const parent,
struct gui_common *const child)
{
if (!child->cb || !child->cb->get_dim)
return;
struct gui_container *const c = (struct gui_container *)parent;
short w, h;
child->cb->get_dim(child, &w, &h);
switch (c->mode)
{
case GUI_CONTAINER_MODE_H:
if (c->w)
c->w += c->spacing;
child->x += c->w;
c->w += w;
if (h > c->h)
c->h = h;
break;
case GUI_CONTAINER_MODE_V:
if (c->h)
c->h += c->spacing;
child->y += c->h;
c->h += h;
if (w > c->w)
c->w = w;
break;
}
}
static void get_dim(const struct gui_common *const g,
short *const w, short *const h)
{
struct gui_container *const c = (struct gui_container *)g;
*w = c->w;
*h = c->h;
}
void gui_container_init(struct gui_container *const c)
{
static const struct gui_common_cb cb =
{
.add_child = add_child,
.get_dim = get_dim
};
*c = (const struct gui_container)
{
.common =
{
.cb = &cb
}
};
}

View File

@ -3,14 +3,14 @@
static void get_centered(const struct gui_common *const g,
short *const x, short *const y)
{
if (g->get_dim)
if (g->cb && g->cb->get_dim)
{
short w, h, pw = 0, ph = 0;
if (g->parent)
{
if (g->parent->get_dim)
g->parent->get_dim(g->parent, &pw, &ph);
if (g->parent->cb && g->parent->cb->get_dim)
g->parent->cb->get_dim(g->parent, &pw, &ph);
}
else
{
@ -18,7 +18,7 @@ static void get_centered(const struct gui_common *const g,
ph = screen_h;
}
g->get_dim(g, &w, &h);
g->cb->get_dim(g, &w, &h);
if (g->hcentered)
*x += (pw - w) / 2;
@ -34,7 +34,9 @@ void gui_coords(const struct gui_common *const g, short *const x,
*x = g->x;
*y = g->y;
for (const struct gui_common *p = g->parent; p; p = p->parent)
const struct gui_common *p = g->parent;
if (p)
{
short px = p->x, py = p->y;
@ -69,12 +71,15 @@ void gui_add_child(struct gui_common *const p,
p->child = c;
c->parent = p;
if (p->cb && p->cb->add_child)
p->cb->add_child(p, c);
}
int gui_update(struct gui_common *const g, const union peripheral *const p,
const struct camera *const c)
{
if (g->update && g->update(g, p, c))
if (g->cb && g->cb->update && g->cb->update(g, p, c))
return -1;
if (g->child && gui_update(g->child, p, c))
@ -89,7 +94,7 @@ int gui_update(struct gui_common *const g, const union peripheral *const p,
int gui_render(const struct gui_common *const g)
{
if (g->render && g->render(g))
if (g->cb && g->cb->render && g->cb->render(g))
return -1;
if (g->child && gui_render(g->child))

View File

@ -23,12 +23,17 @@ static void get_dim(const struct gui_common *const g,
void gui_label_init(struct gui_label *const l)
{
static const struct gui_common_cb cb =
{
.get_dim = get_dim,
.render = render
};
*l = (const struct gui_label)
{
.common =
{
.get_dim = get_dim,
.render = render
.cb = &cb
}
};
}

View File

@ -83,11 +83,16 @@ static int render(const struct gui_common *const g)
void gui_progress_bar_init(struct gui_progress_bar *const pb)
{
static const struct gui_common_cb cb =
{
.render = render
};
*pb = (const struct gui_progress_bar)
{
.common =
{
.render = render
.cb = &cb
}
};
}

View File

@ -224,12 +224,17 @@ static void get_dim(const struct gui_common *const g, short *const w,
void gui_rounded_rect_init(struct gui_rounded_rect *const r)
{
static const struct gui_common_cb cb =
{
.get_dim = get_dim,
.render = render
};
*r = (const struct gui_rounded_rect)
{
.common =
{
.get_dim = get_dim,
.render = render
.cb = &cb
}
};
}

View File

@ -4,6 +4,7 @@
#include <gfx.h>
#include <gui.h>
#include <gui/button.h>
#include <gui/container.h>
#include <system.h>
#include <stdbool.h>
@ -15,22 +16,35 @@ static void on_pressed(void *const arg)
int menu(void)
{
struct camera cam = {0};
struct gui_button play;
struct gui_button play, exit_btn;
struct gui_container cnt;
union peripheral p;
bool start = false;
bool start = false, exit = false;
if (game_resinit())
return -1;
cursor_init(&cam.cursor);
gui_container_init(&cnt);
cnt.mode = GUI_CONTAINER_MODE_V;
cnt.common.hcentered = true;
cnt.common.vcentered = true;
gui_button_init(&play);
gui_button_init(&exit_btn);
play.on_pressed = on_pressed;
play.arg = &start;
play.w = 140;
play.common.hcentered = true;
play.common.vcentered = true;
play.label.text = "Play";
exit_btn.arg = &exit;
exit_btn.w = 140;
exit_btn.common.hcentered = true;
exit_btn.label.text = "Exit";
exit_btn.on_pressed = on_pressed;
gui_add_child(&cnt.common, &play.common);
gui_add_child(&cnt.common, &exit_btn.common);
{
const struct peripheral_cfg cfg =
{
@ -46,9 +60,6 @@ int menu(void)
peripheral_update(&p);
camera_update(&cam, &p);
play.label.common.x = play.w / 2 - 20;
play.label.common.y = 4;
if (gui_update(&play.common, &p, &cam))
return -1;
@ -58,10 +69,10 @@ int menu(void)
r->h = screen_h;
rect_sort(r);
if (p.common.exit)
if (p.common.exit || exit)
return 0;
if (gui_render(&play.common)
if (gui_render(&cnt.common)
|| cursor_render(&cam.cursor)
|| gfx_draw())
return -1;

View File

@ -5,6 +5,7 @@
#include <unit.h>
#include <gui.h>
#include <gui/bar.h>
#include <gui/container.h>
#include <gui/label.h>
#include <gui/progress_bar.h>
#include <gui/rounded_rect.h>
@ -265,9 +266,18 @@ static int render_sel(const struct human_player *const h)
static int render_top(const struct human_player *const h)
{
const struct player *const pl = &h->pl;
struct gui_container c;
struct gui_bar b;
gui_bar_init(&b);
b.w = screen_w;
gui_container_init(&c);
c.mode = GUI_CONTAINER_MODE_H;
c.spacing = 16;
c.common.hcentered = true;
c.common.vcentered = true;
gui_add_child(&b.common, &c.common);
char wood_str[sizeof "Wood=429496729"];
{
@ -281,10 +291,9 @@ static int render_top(const struct human_player *const h)
struct gui_label wl;
gui_label_init(&wl);
wl.common.x = 16;
wl.common.y = 6;
wl.common.vcentered = true;
wl.text = wood_str;
gui_add_child(&b.common, &wl.common);
gui_add_child(&c.common, &wl.common);
char gold_str[sizeof "Gold=429496729"];
@ -299,10 +308,9 @@ static int render_top(const struct human_player *const h)
struct gui_label gl;
gui_label_init(&gl);
gl.common.x = 108;
gl.common.y = 6;
wl.common.vcentered = true;
gl.text = gold_str;
gui_add_child(&b.common, &gl.common);
gui_add_child(&c.common, &gl.common);
char pop_str[sizeof "Pop.=255/255"];
@ -317,10 +325,9 @@ static int render_top(const struct human_player *const h)
struct gui_label popl;
gui_label_init(&popl);
popl.common.x = 212;
popl.common.y = 6;
wl.common.vcentered = true;
popl.text = pop_str;
gui_add_child(&b.common, &popl.common);
gui_add_child(&c.common, &popl.common);
if (gui_render(&b.common))
return -1;