[ui] Implement a layout stack and radio buttons

Also, remove an intermittent double free caused by deleting views that
have already been deleted.
This commit is contained in:
Bill Currie 2023-07-04 01:31:04 +09:00
parent 6fa016e23b
commit 403cf72f52
3 changed files with 97 additions and 9 deletions

View file

@ -53,12 +53,18 @@ void IMUI_ProcessEvent (imui_ctx_t *ctx, const struct IE_event_s *ie_event);
void IMUI_BeginFrame (imui_ctx_t *ctx); void IMUI_BeginFrame (imui_ctx_t *ctx);
void IMUI_Draw (imui_ctx_t *ctx); void IMUI_Draw (imui_ctx_t *ctx);
void IMUI_PushLayout (imui_ctx_t *ctx, bool vertical);
void IMUI_PopLayout (imui_ctx_t *ctx);
bool IMUI_Button (imui_ctx_t *ctx, const char *label); bool IMUI_Button (imui_ctx_t *ctx, const char *label);
bool IMUI_Checkbox (imui_ctx_t *ctx, bool *flag, const char *label); bool IMUI_Checkbox (imui_ctx_t *ctx, bool *flag, const char *label);
void IMUI_Radio (imui_ctx_t *ctx, int *state, int value, const char *label); void IMUI_Radio (imui_ctx_t *ctx, int *curvalue, int value, const char *label);
void IMUI_Slider (imui_ctx_t *ctx, float *value, float minval, float maxval, void IMUI_Slider (imui_ctx_t *ctx, float *value, float minval, float maxval,
const char *label); const char *label);
#define IMUI_DeferLoop(begin, end) \
for (int _i_ = ((begin), 0); !_i_; _i_++, (end))
// #define IMUI_context to an imui_ctx_t * variable // #define IMUI_context to an imui_ctx_t * variable
#define UI_Button(label) \ #define UI_Button(label) \
@ -73,4 +79,8 @@ void IMUI_Slider (imui_ctx_t *ctx, float *value, float minval, float maxval,
#define UI_Slider(value, minval, maxval, label) \ #define UI_Slider(value, minval, maxval, label) \
IMUI_Slider(IMUI_context, value, minval, maxval, label) IMUI_Slider(IMUI_context, value, minval, maxval, label)
#define UI_Layout(vertical) \
IMUI_DeferLoop (IMUI_PushLayout (IMUI_context, vertical), \
IMUI_PopLayout (IMUI_context ))
#endif//__QF_ui_imui_h #endif//__QF_ui_imui_h

View file

@ -150,10 +150,29 @@ Con_Debug_Draw (void)
} }
IMUI_BeginFrame (debug_imui); IMUI_BeginFrame (debug_imui);
static int state;
static bool flag = true;
UI_Layout(true) {
UI_Layout(false) {
if (UI_Button ("Close Debug")) { if (UI_Button ("Close Debug")) {
close_debug (); close_debug ();
} }
if (flag) {
UI_Button ("_##1");
}
}
UI_Layout(false) {
UI_Checkbox (&flag, "hi there");
if (flag) {
UI_Button ("_##2");
}
}
UI_Layout(false) {
UI_Radio (&state, 0, "A");
UI_Radio (&state, 1, "B");
UI_Radio (&state, 2, "C");
}
}
IMUI_Draw (debug_imui); IMUI_Draw (debug_imui);
} }

View file

@ -33,6 +33,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "QF/darray.h"
#include "QF/ecs.h" #include "QF/ecs.h"
#include "QF/hash.h" #include "QF/hash.h"
#include "QF/mathlib.h" #include "QF/mathlib.h"
@ -61,7 +62,6 @@ struct imui_ctx_s {
uint32_t canvas; uint32_t canvas;
ecs_system_t vsys; ecs_system_t vsys;
text_system_t tsys; text_system_t tsys;
view_t root_view;
hashctx_t *hashctx; hashctx_t *hashctx;
hashtab_t *tab; hashtab_t *tab;
PR_RESMAP (imui_state_t) state_map; PR_RESMAP (imui_state_t) state_map;
@ -73,6 +73,10 @@ struct imui_ctx_s {
int64_t frame_end; int64_t frame_end;
uint32_t frame_count; uint32_t frame_count;
view_t root_view;
view_t current_parent;
struct DARRAY_TYPE(view_t) parent_stack;
uint32_t hot; uint32_t hot;
uint32_t active; uint32_t active;
bool mouse_pressed; bool mouse_pressed;
@ -155,11 +159,13 @@ IMUI_NewContext (canvas_system_t canvas_sys, const char *font, float fontsize)
.vsys = { canvas_sys.reg, canvas_sys.view_base }, .vsys = { canvas_sys.reg, canvas_sys.view_base },
.tsys = { canvas_sys.reg, canvas_sys.view_base, canvas_sys.text_base }, .tsys = { canvas_sys.reg, canvas_sys.view_base, canvas_sys.text_base },
.root_view = Canvas_GetRootView (canvas_sys, canvas), .root_view = Canvas_GetRootView (canvas_sys, canvas),
.parent_stack = DARRAY_STATIC_INIT (8),
.hot = nullent, .hot = nullent,
.active = nullent, .active = nullent,
.mouse_position = {-1, -1}, .mouse_position = {-1, -1},
}; };
ctx->tab = Hash_NewTable (511, imui_state_getkey, 0, ctx, &ctx->hashctx); ctx->tab = Hash_NewTable (511, imui_state_getkey, 0, ctx, &ctx->hashctx);
ctx->current_parent = ctx->root_view;
auto fpath = Font_SystemFont (font); auto fpath = Font_SystemFont (font);
if (fpath) { if (fpath) {
@ -232,6 +238,8 @@ IMUI_BeginFrame (imui_ctx_t *ctx)
ctx->root_view = View_AddToEntity (root_ent, ctx->vsys, nullview); ctx->root_view = View_AddToEntity (root_ent, ctx->vsys, nullview);
ctx->frame_start = Sys_LongTime (); ctx->frame_start = Sys_LongTime ();
ctx->frame_count++; ctx->frame_count++;
ctx->current_parent = ctx->root_view;
DARRAY_RESIZE (&ctx->parent_stack, 0);
} }
static void static void
@ -241,7 +249,6 @@ prune_objects (imui_ctx_t *ctx)
if ((*s)->frame_count == ctx->frame_count) { if ((*s)->frame_count == ctx->frame_count) {
s = &(*s)->next; s = &(*s)->next;
} else { } else {
View_Delete (View_FromEntity (ctx->vsys, (*s)->entity));
Hash_Del (ctx->tab, (*s)->label + (*s)->key_offset); Hash_Del (ctx->tab, (*s)->label + (*s)->key_offset);
imui_state_free (ctx, *s); imui_state_free (ctx, *s);
} }
@ -385,6 +392,26 @@ IMUI_Draw (imui_ctx_t *ctx)
ctx->frame_end = Sys_LongTime (); ctx->frame_end = Sys_LongTime ();
} }
void
IMUI_PushLayout (imui_ctx_t *ctx, bool vertical)
{
DARRAY_APPEND (&ctx->parent_stack, ctx->current_parent);
ctx->current_parent = View_New (ctx->vsys, ctx->current_parent);
*View_Control (ctx->current_parent) = (viewcont_t) {
.gravity = grav_northwest,
.visible = 1,
.semantic_x = IMUI_SizeKind_ChildrenSum,
.semantic_y = IMUI_SizeKind_ChildrenSum,
.vertical = vertical,
};
}
void
IMUI_PopLayout (imui_ctx_t *ctx)
{
ctx->current_parent = DARRAY_REMOVE (&ctx->parent_stack);
}
static bool static bool
check_button_state (imui_ctx_t *ctx, uint32_t entity) check_button_state (imui_ctx_t *ctx, uint32_t entity)
{ {
@ -472,7 +499,7 @@ IMUI_Button (imui_ctx_t *ctx, const char *label)
auto state = imui_get_state (ctx, label); auto state = imui_get_state (ctx, label);
uint32_t old_entity = state->entity; uint32_t old_entity = state->entity;
auto view = View_New (ctx->vsys, ctx->root_view); auto view = View_New (ctx->vsys, ctx->current_parent);
state->entity = view.id; state->entity = view.id;
update_hot_active (ctx, old_entity, state->entity); update_hot_active (ctx, old_entity, state->entity);
@ -489,7 +516,7 @@ IMUI_Checkbox (imui_ctx_t *ctx, bool *flag, const char *label)
auto state = imui_get_state (ctx, label); auto state = imui_get_state (ctx, label);
uint32_t old_entity = state->entity; uint32_t old_entity = state->entity;
auto view = View_New (ctx->vsys, ctx->root_view); auto view = View_New (ctx->vsys, ctx->current_parent);
state->entity = view.id; state->entity = view.id;
update_hot_active (ctx, old_entity, state->entity); update_hot_active (ctx, old_entity, state->entity);
@ -522,8 +549,40 @@ IMUI_Checkbox (imui_ctx_t *ctx, bool *flag, const char *label)
} }
void void
IMUI_Radio (imui_ctx_t *ctx, int *state, int value, const char *label) IMUI_Radio (imui_ctx_t *ctx, int *curvalue, int value, const char *label)
{ {
auto state = imui_get_state (ctx, label);
uint32_t old_entity = state->entity;
auto view = View_New (ctx->vsys, ctx->current_parent);
state->entity = view.id;
update_hot_active (ctx, old_entity, state->entity);
set_control (ctx, view, true);
View_Control (view)->semantic_x = IMUI_SizeKind_ChildrenSum;
View_Control (view)->semantic_y = IMUI_SizeKind_ChildrenSum;
set_fill (ctx, view, 0);
auto checkbox = View_New (ctx->vsys, view);
set_control (ctx, checkbox, false);
View_SetLen (checkbox, 20, 20);
set_fill (ctx, checkbox, 0xfe);
if (*curvalue != value) {
auto punch = View_New (ctx->vsys, checkbox);
set_control (ctx, punch, false);
View_SetGravity (punch, grav_center);
View_SetLen (punch, 14, 14);
set_fill (ctx, punch, 0);
}
auto text = View_New (ctx->vsys, view);
set_control (ctx, text, false);
add_text (text, state, ctx);
if (check_button_state (ctx, state->entity)) {
*curvalue = value;
}
} }
void void