From df40a50b917366243faf7e9529cb2eaf82fa1152 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Thu, 6 Jul 2023 12:21:14 +0900 Subject: [PATCH] [ui] Implement dragable collapsible windows And of course, closable. --- include/QF/ui/imui.h | 16 +++++++ libs/console/cl_debug.c | 59 +++++++++++++++---------- libs/ui/imui.c | 96 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 23 deletions(-) diff --git a/include/QF/ui/imui.h b/include/QF/ui/imui.h index f6d29c454..29de26269 100644 --- a/include/QF/ui/imui.h +++ b/include/QF/ui/imui.h @@ -68,6 +68,16 @@ typedef struct imui_style_s { imui_color_t text; } imui_style_t; +typedef struct imui_window_s { + const char *name; + int xpos; + int ypos; + int xlen; + int ylen; + bool is_open; + bool is_collapsed; +} imui_window_t; + imui_ctx_t *IMUI_NewContext (struct canvas_system_s canvas_sys, const char *font, float fontsize); void IMUI_DestroyContext (imui_ctx_t *ctx); @@ -80,6 +90,8 @@ void IMUI_Draw (imui_ctx_t *ctx); void IMUI_PushLayout (imui_ctx_t *ctx, bool vertical); void IMUI_PopLayout (imui_ctx_t *ctx); +void IMUI_StartWindow (imui_ctx_t *ctx, imui_window_t *window); +void IMUI_EndWindow (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); @@ -112,6 +124,10 @@ void IMUI_FlexibleSpace (imui_ctx_t *ctx); IMUI_DeferLoop (IMUI_PushLayout (IMUI_context, vertical), \ IMUI_PopLayout (IMUI_context )) +#define UI_Window(window) \ + IMUI_DeferLoop (IMUI_StartWindow (IMUI_context, window), \ + IMUI_EndWindow (IMUI_context)) + #define UI_Horizontal UI_Layout(false) #define UI_Vertical UI_Layout(true) diff --git a/libs/console/cl_debug.c b/libs/console/cl_debug.c index 0ef2911bf..f7c44054b 100644 --- a/libs/console/cl_debug.c +++ b/libs/console/cl_debug.c @@ -156,34 +156,47 @@ Con_Debug_Draw (void) IMUI_BeginFrame (debug_imui); static int state; static bool flag = true; - UI_Vertical { - UI_Horizontal { - if (UI_Button ("Close Debug")) { - close_debug (); - } - if (flag) { - UI_FlexibleSpace (); - UI_Button ("abcdefghijklmnopqrstuvwxyza##1"); - } +static imui_window_t window = { + .name = "Test Window", + .xpos = 50, + .ypos = 50, + .xlen = 400, + .ylen = 250, + .is_open = true, +}; + UI_Window (&window) { + if (!window.is_open || window.is_collapsed) { + continue; } - UI_Horizontal { - UI_Checkbox (&flag, "hi there"); - if (flag) { + UI_Vertical { + UI_Horizontal { + if (UI_Button ("Close Debug")) { + close_debug (); + } + UI_FlexibleSpace (); + if (flag) { + UI_Button ("abcde##1"); + } + } + UI_Horizontal { + UI_Checkbox (&flag, "hi there"); + UI_FlexibleSpace (); + if (flag) { + UI_Button ("abcdefg##2"); + } + } + UI_Horizontal { + UI_Radio (&state, 0, "A"); + UI_Radio (&state, 1, "B"); + UI_Radio (&state, 2, "C"); + UI_FlexibleSpace (); + } + UI_Horizontal { + UI_Button (va(0, "mem: %zd", Sys_CurrentRSS ())); UI_FlexibleSpace (); - UI_Button ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst##2"); } - } - UI_Horizontal { - UI_Radio (&state, 0, "A"); - UI_Radio (&state, 1, "B"); - UI_Radio (&state, 2, "C"); UI_FlexibleSpace (); } - UI_Horizontal { - UI_Button (va(0, "mem: %zd", Sys_CurrentRSS ())); - UI_FlexibleSpace (); - } - UI_FlexibleSpace (); } IMUI_Draw (debug_imui); diff --git a/libs/ui/imui.c b/libs/ui/imui.c index c3bc46258..1c17a8e55 100644 --- a/libs/ui/imui.c +++ b/libs/ui/imui.c @@ -39,6 +39,7 @@ #include "QF/mathlib.h" #include "QF/progs.h" #include "QF/quakeio.h" +#include "QF/va.h" #include "QF/input/event.h" @@ -90,6 +91,7 @@ struct imui_ctx_s { uint32_t hot; uint32_t active; + view_pos_t mouse_active; bool mouse_pressed; bool mouse_released; unsigned mouse_buttons; @@ -632,6 +634,26 @@ check_button_state (imui_ctx_t *ctx, uint32_t entity) return result; } +static view_pos_t +check_drag_delta (imui_ctx_t *ctx, uint32_t entity) +{ + view_pos_t delta = {}; + if (ctx->active == entity) { + delta.x = ctx->mouse_position.x - ctx->mouse_active.x; + delta.y = ctx->mouse_position.y - ctx->mouse_active.y; + ctx->mouse_active = ctx->mouse_position; + if (ctx->mouse_released) { + ctx->active = nullent; + } + } else if (ctx->hot == entity) { + if (ctx->mouse_pressed) { + ctx->mouse_active = ctx->mouse_position; + ctx->active = entity; + } + } + return delta; +} + static view_t add_text (imui_ctx_t *ctx, view_t view, imui_state_t *state, int mode) { @@ -703,6 +725,14 @@ set_control (imui_ctx_t *ctx, view_t view, bool active) }; } +static void +set_expand_x (imui_ctx_t *ctx, view_t view, int weight) +{ + View_Control (view)->semantic_x = imui_size_expand; + uint32_t c_percent_x = ctx->csys.imui_base + imui_percent_x; + *(int *) Ent_AddComponent(view.id, c_percent_x, ctx->csys.reg) = weight; +} + bool IMUI_Button (imui_ctx_t *ctx, const char *label) { @@ -817,3 +847,69 @@ IMUI_FlexibleSpace (imui_ctx_t *ctx) set_fill (ctx, view, ctx->style.background.normal); } + +void +IMUI_StartWindow (imui_ctx_t *ctx, imui_window_t *window) +{ + if (!window->is_open) { + return; + } + auto state = imui_get_state (ctx, window->name); + uint32_t old_entity = state->entity; + + DARRAY_APPEND (&ctx->parent_stack, ctx->current_parent); + + auto window_view = View_New (ctx->vsys, ctx->current_parent); + state->entity = window_view.id; + int mode = update_hot_active (ctx, old_entity, state->entity); + + ctx->current_parent = window_view; + *View_Control (window_view) = (viewcont_t) { + .gravity = grav_northwest, + .visible = 1, + .semantic_x = imui_size_none, + .semantic_y = imui_size_none, + .vertical = true, + .active = 1, + }; + View_SetPos (window_view, window->xpos, window->ypos); + View_SetLen (window_view, window->xlen, window->ylen); + +#define IMUI_context ctx + UI_Horizontal { + if (UI_Button (va (0, "%c##collapse_%s", + window->is_collapsed ? '>' : 'v', window->name))) { + window->is_collapsed = !window->is_collapsed; + } + + auto tb_state = imui_get_state (ctx, va (0, "%s##title_bar", + window->name)); + uint32_t tb_old_entity = tb_state->entity; + auto title_bar = View_New (ctx->vsys, ctx->current_parent); + tb_state->entity = title_bar.id; + int tb_mode = update_hot_active (ctx, tb_old_entity, tb_state->entity); + auto delta = check_drag_delta (ctx, tb_state->entity); + if (ctx->active == tb_state->entity) { + window->xpos += delta.x; + window->ypos += delta.y; + } + + set_control (ctx, title_bar, true); + set_expand_x (ctx, title_bar, 100); + set_fill (ctx, title_bar, ctx->style.foreground.color[tb_mode]); + + auto title = add_text (ctx, title_bar, state, mode); + View_Control (title)->gravity = grav_center; + + if (UI_Button (va (0, "X##close_%s", window->name))) { + window->is_open = false; + } + } +#undef IMUI_context +} + +void +IMUI_EndWindow (imui_ctx_t *ctx) +{ + IMUI_PopLayout (ctx); +}