[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_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_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,
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 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) \
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

View file

@ -150,9 +150,28 @@ Con_Debug_Draw (void)
}
IMUI_BeginFrame (debug_imui);
if (UI_Button ("Close Debug")) {
close_debug ();
static int state;
static bool flag = true;
UI_Layout(true) {
UI_Layout(false) {
if (UI_Button ("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);

View file

@ -33,6 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include "QF/darray.h"
#include "QF/ecs.h"
#include "QF/hash.h"
#include "QF/mathlib.h"
@ -61,7 +62,6 @@ struct imui_ctx_s {
uint32_t canvas;
ecs_system_t vsys;
text_system_t tsys;
view_t root_view;
hashctx_t *hashctx;
hashtab_t *tab;
PR_RESMAP (imui_state_t) state_map;
@ -73,6 +73,10 @@ struct imui_ctx_s {
int64_t frame_end;
uint32_t frame_count;
view_t root_view;
view_t current_parent;
struct DARRAY_TYPE(view_t) parent_stack;
uint32_t hot;
uint32_t active;
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 },
.tsys = { canvas_sys.reg, canvas_sys.view_base, canvas_sys.text_base },
.root_view = Canvas_GetRootView (canvas_sys, canvas),
.parent_stack = DARRAY_STATIC_INIT (8),
.hot = nullent,
.active = nullent,
.mouse_position = {-1, -1},
};
ctx->tab = Hash_NewTable (511, imui_state_getkey, 0, ctx, &ctx->hashctx);
ctx->current_parent = ctx->root_view;
auto fpath = Font_SystemFont (font);
if (fpath) {
@ -232,6 +238,8 @@ IMUI_BeginFrame (imui_ctx_t *ctx)
ctx->root_view = View_AddToEntity (root_ent, ctx->vsys, nullview);
ctx->frame_start = Sys_LongTime ();
ctx->frame_count++;
ctx->current_parent = ctx->root_view;
DARRAY_RESIZE (&ctx->parent_stack, 0);
}
static void
@ -241,7 +249,6 @@ prune_objects (imui_ctx_t *ctx)
if ((*s)->frame_count == ctx->frame_count) {
s = &(*s)->next;
} else {
View_Delete (View_FromEntity (ctx->vsys, (*s)->entity));
Hash_Del (ctx->tab, (*s)->label + (*s)->key_offset);
imui_state_free (ctx, *s);
}
@ -385,6 +392,26 @@ IMUI_Draw (imui_ctx_t *ctx)
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
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);
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;
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);
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;
update_hot_active (ctx, old_entity, state->entity);
@ -522,8 +549,40 @@ 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)
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