[ui] Implement event handling in imui

Button presses work nicely thanks to both Casey Muratori and Darian (for
clearing up some of Casey's comments about `hot`).
This commit is contained in:
Bill Currie 2023-07-02 01:25:27 +09:00
parent e89b5a88fa
commit ed5ef3a5fb
3 changed files with 152 additions and 8 deletions

View file

@ -33,6 +33,7 @@
typedef struct imui_ctx_s imui_ctx_t;
struct canvas_system_s;
struct IE_event_s;
typedef enum IMUI_SizeKind {
IMUI_SizeKind_Null,
@ -64,6 +65,8 @@ void IMUI_DestroyContext (imui_ctx_t *ctx);
void IMUI_SetVisible (imui_ctx_t *ctx, bool visible);
void IMUI_SetSize (imui_ctx_t *ctx, int xlen, int ylen);
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);
bool IMUI_Button (imui_ctx_t *ctx, const char *label);

View file

@ -3,6 +3,8 @@
#endif
#include "QF/cvar.h"
#include "QF/keys.h"
#include "QF/sys.h"
#include "QF/input/event.h"
@ -17,7 +19,9 @@
#include "cl_console.h"
static int debug_event_id;
static int debug_saved_focus;
static imui_ctx_t *debug_imui;
static int64_t debug_enable_time;
#define IMUI_context debug_imui
bool con_debug;
@ -54,6 +58,10 @@ con_debug_f (void *data, const cvar_t *cvar)
Con_Show_Mouse (con_debug);
if (debug_imui) {
IMUI_SetVisible (debug_imui, con_debug);
debug_enable_time = Sys_LongTime ();
if (!con_debug) {
IE_Set_Focus (debug_saved_focus);
}
}
}
@ -70,11 +78,36 @@ debug_app_window (const IE_event_t *ie_event)
}
}
static void
debug_mouse (const IE_event_t *ie_event)
{
IMUI_ProcessEvent (debug_imui, ie_event);
}
static void
close_debug (void)
{
con_debug = false;
con_debug_f (0, &con_debug_cvar);
}
static void
debug_key (const IE_event_t *ie_event)
{
int shift = ie_event->key.shift & ~(ies_capslock | ies_numlock);
if (ie_event->key.code == QFK_ESCAPE && shift == ies_control) {
close_debug ();
}
IMUI_ProcessEvent (debug_imui, ie_event);
}
static int
debug_event_handler (const IE_event_t *ie_event, void *data)
{
static void (*handlers[ie_event_count]) (const IE_event_t *ie_event) = {
[ie_app_window] = debug_app_window,
[ie_mouse] = debug_mouse,
[ie_key] = debug_key,
};
if ((unsigned) ie_event->type >= ie_event_count
|| !handlers[ie_event->type]) {
@ -110,7 +143,17 @@ Con_Debug_Shutdown (void)
void
Con_Debug_Draw (void)
{
UI_Button ("Hi there!");
if (debug_enable_time && Sys_LongTime () - debug_enable_time > 1000) {
debug_saved_focus = IE_Get_Focus ();
IE_Set_Focus (debug_event_id);
debug_enable_time = 0;
}
IMUI_BeginFrame (debug_imui);
if (UI_Button ("Close Debug")) {
close_debug ();
}
IMUI_Draw (debug_imui);
}

View file

@ -38,6 +38,8 @@
#include "QF/progs.h"
#include "QF/quakeio.h"
#include "QF/input/event.h"
#include "QF/ui/canvas.h"
#include "QF/ui/font.h"
#include "QF/ui/imui.h"
@ -50,7 +52,6 @@ typedef struct imui_state_s {
uint32_t label_len;
int key_offset;
uint32_t entity;
bool pressed;
} imui_state_t;
struct imui_ctx_s {
@ -64,6 +65,21 @@ struct imui_ctx_s {
PR_RESMAP (imui_state_t) state_map;
imui_state_t *states;
font_t *font;
int64_t frame_start;
int64_t frame_draw;
int64_t frame_end;
uint32_t framecount;
uint32_t new_hot;
uint32_t hot;
uint32_t active;
bool mouse_pressed;
bool mouse_released;
unsigned mouse_buttons;
view_pos_t mouse_position;
unsigned shift;
int key_code;
int unicode;
};
static imui_state_t *
@ -138,6 +154,10 @@ IMUI_NewContext (canvas_system_t canvas_sys, const char *font, float fontsize)
.tsys = { canvas_sys.reg, canvas_sys.view_base, canvas_sys.text_base },
.root_view = Canvas_GetRootView (canvas_sys, canvas),
.tab = Hash_NewTable (511, imui_state_getkey, 0, ctx, &ctx->hashctx),
.new_hot = nullent,
.hot = nullent,
.active = nullent,
.mouse_position = {-1, -1},
};
auto fpath = Font_SystemFont (font);
@ -182,10 +202,87 @@ IMUI_SetSize (imui_ctx_t *ctx, int xlen, int ylen)
View_UpdateHierarchy (ctx->root_view);
}
void
IMUI_ProcessEvent (imui_ctx_t *ctx, const IE_event_t *ie_event)
{
if (ie_event->type == ie_mouse) {
auto m = &ie_event->mouse;
ctx->mouse_position = (view_pos_t) { m->x, m->y };
unsigned old = ctx->mouse_buttons & 1;
unsigned new = m->buttons & 1;
ctx->mouse_pressed = (old ^ new) & new;
ctx->mouse_released = (old ^ new) & !new;
ctx->mouse_buttons = m->buttons;
} else {
auto k = &ie_event->key;
//printf ("imui: %d %d %x\n", k->code, k->unicode, k->shift);
ctx->shift = k->shift;
ctx->key_code = k->code;
ctx->unicode = k->unicode;
}
}
void
IMUI_BeginFrame (imui_ctx_t *ctx)
{
ctx->frame_start = Sys_LongTime ();
ctx->framecount++;
ctx->hot = ctx->new_hot;
ctx->new_hot = nullent;
}
void
IMUI_Draw (imui_ctx_t *ctx)
{
ctx->frame_draw = Sys_LongTime ();
View_UpdateHierarchy (ctx->root_view);
ctx->frame_end = Sys_LongTime ();
}
static bool
check_button_state (imui_ctx_t *ctx, uint32_t entity)
{
bool result = false;
if (ctx->active == entity) {
if (ctx->mouse_released) {
result = ctx->hot == entity;
ctx->active = nullent;
}
} else if (ctx->hot == entity) {
if (ctx->mouse_pressed) {
ctx->active = entity;
}
}
return result;
}
static void
check_inside (imui_ctx_t *ctx, view_pos_t pos, view_pos_t len, uint32_t entity)
{
auto mp = ctx->mouse_position;
if (mp.x >= pos.x && mp.y >= pos.y
&& mp.x < pos.x + len.x && mp.y < pos.y + len.y) {
if (ctx->active == entity || ctx->active == nullent) {
ctx->new_hot = entity;
}
}
}
static view_t
add_text (view_t view, imui_state_t *state, imui_ctx_t *ctx)
{
uint32_t c_glyphs = ctx->csys.base + canvas_glyphs;
uint32_t c_passage_glyphs = ctx->csys.text_base + text_passage_glyphs;
auto reg = ctx->csys.reg;
auto text = Text_StringView (ctx->tsys, view, ctx->font,
state->label, state->label_len, 0, 0);
View_SetVisible (text, 1);
Ent_SetComponent (text.id, c_glyphs, reg,
Ent_GetComponent (text.id, c_passage_glyphs, reg));
return text;
}
bool
@ -203,15 +300,16 @@ IMUI_Button (imui_ctx_t *ctx, const char *label)
View_SetGravity (view, grav_northwest);
View_SetResize (view, 0, 0);
auto text = Text_StringView (ctx->tsys, view, ctx->font,
state->label, state->label_len, 0, 0);
View_SetVisible (text, 1);
Ent_SetComponent (text.id, ctx->csys.base + canvas_glyphs, ctx->csys.reg,
Ent_GetComponent (text.id, ctx->csys.text_base + text_passage_glyphs, ctx->csys.reg));
auto text = add_text (view, state, ctx);
auto len = View_GetLen (text);
View_SetLen (view, len.x, len.y);
}
return state->pressed;
auto view = View_FromEntity (ctx->vsys, state->entity);
auto len = View_GetLen (view);
auto pos = View_GetAbs (view);
bool result = check_button_state (ctx, state->entity);
check_inside (ctx, pos, len, state->entity);
return result;
}
bool