[ui] Implement auto-layout

TextContent seems redundant at this stage since a text view is always
sized to its content, and PercentOfParent doesn't work yet. Pixels
definitely works and Null seems to work in that it does no sizing or
positioning. Vertical layout is supported but not yet tested, similar
for ChildrenSum, but I can have two buttons side by side.
This commit is contained in:
Bill Currie 2023-07-02 15:04:22 +09:00
parent 153958a51f
commit e98cd1355d
3 changed files with 100 additions and 19 deletions

View file

@ -43,22 +43,6 @@ typedef enum IMUI_SizeKind {
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);

View file

@ -83,6 +83,9 @@ typedef struct viewcont_s {
unsigned resize_y:1; ///< If true, view's height follows parent's.
unsigned bol_suppress:1; ///< If true, view_flow skips at start of line.
unsigned flow_size:1; ///< If true, view's size is adjusted to flow.
unsigned semantic_x:3; ///< layout size control (IMUI_SizeKind)
unsigned semantic_y:3; ///< layout size control (IMUI_SizeKind)
unsigned vertical:1; ///< true: layout is vertical, else horizontal
} viewcont_t;
enum {

View file

@ -35,6 +35,7 @@
#include "QF/ecs.h"
#include "QF/hash.h"
#include "QF/mathlib.h"
#include "QF/progs.h"
#include "QF/quakeio.h"
@ -249,13 +250,102 @@ prune_objects (imui_ctx_t *ctx)
}
}
//FIXME currently works properly only for grav_northwest
static void
layout_objects (imui_ctx_t *ctx)
{
auto ref = View_GetRef (ctx->root_view);
auto h = ref->hierarchy;
view_pos_t *pos = h->components[view_pos];
view_pos_t *len = h->components[view_len];
viewcont_t *cont = h->components[view_control];
uint32_t *parent = h->parentIndex;
struct boolpair {
bool x, y;
} down_depend[h->num_objects];
// the root view size is always explicity
down_depend[0] = (struct boolpair) { false, false };
for (uint32_t i = 1; i < h->num_objects; i++) {
if (cont[i].semantic_x == IMUI_SizeKind_ChildrenSum) {
down_depend[i].x = 1;
} else if (!(down_depend[i].x = down_depend[parent[i]].x)
&& cont[i].semantic_x == IMUI_SizeKind_PercentOfParent) {
len[i].x = (len[parent[i]].x * 100) / 100; //FIXME precent
}
if (cont[i].semantic_y == IMUI_SizeKind_ChildrenSum) {
down_depend[i].y = 1;
} else if (!(down_depend[i].y = down_depend[parent[i]].y)
&& cont[i].semantic_y == IMUI_SizeKind_PercentOfParent) {
len[i].y = (len[parent[i]].y * 100) / 100; //FIXME precent
}
}
for (uint32_t i = h->num_objects; --i > 0; ) {
view_pos_t clen = len[i];
if (cont[i].semantic_x == IMUI_SizeKind_ChildrenSum) {
clen.x = 0;
if (cont[i].vertical) {
for (uint32_t j = 0; j < h->childCount[i]; j++) {
uint32_t child = h->childIndex[i] + j;
clen.x = max (clen.x, len[child].x);
}
} else {
for (uint32_t j = 0; j < h->childCount[i]; j++) {
uint32_t child = h->childIndex[i] + j;
clen.x += len[child].x;
}
}
}
if (cont[i].semantic_y == IMUI_SizeKind_ChildrenSum) {
clen.y = 0;
if (!cont[i].vertical) {
for (uint32_t j = 0; j < h->childCount[i]; j++) {
uint32_t child = h->childIndex[i] + j;
clen.y = max (clen.y, len[child].y);
}
} else {
for (uint32_t j = 0; j < h->childCount[i]; j++) {
uint32_t child = h->childIndex[i] + j;
clen.y += len[child].y;
}
}
}
len[i] = clen;
}
view_pos_t cpos = {};
uint32_t cur_parent = 0;
for (uint32_t i = 1; i < h->num_objects; i++) {
if (parent[i] != cur_parent) {
cur_parent = parent[i];
cpos = (view_pos_t) {};
}
if (cont[i].semantic_x != IMUI_SizeKind_Null
&& cont[i].semantic_y != IMUI_SizeKind_Null) {
pos[i] = cpos;
} else if (cont[i].semantic_x != IMUI_SizeKind_Null) {
pos[i].x = cpos.x;
} else if (cont[i].semantic_y != IMUI_SizeKind_Null) {
pos[i].y = cpos.y;
}
if (cont[parent[i]].vertical) {
cpos.y += cont[i].semantic_y == IMUI_SizeKind_Null ? 0 : len[i].y;
} else {
cpos.x += cont[i].semantic_x == IMUI_SizeKind_Null ? 0 : len[i].x;
}
}
View_UpdateHierarchy (ctx->root_view);
}
void
IMUI_Draw (imui_ctx_t *ctx)
{
ctx->frame_draw = Sys_LongTime ();
prune_objects (ctx);
View_UpdateHierarchy (ctx->root_view);
layout_objects (ctx);
ctx->frame_end = Sys_LongTime ();
}
@ -322,8 +412,12 @@ IMUI_Button (imui_ctx_t *ctx, const char *label)
ctx->csys.reg) = 0;
View_SetVisible (view, 1);
View_SetGravity (view, grav_northwest);
View_SetResize (view, 0, 0);
*View_Control (view) = (viewcont_t) {
.gravity = grav_northwest,
.visible = 1,
.semantic_x = IMUI_SizeKind_Pixels,
.semantic_y = IMUI_SizeKind_Pixels,
};
auto text = add_text (view, state, ctx);
auto len = View_GetLen (text);