mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-04-10 19:30:56 +00:00
[ui] Add the beginnings of an immediate mode UI
Based on the articles on Hidden Grove (https://www.rfleury.com/archive?sort=new). So far, I can get a non-functional button on the screen :)
This commit is contained in:
parent
4eef11c329
commit
0257165b7d
3 changed files with 325 additions and 0 deletions
89
include/QF/ui/imui.h
Normal file
89
include/QF/ui/imui.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
imui.h
|
||||
|
||||
Immediate mode user interface
|
||||
|
||||
Copyright (C) 2023 Bill Currie <bill@taniwha.org>
|
||||
|
||||
Author: Bill Currie <bill@taniwha.org>
|
||||
Date: 2023/07/01
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __QF_ui_imui_h
|
||||
#define __QF_ui_imui_h
|
||||
|
||||
typedef struct imui_ctx_s imui_ctx_t;
|
||||
struct canvas_system_s;
|
||||
|
||||
typedef enum IMUI_SizeKind {
|
||||
IMUI_SizeKind_Null,
|
||||
IMUI_SizeKind_Pixels,
|
||||
IMUI_SizeKind_TextContent,
|
||||
IMUI_SizeKind_PercentOfParent,
|
||||
IMUI_SizeKind_ChildrenSum,
|
||||
} IMUI_SizeKind;
|
||||
|
||||
typedef enum IMUI_Axis {
|
||||
IMUI_Axis_X,
|
||||
IMUI_Axis_Y,
|
||||
IMUI_Axis_count,
|
||||
} IMUI_Axis;
|
||||
|
||||
typedef struct imui_size {
|
||||
IMUI_SizeKind kind;
|
||||
float value;
|
||||
float strictness;
|
||||
} imui_size;
|
||||
|
||||
typedef struct imui_layout_s {
|
||||
imui_size semantic_size[IMUI_Axis_count];
|
||||
} imui_layout_t;
|
||||
|
||||
imui_ctx_t *IMUI_NewContext (struct canvas_system_s canvas_sys,
|
||||
const char *font, float fontsize);
|
||||
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_Draw (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_Slider (imui_ctx_t *ctx, float *value, float minval, float maxval,
|
||||
const char *label);
|
||||
|
||||
// #define IMUI_context to an imui_ctx_t * variable
|
||||
|
||||
#define UI_Button(label) \
|
||||
IMUI_Button(IMUI_context, label)
|
||||
|
||||
#define UI_Checkbox(flag, label) \
|
||||
IMUI_Checkbox(IMUI_context, flag, label)
|
||||
|
||||
#define UI_Radio(state, value, label) \
|
||||
IMUI_Radio(IMUI_context, state, value, label)
|
||||
|
||||
#define UI_Slider(value, minval, maxval, label) \
|
||||
IMUI_Slider(IMUI_context, value, minval, maxval, label)
|
||||
|
||||
#endif//__QF_ui_imui_h
|
|
@ -22,6 +22,7 @@ libs_ui_libQFgui_la_DEPENDENCIES= $(gui_deps) $(ui_deps)
|
|||
libs_ui_libQFgui_la_SOURCES= \
|
||||
libs/ui/canvas.c \
|
||||
libs/ui/font.c \
|
||||
libs/ui/imui.c \
|
||||
libs/ui/text.c
|
||||
|
||||
libs_ui_libQFui_la_LDFLAGS= $(lib_ldflags)
|
||||
|
|
235
libs/ui/imui.c
Normal file
235
libs/ui/imui.c
Normal file
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
imui.c
|
||||
|
||||
Immediate mode user inferface
|
||||
|
||||
Copyright (C) 2023 Bill Currie <bill@taniwha.org>
|
||||
|
||||
Author: Bill Currie <bill@taniwha.org>
|
||||
Date: 2023/07/01
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "QF/ecs.h"
|
||||
#include "QF/hash.h"
|
||||
#include "QF/progs.h"
|
||||
#include "QF/quakeio.h"
|
||||
|
||||
#include "QF/ui/canvas.h"
|
||||
#include "QF/ui/font.h"
|
||||
#include "QF/ui/imui.h"
|
||||
#include "QF/ui/text.h"
|
||||
|
||||
typedef struct imui_state_s {
|
||||
struct imui_state_s *next;
|
||||
struct imui_state_s **prev;
|
||||
char *label;
|
||||
uint32_t label_len;
|
||||
int key_offset;
|
||||
uint32_t entity;
|
||||
bool pressed;
|
||||
} imui_state_t;
|
||||
|
||||
struct imui_ctx_s {
|
||||
canvas_system_t csys;
|
||||
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;
|
||||
imui_state_t *states;
|
||||
font_t *font;
|
||||
};
|
||||
|
||||
static imui_state_t *
|
||||
imui_state_new (imui_ctx_t *ctx)
|
||||
{
|
||||
imui_state_t *state = PR_RESNEW (ctx->state_map);
|
||||
*state = (imui_state_t) {
|
||||
.next = ctx->states,
|
||||
.prev = &ctx->states,
|
||||
.entity = nullent,
|
||||
};
|
||||
if (ctx->states) {
|
||||
ctx->states->prev = &state->next;
|
||||
}
|
||||
ctx->states = state;
|
||||
return state;
|
||||
}
|
||||
/*
|
||||
static void
|
||||
imui_state_free (imui_ctx_t *ctx, imui_state_t *state)
|
||||
{
|
||||
if (state->next) {
|
||||
state->next->prev = state->prev;
|
||||
}
|
||||
*state->prev = state->next;
|
||||
PR_RESFREE (ctx->state_map, state);
|
||||
}
|
||||
*/
|
||||
static imui_state_t *
|
||||
imui_get_state (imui_ctx_t *ctx, const char *label)
|
||||
{
|
||||
int key_offset = 0;
|
||||
uint32_t label_len = ~0u;
|
||||
const char *key = strstr (label, "##");
|
||||
if (key) {
|
||||
// key is '###': hash only past this
|
||||
if (key[2] == '#') {
|
||||
key_offset = (key += 3) - label;
|
||||
}
|
||||
label_len = key - label;
|
||||
} else {
|
||||
key = label;
|
||||
}
|
||||
imui_state_t *state = Hash_Find (ctx->tab, key);
|
||||
if (state) {
|
||||
return state;
|
||||
}
|
||||
state = imui_state_new (ctx);
|
||||
state->label = strdup (label);
|
||||
state->label_len = label_len == ~0u ? strlen (label) : label_len;
|
||||
state->key_offset = key_offset;
|
||||
Hash_Add (ctx->tab, state);
|
||||
return state;
|
||||
}
|
||||
|
||||
static const char *
|
||||
imui_state_getkey (const void *obj, void *data)
|
||||
{
|
||||
auto state = (const imui_state_t *) obj;
|
||||
return state->label + state->key_offset;
|
||||
}
|
||||
|
||||
imui_ctx_t *
|
||||
IMUI_NewContext (canvas_system_t canvas_sys, const char *font, float fontsize)
|
||||
{
|
||||
imui_ctx_t *ctx = malloc (sizeof (imui_ctx_t));
|
||||
uint32_t canvas;
|
||||
*ctx = (imui_ctx_t) {
|
||||
.csys = canvas_sys,
|
||||
.canvas = canvas = Canvas_New (canvas_sys),
|
||||
.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),
|
||||
.tab = Hash_NewTable (511, imui_state_getkey, 0, ctx, &ctx->hashctx),
|
||||
};
|
||||
|
||||
auto fpath = Font_SystemFont (font);
|
||||
if (fpath) {
|
||||
QFile *file = Qopen (fpath, "rb");
|
||||
if (file) {
|
||||
ctx->font = Font_Load (file, fontsize);
|
||||
//Qclose (file); FIXME closed by QFS_LoadFile
|
||||
}
|
||||
free (fpath);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void
|
||||
IMUI_DestroyContext (imui_ctx_t *ctx)
|
||||
{
|
||||
for (auto s = ctx->states; s; s = s->next) {
|
||||
free (s->label);
|
||||
}
|
||||
PR_RESDELMAP (ctx->state_map);
|
||||
|
||||
if (ctx->font) {
|
||||
Font_Free (ctx->font);
|
||||
}
|
||||
|
||||
Hash_DelTable (ctx->tab);
|
||||
Hash_DelContext (ctx->hashctx);
|
||||
free (ctx);
|
||||
}
|
||||
|
||||
void
|
||||
IMUI_SetVisible (imui_ctx_t *ctx, bool visible)
|
||||
{
|
||||
*Canvas_Visible (ctx->csys, ctx->canvas) = visible;
|
||||
}
|
||||
|
||||
void
|
||||
IMUI_SetSize (imui_ctx_t *ctx, int xlen, int ylen)
|
||||
{
|
||||
View_SetLen (ctx->root_view, xlen, ylen);
|
||||
View_UpdateHierarchy (ctx->root_view);
|
||||
}
|
||||
|
||||
void
|
||||
IMUI_Draw (imui_ctx_t *ctx)
|
||||
{
|
||||
View_UpdateHierarchy (ctx->root_view);
|
||||
}
|
||||
|
||||
bool
|
||||
IMUI_Button (imui_ctx_t *ctx, const char *label)
|
||||
{
|
||||
auto state = imui_get_state (ctx, label);
|
||||
if (state->entity == nullent) {
|
||||
auto view = View_New (ctx->vsys, ctx->root_view);
|
||||
state->entity = view.id;
|
||||
|
||||
*(byte*) Ent_AddComponent (view.id, ctx->csys.base + canvas_fill,
|
||||
ctx->csys.reg) = 0;
|
||||
|
||||
View_SetVisible (view, 1);
|
||||
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 len = View_GetLen (text);
|
||||
View_SetLen (view, len.x, len.y);
|
||||
}
|
||||
return state->pressed;
|
||||
}
|
||||
|
||||
bool
|
||||
IMUI_Checkbox (imui_ctx_t *ctx, bool *flag, const char *label)
|
||||
{
|
||||
auto state = imui_get_state (ctx, label);
|
||||
if (state->entity == nullent) {
|
||||
}
|
||||
return *flag;
|
||||
}
|
||||
|
||||
void
|
||||
IMUI_Radio (imui_ctx_t *ctx, int *state, int value, const char *label)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
IMUI_Slider (imui_ctx_t *ctx, float *value, float minval, float maxval,
|
||||
const char *label)
|
||||
{
|
||||
}
|
Loading…
Reference in a new issue