Mercurial > molko
changeset 395:ef2fc4442ed5
ui: don't use fixed size arrays
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 18 Feb 2022 16:02:01 +0100 |
parents | 8273c40a5691 |
children | c9769a77ad8c |
files | examples/example-gridmenu/main.c src/libmlk-rpg/rpg/battle-bar-default.c src/libmlk-rpg/rpg/battle-bar-default.h src/libmlk-ui/ui/gridmenu.c src/libmlk-ui/ui/gridmenu.h |
diffstat | 5 files changed, 172 insertions(+), 203 deletions(-) [+] |
line wrap: on
line diff
--- a/examples/example-gridmenu/main.c Fri Feb 18 16:00:53 2022 +0100 +++ b/examples/example-gridmenu/main.c Fri Feb 18 16:02:01 2022 +0100 @@ -58,30 +58,20 @@ static void handle(struct state *st, const union event *ev) { + struct gridmenu *menu = st->data; + switch (ev->type) { case EVENT_QUIT: game_quit(); break; default: - gridmenu_handle(st->data, ev); + if (gridmenu_handle(st->data, ev)) + tracef("selected index: %zu (%s)", menu->selected, menu->items[menu->selected]); break; } } static void -update(struct state *st, unsigned int ticks) -{ - (void)ticks; - - struct gridmenu *menu = st->data; - - if (menu->state == GRIDMENU_STATE_ACTIVATED) { - tracef("selected index: %u", (unsigned int)menu->selected); - gridmenu_reset(menu); - } -} - -static void draw(struct state *st) { painter_set_color(0x4f8fbaff); @@ -93,37 +83,37 @@ static void run(void) { - struct gridmenu menu = { - .menu = { - "Feu mineur", - "Feu majeur", - "Feu septième", - "Glace mineure", - "Glace majeure", - "Glace septième", - "Foudre mineure", - "Foudre majeure", - "Foudre septième", - "Choc mineur", - "Choc majeur", - "Choc septième", - }, - .w = 300, - .h = 100, - .nrows = 3, - .ncols = 2 + const char * const items[] = { + "Feu mineur", + "Feu majeur", + "Feu septième", + "Glace mineure", + "Glace majeure", + "Glace septième", + "Foudre mineure", + "Foudre majeure", + "Foudre septième", + "Choc mineur", + "Choc majeur", + "Choc septième", + "Portée", + "Escapade", + "Destruction", + "Résurrection", + "Double tour" }; + + struct gridmenu menu = {0}; struct state state = { .data = &menu, .handle = handle, - .update = update, .draw = draw, }; - align(ALIGN_CENTER, &menu.x, &menu.y, menu.w, menu.h, 0, 0, W, H); + gridmenu_init(&menu, 3, 2, items, UTIL_SIZE(items)); + gridmenu_resize(&menu, 0, 0, 300, 100); - /* Need to repaint at least once. */ - gridmenu_repaint(&menu); + align(ALIGN_CENTER, &menu.x, &menu.y, menu.w, menu.h, 0, 0, W, H); game_init(states, UTIL_SIZE(states)); game_push(&state);
--- a/src/libmlk-rpg/rpg/battle-bar-default.c Fri Feb 18 16:00:53 2022 +0100 +++ b/src/libmlk-rpg/rpg/battle-bar-default.c Fri Feb 18 16:02:01 2022 +0100 @@ -127,9 +127,6 @@ const struct spell *sp = ch->spells[bar->sub_grid.selected]; struct selection sel = {0}; - /* Don't forget to reset the gridmenu state. */ - gridmenu_reset(&bar->sub_grid); - if (bar->sub_grid.selected > CHARACTER_SPELL_MAX) return; if (!(sp = ch->spells[bar->sub_grid.selected]) || sp->mp > (unsigned int)(ch->mp)) @@ -412,13 +409,14 @@ { /* Go back to main menu if I press escape. */ if (ev->key.key == KEY_ESCAPE) { - gridmenu_reset(&bar->sub_grid); + //gridmenu_reset(&bar->sub_grid); bar->state = BATTLE_BAR_DEFAULT_STATE_MENU; return; } gridmenu_handle(&bar->sub_grid, ev); +#if 0 if (bar->sub_grid.state == GRIDMENU_STATE_ACTIVATED) { gridmenu_reset(&bar->sub_grid); @@ -433,6 +431,7 @@ break; } } +#endif } static void @@ -488,7 +487,7 @@ bar->sub_grid.nrows = 3; bar->sub_grid.ncols = 4; - memset(bar->sub_grid.menu, 0, sizeof (bar->sub_grid.menu)); + //memset(bar->sub_grid.menu, 0, sizeof (bar->sub_grid.menu)); } static void @@ -500,7 +499,7 @@ } static void -select(struct battle_bar *bar, struct battle *bt, const struct selection *sel) +self_select(struct battle_bar *bar, struct battle *bt, const struct selection *sel) { battle_bar_default_select(bar->data, bt, sel); } @@ -559,6 +558,7 @@ assert(bt); assert(ch); +#if 0 init_gridmenu(bar, bt); for (size_t i = 0; i < CHARACTER_SPELL_MAX; ++i) @@ -568,6 +568,7 @@ gridmenu_repaint(&bar->sub_grid); bar->state = BATTLE_BAR_DEFAULT_STATE_GRID; +#endif } void @@ -576,6 +577,8 @@ assert(bar); assert(bt); +#if 0 + init_gridmenu(bar, bt); for (size_t i = 0; i < INVENTORY_ITEM_MAX; ++i) { @@ -589,6 +592,7 @@ gridmenu_repaint(&bar->sub_grid); bar->state = BATTLE_BAR_DEFAULT_STATE_GRID; +#endif } void @@ -596,7 +600,7 @@ { assert(bar); - gridmenu_reset(&bar->sub_grid); + //gridmenu_reset(&bar->sub_grid); bar->menu = BATTLE_BAR_DEFAULT_MENU_ATTACK; bar->state = BATTLE_BAR_DEFAULT_STATE_MENU; @@ -672,7 +676,7 @@ { assert(bar); - gridmenu_finish(&bar->sub_grid); + //gridmenu_finish(&bar->sub_grid); memset(bar, 0, sizeof (*bar)); } @@ -687,7 +691,7 @@ self = alloc_new0(sizeof (*self)); self->bar.data = self; self->bar.start = start; - self->bar.select = select; + self->bar.select = self_select; self->bar.handle = handle; self->bar.draw = draw; self->bar.finish = finish;
--- a/src/libmlk-rpg/rpg/battle-bar-default.h Fri Feb 18 16:00:53 2022 +0100 +++ b/src/libmlk-rpg/rpg/battle-bar-default.h Fri Feb 18 16:02:01 2022 +0100 @@ -57,7 +57,7 @@ enum battle_bar_default_menu menu; /* Sub menu selection (spells/objects). */ - char sub_items[GRIDMENU_ENTRY_MAX][128]; + char sub_items[16][128]; struct gridmenu sub_grid; };
--- a/src/libmlk-ui/ui/gridmenu.c Fri Feb 18 16:00:53 2022 +0100 +++ b/src/libmlk-ui/ui/gridmenu.c Fri Feb 18 16:02:01 2022 +0100 @@ -17,6 +17,7 @@ */ #include <assert.h> +#include <math.h> #include <string.h> #include <core/event.h> @@ -49,59 +50,61 @@ } static void -positionate(struct gridmenu *menu) +geometry(struct gridmenu *menu) { const struct theme *theme = THEME(menu); - struct gridmenu_texture *gtex = &menu->tex; - unsigned int vreq, hreq; + struct label label = { + .theme = theme, + .flags = LABEL_FLAGS_SHADOW + }; + unsigned int reqw = 0, reqh = 0, lw, lh; - /* Compute which item has the bigger width to create a spacing. */ - for (size_t i = 0; i < GRIDMENU_ENTRY_MAX; ++i) { - unsigned int lw, lh; + /* Compute which item has the bigger width/height to create a spacing. */ + menu->eltw = menu->elth = 0; + menu->spacew = menu->spaceh = 0; - gtex->labels[i].theme = theme; - gtex->labels[i].flags = LABEL_FLAGS_SHADOW; - - if (!(gtex->labels[i].text = menu->menu[i])) + for (size_t i = 0; i < menu->itemsz; ++i) { + if (!(label.text = menu->items[i])) continue; - label_query(>ex->labels[i], &lw, &lh); - gtex->eltw = lw > gtex->eltw ? lw : gtex->eltw; - gtex->elth = lh > gtex->elth ? lh : gtex->elth; + + label_query(&label, &lw, &lh); + + menu->eltw = fmax(menu->eltw, lw); + menu->elth = fmax(menu->elth, lh); } - vreq = (theme->padding * 2) + (gtex->elth * menu->nrows); - hreq = (theme->padding * 2) + (gtex->eltw * menu->ncols); + /* Total texture size required to draw items. */ + reqw = (theme->padding * 2) + (menu->eltw * menu->ncols); + reqh = (theme->padding * 2) + (menu->elth * menu->nrows); - /* Compute spacing between elements. */ - if (hreq > menu->w) { - tracef(_("gridmenu width is too small: %u < %u"), menu->w, vreq); - gtex->spaceh = 1; - } else { - hreq -= theme->padding * 2; - gtex->spaceh = (menu->w - hreq) / menu->ncols; + /* + * Compute spacing between elements. We remove the padding because it + * is outside of the elements. + */ + if (reqw > menu->w) { + tracef(_("gridmenu width is too small: %u < %u"), menu->w, reqw); + menu->spacew = 1; + } else if (menu->ncols > 1) { + reqw -= theme->padding * 2; + menu->spacew = (menu->w - reqw) / menu->ncols; } - if (vreq > menu->h) { - tracef(_("gridmenu height is too small: %u < %u"), menu->h, vreq); - gtex->spacev = 1; - } else { - vreq -= theme->padding * 2; - gtex->spacev = (menu->h - vreq) / menu->nrows; + if (reqh > menu->h) { + tracef(_("gridmenu height is too small: %u < %u"), menu->h, reqh); + menu->spaceh = 1; + } else if (menu->nrows > 1) { + reqh -= theme->padding * 2; + menu->spaceh = (menu->h - reqh) / menu->nrows; } - - /* This is the whole height. */ - gtex->relh = theme->padding * 2; - gtex->relh += gtex->elth * (GRIDMENU_ENTRY_MAX / menu->ncols); - gtex->relh += gtex->spacev * (GRIDMENU_ENTRY_MAX / menu->ncols); } static void -repaint_frame(struct gridmenu *menu) +draw_frame(const struct gridmenu *menu) { const struct frame f = { - .x = 0, - .y = menu->tex.rely, + .x = menu->x, + .y = menu->y, .w = menu->w, .h = menu->h, .theme = menu->theme, @@ -111,71 +114,55 @@ } static void -repaint_labels(struct gridmenu *menu) +draw_labels(const struct gridmenu *menu) { + size_t pagesz, pagenr, item, c = 0, r = 0; + struct label label = {0}; struct theme theme; - unsigned int r = 0, c = 0; /* Copy theme to change color if selected. */ theme_shallow(&theme, THEME(menu)); + label.theme = &theme; + label.flags = LABEL_FLAGS_SHADOW; - for (size_t i = 0; i < GRIDMENU_ENTRY_MAX; ++i) { - struct label *l = &menu->tex.labels[i]; + /* + * Select the first top-left column based on the current selection and + * the number of rows/columns. + */ + pagesz = menu->nrows * menu->ncols; + pagenr = menu->selected / pagesz; - if (i == menu->selected) + for (size_t i = 0; i < pagesz; ++i) { + item = i + pagenr * pagesz; + + if (item >= menu->itemsz) + continue; + + label.text = menu->items[item]; + label.x = menu->x + theme.padding + (c * menu->eltw) + (c * menu->spacew); + label.y = menu->y + theme.padding + (r * menu->elth) + (r * menu->spaceh); + + if (i == menu->selected % pagesz) theme.colors[THEME_COLOR_NORMAL] = THEME(menu)->colors[THEME_COLOR_SELECTED]; else theme.colors[THEME_COLOR_NORMAL] = THEME(menu)->colors[THEME_COLOR_NORMAL]; - l->theme = &theme; - l->x = theme.padding + (c * menu->tex.eltw) + (c * menu->tex.spaceh); - l->y = theme.padding + (r * menu->tex.elth) + (r * menu->tex.spacev); + label_draw(&label); if (++c >= menu->ncols) { ++r; c = 0; } - - if (l->text) - label_draw(l); } } -static void -repaint(struct gridmenu *menu) -{ - struct texture *tex = &menu->tex.texture; - - if (!texture_ok(tex) && texture_new(tex, menu->w, menu->tex.relh) < 0) - panic(); - - PAINTER_BEGIN(tex); - - painter_clear(); - repaint_frame(menu); - repaint_labels(menu); - - PAINTER_END(); -} - -static void -zoom(struct gridmenu *menu) -{ - struct gridmenu_texture *tex = &menu->tex; - struct label *cur = &tex->labels[menu->selected]; - - /* Readjust relative position. */ - if ((unsigned int)cur->y > tex->rely + menu->h || cur->y < tex->rely) - tex->rely = cur->y - THEME(menu)->padding; -} - -static void +static int handle_keydown(struct gridmenu *menu, const struct event_key *key) { assert(key->type == EVENT_KEYDOWN); const struct index idx = get_index(menu); - const unsigned int save = menu->selected; + int validate = 0; switch (key->key) { case KEY_UP: @@ -183,80 +170,97 @@ menu->selected -= menu->ncols; break; case KEY_RIGHT: - if (idx.col + 1U < menu->ncols) + if (menu->selected + 1U < menu->itemsz) menu->selected += 1; break; case KEY_DOWN: - if (idx.row + 1U < GRIDMENU_ENTRY_MAX / menu->ncols) + if (idx.row + 1U < menu->itemsz / menu->ncols) menu->selected += menu->ncols; + else + menu->selected = menu->itemsz - 1; break; case KEY_LEFT: if (idx.col > 0) menu->selected -= 1; break; case KEY_ENTER: - menu->state = GRIDMENU_STATE_ACTIVATED; + validate = 1; break; default: break; } - if (save != menu->selected) - gridmenu_repaint(menu); + return validate; } -static void +static int handle_clickdown(struct gridmenu *menu, const struct event_click *click) { assert(click->type == EVENT_CLICKDOWN); - const unsigned int save = menu->selected; + const struct theme *theme = THEME(menu); + size_t pagesz, pagenr, selected, c = 0, r = 0; + int x, y; + + pagesz = menu->nrows * menu->ncols; + pagenr = menu->selected / pagesz; + + for (size_t i = 0; i < pagesz; ++i) { + x = menu->x + theme->padding + (c * menu->eltw) + (c * menu->spacew); + y = menu->y + theme->padding + (r * menu->elth) + (r * menu->spaceh); - for (size_t i = 0; i < GRIDMENU_ENTRY_MAX; ++i) { - const struct label *l = &menu->tex.labels[i]; - const int x = menu->x + l->x; - const int y = menu->y + l->y - menu->tex.rely; - const unsigned int w = menu->tex.eltw; - const unsigned int h = menu->tex.elth; + if (maths_is_boxed(x, y, menu->eltw, menu->elth, click->x, click->y)) { + selected = c + r * menu->ncols; + selected += pagesz * pagenr; - if (maths_is_boxed(x, y, w, h, click->x, click->y)) { - menu->selected = i; - break; + if (selected < menu->itemsz) { + menu->selected = selected; + return 1; + } + } + + if (++c >= menu->ncols) { + ++r; + c = 0; } } - /* A click immediately active the widget. */ - if (save != menu->selected) { - gridmenu_repaint(menu); - menu->state = GRIDMENU_STATE_ACTIVATED; - } + return 0; } void -gridmenu_reset(struct gridmenu *menu) +gridmenu_init(struct gridmenu *menu, + unsigned int nr, + unsigned int nc, + const char * const *items, + size_t itemsz) { assert(menu); + assert(nr); + assert(nc); - menu->state = GRIDMENU_STATE_NONE; + memset(menu, 0, sizeof (*menu)); + + menu->nrows = nr; + menu->ncols = nc; + menu->items = items; + menu->itemsz = itemsz; } void -gridmenu_repaint(struct gridmenu *menu) +gridmenu_resize(struct gridmenu *menu, int x, int y, unsigned int w, unsigned int h) { assert(menu); - assert(GRIDMENU_ENTRY_MAX % menu->ncols == 0); - - /* Re-compute positions. */ - positionate(menu); - /* Zoom to the appropriate y-relative. */ - zoom(menu); + menu->x = x; + menu->y = y; + menu->w = w; + menu->h = h; - /* Redraw. */ - repaint(menu); + geometry(menu); } -void +int gridmenu_handle(struct gridmenu *menu, const union event *ev) { assert(menu); @@ -264,35 +268,23 @@ switch (ev->type) { case EVENT_KEYDOWN: - handle_keydown(menu, &ev->key); + return handle_keydown(menu, &ev->key); break; case EVENT_CLICKDOWN: - handle_clickdown(menu, &ev->click); + return handle_clickdown(menu, &ev->click); break; default: break; } + + return 0; } void gridmenu_draw(const struct gridmenu *menu) { assert(menu); - assert(menu->nrows > 0 && menu->ncols > 0); - assert(texture_ok(&menu->tex.texture)); - texture_scale(&menu->tex.texture, - 0, menu->tex.rely, menu->w, menu->h, - menu->x, menu->y, menu->w, menu->h, 0.0); + draw_frame(menu); + draw_labels(menu); } - -void -gridmenu_finish(struct gridmenu *menu) -{ - assert(menu); - - if (texture_ok(&menu->tex.texture)) - texture_finish(&menu->tex.texture); - - memset(menu, 0, sizeof (*menu)); -}
--- a/src/libmlk-ui/ui/gridmenu.h Fri Feb 18 16:00:53 2022 +0100 +++ b/src/libmlk-ui/ui/gridmenu.h Fri Feb 18 16:02:01 2022 +0100 @@ -22,63 +22,46 @@ #include <stddef.h> #include <core/core.h> -#include <core/texture.h> #include "label.h" -#define GRIDMENU_ENTRY_MAX (256) - struct theme; union event; -enum gridmenu_state { - GRIDMENU_STATE_NONE, - GRIDMENU_STATE_ACTIVATED -}; - -struct gridmenu_texture { - int rely; - unsigned int relh; - unsigned int eltw; - unsigned int elth; - unsigned int spacev; - unsigned int spaceh; - struct texture texture; - struct label labels[GRIDMENU_ENTRY_MAX]; -}; - struct gridmenu { int x; int y; unsigned int w; unsigned int h; - enum gridmenu_state state; size_t selected; const struct theme *theme; - const char *menu[GRIDMENU_ENTRY_MAX]; + const char * const *items; + size_t itemsz; unsigned int nrows; unsigned int ncols; - struct gridmenu_texture tex; + + /* Private fields. */ + unsigned int eltw; /* maximum entry label width */ + unsigned int elth; /* maximum entry label height */ + unsigned int spacew; /* space between element horizontally */ + unsigned int spaceh; /* and vertically */ }; CORE_BEGIN_DECLS void -gridmenu_reset(struct gridmenu *); +gridmenu_init(struct gridmenu *, unsigned int, unsigned int, const char * const *, size_t); void -gridmenu_repaint(struct gridmenu *); +gridmenu_resize(struct gridmenu *, int, int, unsigned int, unsigned int); -void +int gridmenu_handle(struct gridmenu *, const union event *); void gridmenu_draw(const struct gridmenu *); -void -gridmenu_finish(struct gridmenu *); - CORE_END_DECLS #endif /* !MLK_UI_GRIDMENU_H */