more work on UI, starting to replace existing UI code

This commit is contained in:
Matthew 2025-12-13 20:56:52 +11:00
parent ad3ababe97
commit 8acbeee072
4 changed files with 250 additions and 90 deletions

View File

@ -16,8 +16,9 @@
"versions": ["VULKAN_DEBUG", "ENABLE_RENDERER"], "versions": ["VULKAN_DEBUG", "ENABLE_RENDERER"],
"preGenerateCommands-linux": ["./build.sh"], "preGenerateCommands-linux": ["./build.sh"],
"preGenerateCommands-windows": [], "preGenerateCommands-windows": [],
"dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-link-debuglib"], "dflags": ["-P-I/usr/include/freetype2", "-Jbuild", "-Jassets"],
"dflags-dmd": ["-P=-DSTBI_NO_SIMD"] "dflags-ldc2": ["-link-debuglib"],
"dflags-dmd": []
}, },
{ {
"name": "editor-test", "name": "editor-test",
@ -33,8 +34,9 @@
"versions": ["VULKAN_DEBUG"], "versions": ["VULKAN_DEBUG"],
"preGenerateCommands-linux": ["./build.sh"], "preGenerateCommands-linux": ["./build.sh"],
"preGenerateCommands-windows": [], "preGenerateCommands-windows": [],
"dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-link-debuglib", "-unittest"], "dflags": ["-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-unittest"],
"dflags-dmd": ["-P=-DSTBI_NO_SIMD"] "dflags-ldc2": ["-link-debuglib"],
"dflags-dmd": []
}, },
] ]
} }

@ -1 +1 @@
Subproject commit e5f91cae6d90c40e458e1208c2ab8f8eb917c99a Subproject commit 617e03f917911f6f9898086720a6539756f79758

View File

@ -141,6 +141,29 @@ Cycle(EditorCtx* ctx, Inputs* inputs)
BeginUI(inputs); BeginUI(inputs);
UICtx* ui_ctx = GetCtx();
Vec4[4] col0 = Vec4(0.2, 0.4, 0.8, 1.0);
Vec4[4] col1 = Vec4(0.8, 0.4, 0.2, 1.0);
Vec4[4] col_sep = Vec4(0.2, 0.8, 0.1, 1.0);
Push!("size_info")(ui_ctx, MakeUISizeX(ST.Percentage, 0.5));
Push!("bg_col", true)(ui_ctx, col0);
UIItem* p0 = MakeItem("###p0", UIF.DrawBackground|UIF.Resizeable);
Logf("%s", *ui_ctx.size_info.top);
Push!("size_info", true)(ui_ctx, MakeUISizeX(ST.Pixels, 2.0));
Logf("%s", *ui_ctx.size_info.top);
Push!("bg_col", true)(ui_ctx, col_sep);
UIItem* sep = MakeItem("###sep", UIF.Draggable|UIF.DrawBackground|UIF.ResizeAdjacent);
Logf("%s", *ui_ctx.size_info.top);
Push!("bg_col", true)(ui_ctx, col1);
UIItem* p1 = MakeItem("###p1", UIF.DrawBackground|UIF.Resizeable);
/*
UIPanel* root = ctx.base_panel; UIPanel* root = ctx.base_panel;
root.size.x = g_ui_ctx.res.x; root.size.x = g_ui_ctx.res.x;
@ -150,7 +173,7 @@ Cycle(EditorCtx* ctx, Inputs* inputs)
static foreach(axis; A2D.min .. A2D.max) static foreach(axis; A2D.min .. A2D.max)
{ {
for(UIPanel* p = root; !Nil(p); p = Recurse(p, root, g_UI_NIL_PANEL)) for(UIPanel* p = root; !Nil(p); p = Recurse(p, g_UI_NIL_PANEL))
{ {
f32 pos = p.rect.p0.v[axis]; f32 pos = p.rect.p0.v[axis];
for(UIPanel* c = p.first; !Nil(c); c = c.next) for(UIPanel* c = p.first; !Nil(c); c = c.next)
@ -167,7 +190,7 @@ Cycle(EditorCtx* ctx, Inputs* inputs)
} }
} }
for(auto p = ctx.base_panel; !Nil(p); p = Recurse(p, ctx.base_panel, g_UI_NIL_PANEL)) for(auto p = ctx.base_panel; !Nil(p); p = Recurse(p, g_UI_NIL_PANEL))
{ {
Panel(p); Panel(p);
} }
@ -181,6 +204,7 @@ Cycle(EditorCtx* ctx, Inputs* inputs)
CommandPalette(&ctx.cmd); CommandPalette(&ctx.cmd);
} }
*/
EndUI(); EndUI();
} }

View File

@ -14,6 +14,7 @@ import std.math.traits : isNaN;
import std.math.rounding : ceil, floor; import std.math.rounding : ceil, floor;
import std.math.exponential : pow; import std.math.exponential : pow;
import std.math.remainder : fmod; import std.math.remainder : fmod;
import std.algorithm.comparison : clamp;
import std.format : sformat; import std.format : sformat;
import core.stdc.string : memset; import core.stdc.string : memset;
import core.stdc.math : fabsf; import core.stdc.math : fabsf;
@ -102,12 +103,17 @@ enum UIFlags
ScrollY = 1<<6, ScrollY = 1<<6,
ClampX = 1<<7, ClampX = 1<<7,
ClampY = 1<<8, ClampY = 1<<8,
Resizeable = 1<<9,
ResizeAdjacent = 1<<10,
FloatingWindow = 1<<11,
CenteredWindow = 1<<12,
TextInput = 1<<13,
Clamp = UIFlags.ClampX | UIFlags.ClampY, Clamp = UIFlags.ClampX | UIFlags.ClampY,
} }
alias UIF = UIFlags; alias UIF = UIFlags;
const UIFlags AUTO_FLAGS = UIF.ResizeAdjacent;
enum UISignal enum UISignal
{ {
@ -159,35 +165,27 @@ UICtxParameter(T, string name)
template CtxMemberInfo(int i) template CtxMemberInfo(int i)
{ {
import std.string : endsWith; import std.string : endsWith, startsWith;
import std.traits : isInstanceOf, isPointer; import std.traits : isInstanceOf, isPointer;
struct MemberInfo struct MemberInfo
{ {
string id; string id;
bool is_stack, is_top, is_stack_top; bool is_stack, is_top;
} }
enum string id = __traits(identifier, UICtx.tupleof[i]); enum string id = __traits(identifier, UICtx.tupleof[i]);
enum bool is_ptr = isPointer!(typeof(UICtx.tupleof[i]));
enum bool is_top = endsWith(id, "_top"); enum bool is_top = endsWith(id, "_top");
enum bool is_stack_top = isInstanceOf!(StackTop, typeof(UICtx.tupleof[i])); enum bool is_stack = startsWith(typeof(UICtx.tupleof[i]).stringof, "StackTop!");
static if(is_ptr) enum MemberInfo CtxMemberInfo = MemberInfo(id: id, is_stack: is_stack, is_top: is_top);
{
enum bool is_stack = isInstanceOf!(Stack, typeof(*UICtx.tupleof[i]));
}
else
{
enum bool is_stack = false;
}
enum MemberInfo CtxMemberInfo = MemberInfo(id: id, is_stack: is_ptr && is_stack, is_top: is_top, is_stack_top: is_stack_top);
} }
struct UICtx struct UICtx
{ {
HashTable!(UIHash, UIItem*) items; HashTable!(UIHash, UIItem*) items;
UIItem* free_items;
Arena arena; Arena arena;
Arena temp_arena; Arena temp_arena;
Inputs* inputs; Inputs* inputs;
@ -219,6 +217,7 @@ struct UICtx
u32 tab_width; u32 tab_width;
f32 text_size; f32 text_size;
UIItem* window_root;
UIItem* root; UIItem* root;
UIItem* drag_item; UIItem* drag_item;
UIPanel* parent_panel; UIPanel* parent_panel;
@ -245,13 +244,13 @@ struct Stack(T)
{ {
Stack!(T)* next; Stack!(T)* next;
T value; T value;
bool auto_pop;
} }
struct StackTop(T) struct StackTop(T)
{ {
Stack!(T)* top; Stack!(T)* top;
Stack!(T)* free; Stack!(T)* free;
bool auto_pop;
} }
mixin template UIItemParameters() mixin template UIItemParameters()
@ -264,7 +263,7 @@ mixin template UIItemParameters()
{ {
{ {
enum info = CtxMemberInfo!(i); enum info = CtxMemberInfo!(i);
static if(info.is_stack_top) static if(info.is_stack)
{ {
fields ~= typeof(UICtx.tupleof[i].top.value).stringof ~ " " ~ info.id ~ ";\n"; fields ~= typeof(UICtx.tupleof[i].top.value).stringof ~ " " ~ info.id ~ ";\n";
} }
@ -279,17 +278,20 @@ mixin template UIItemParameters()
struct UIItem struct UIItem
{ {
UIKey key; IVec2 dragged;
UIFlags flags; UIKey key;
u64 last_frame;
IVec2 dragged;
UIItem* next, prev, first, last; // parent in mixin u64 last_frame;
UISignal signal;
UIFlags flags;
Rect rect; UIItem* next, prev, first, last; // parent in mixin
Vec2 size;
u8[] display_string; Rect rect;
Vec2 size;
f32 resize_pct;
u8[] display_string;
mixin UIItemParameters!(); mixin UIItemParameters!();
} }
@ -417,6 +419,7 @@ InitUICtx(PlatformWindow* window)
UICtx ctx = { UICtx ctx = {
items: CreateHashTable!(UIHash, UIItem*)(12), items: CreateHashTable!(UIHash, UIItem*)(12),
free_items: Alloc!(UIItem)(&arena),
arena: arena, arena: arena,
temp_arena: CreateArena(MB(1)), temp_arena: CreateArena(MB(1)),
drag_item: g_UI_NIL, drag_item: g_UI_NIL,
@ -426,6 +429,9 @@ InitUICtx(PlatformWindow* window)
tab_width: 2, tab_width: 2,
}; };
UIItem* fi = ctx.free_items;
fi.first = fi.last = fi.next = fi.prev = fi.parent = g_UI_NIL;
ctx.atlas_buf = CreateAtlas(&arena, ctx.font, 16.0, 256); ctx.atlas_buf = CreateAtlas(&arena, ctx.font, 16.0, 256);
version(ENABLE_RENDERER) version(ENABLE_RENDERER)
@ -435,9 +441,10 @@ InitUICtx(PlatformWindow* window)
for(u64 i = 0; i < FRAME_OVERLAP; i += 1) for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
{ {
ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(&ctx.rd, BT.Vertex, VERTEX_MAX_COUNT); ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(&ctx.rd, BT.Vertex, VERTEX_MAX_COUNT);
ctx.buffers[i].m_idx = CreateMappedBuffer!(u32)(&ctx.rd, BT.Index, cast(u64)(ceil(VERTEX_MAX_COUNT*1.5))); ctx.buffers[i].m_idx = CreateMappedBuffer!(u32)(&ctx.rd, BT.Index, 6);
ctx.buffers[i].vtx = ctx.buffers[i].m_vtx.data; ctx.buffers[i].vtx = ctx.buffers[i].m_vtx.data;
ctx.buffers[i].idx = ctx.buffers[i].m_idx.data; ctx.buffers[i].idx = ctx.buffers[i].m_idx.data;
ctx.buffers[i].idx[0 .. $] = [0, 1, 2, 2, 1, 3];
} }
DescLayoutBinding[2] layout_bindings = [ DescLayoutBinding[2] layout_bindings = [
@ -513,33 +520,54 @@ Set(UIItem* item, UICtx* ctx)
} }
UIItem* UIItem*
MakeItem(T)(T k) if(is(T: UIKey) || StringType!T) MakeItem(T)(T k, UIFlags flags = UIF.None) if(is(T: UIKey) || StringType!T)
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
UIItem* item = Get(k); UIItem* item = Get(k);
item.flags = flags;
Set(item, ctx); Set(item, ctx);
if(!Nil(item.parent)) if(item.flags & (UIF.CenteredWindow | UIF.FloatingWindow))
{
item.parent = ctx.window_root;
DLLPush(item.parent, item, g_UI_NIL);
}
else if(!Nil(item.parent))
{ {
DLLPush(item.parent, item, g_UI_NIL); DLLPush(item.parent, item, g_UI_NIL);
} }
AutoPopStacks(ctx); AutoPopStacks(ctx);
if(item.last_frame != ctx.frame-1 && item.flags & UIF.Resizeable)
{
if(item.size_info[item.parent.layout_axis].type != ST.Percentage)
{
Logf("Warning: Resizing not percentage SizeType item, disabling flag");
item.flags &= ~UIF.Resizeable;
}
else
{
item.resize_pct = item.size_info[item.layout_axis].value;
}
}
item.last_frame = ctx.frame;
return item; return item;
} }
UISignal void
Signal(UIItem* item) Signal(UIItem* item)
{ {
UISignal signal; UICtx* ctx = GetCtx();
UICtx* ctx = GetCtx(); i32 x = ctx.mouse_pos.x;
i32 y = ctx.mouse_pos.y;
i32 x = ctx.mouse_pos.x; item.signal = UIS.None;
i32 y = ctx.mouse_pos.y;
if(x >= item.rect.p0.x && x <= item.rect.p1.x && y >= item.rect.p0.y && y <= item.rect.p1.y) if(x >= item.rect.p0.x && x <= item.rect.p1.x && y >= item.rect.p0.y && y <= item.rect.p1.y)
{ {
signal |= UIS.Hovered; item.signal |= UIS.Hovered;
} }
item.dragged = 0; item.dragged = 0;
@ -548,21 +576,27 @@ Signal(UIItem* item)
{ {
bool taken; bool taken;
Logf("%s", i.type);
if(item.flags & UIF.Clickable && i.type == UIE.Click && InBounds(ctx.mouse_pos, &item.rect)) if(item.flags & UIF.Clickable && i.type == UIE.Click && InBounds(ctx.mouse_pos, &item.rect))
{ {
signal |= UIS.Clicked; Logf("clicked");
taken = true; item.signal |= UIS.Clicked;
taken = true;
} }
if(Nil(ctx.drag_item) && item.flags & UIF.Draggable && i.type == UIE.DragStart && InBounds(i.pos, &item.rect)) if(Nil(ctx.drag_item) && item.flags & UIF.Draggable && i.type == UIE.DragStart && InBounds(i.pos, &item.rect))
{ {
signal |= UIS.Dragged; Logf("dragged");
item.signal |= UIS.Dragged;
ctx.drag_item = item; ctx.drag_item = item;
taken = true; taken = true;
} }
if(ctx.drag_item == item && i.type == UIE.Drag) if(ctx.drag_item == item && i.type == UIE.Drag)
{ {
Logf("dragged");
item.signal |= UIS.Dragged;
item.dragged += i.rel; item.dragged += i.rel;
taken = true; taken = true;
} }
@ -572,8 +606,6 @@ Signal(UIItem* item)
DLLRemove(&ctx.events, i, g_UI_NIL_INPUT); DLLRemove(&ctx.events, i, g_UI_NIL_INPUT);
} }
} }
return signal;
} }
void void
@ -593,8 +625,8 @@ BeginUI(Inputs* inputs)
Reset(a); Reset(a);
// Convert Inputs
ctx.events.first = ctx.events.last = null; ctx.events.first = ctx.events.last = null;
static bool dragging; static bool dragging;
static bool mouse_down; static bool mouse_down;
for(auto i = inputs.first; i; i = i.next) for(auto i = inputs.first; i; i = i.next)
@ -633,6 +665,21 @@ BeginUI(Inputs* inputs)
} }
} }
// Clean up items
KVPair!(UIHash, UIItem*)*[] items = GetAllNodes(&ctx.temp_arena, &ctx.items);
foreach(i; 0 .. items.length)
{
UIItem* item = items[i].value;
if(item.last_frame != ctx.frame)
{
Logf("discarding %s", cast(char[])item.key.hash_text);
item.first = item.last = item.parent = item.prev = item.next = g_UI_NIL;
DLLPush(ctx.free_items, item, g_UI_NIL);
}
}
ctx.frame += 1;
// Ctx state // Ctx state
ctx.f_idx = ctx.frame%FRAME_OVERLAP; ctx.f_idx = ctx.frame%FRAME_OVERLAP;
ctx.inputs = inputs; ctx.inputs = inputs;
@ -660,13 +707,16 @@ BeginUI(Inputs* inputs)
} }
memset(ctx.buffers[ctx.f_idx].vtx.ptr, 0, Vertex.sizeof * ctx.buffers[ctx.f_idx].count); memset(ctx.buffers[ctx.f_idx].vtx.ptr, 0, Vertex.sizeof * ctx.buffers[ctx.f_idx].count);
memset(ctx.buffers[ctx.f_idx].idx.ptr, 0, u32.sizeof * ctx.buffers[ctx.f_idx].count);
ctx.buffers[ctx.f_idx].count = 0; ctx.buffers[ctx.f_idx].count = 0;
// Root Item // Root Item
UISize[2] sizes = [UISize(ST.Pixels, ctx.res.x), UISize(ST.Pixels, ctx.res.y)]; UISize[2] sizes = [UISize(ST.Pixels, ctx.res.x), UISize(ST.Pixels, ctx.res.y)];
Push!("size_info", true)(ctx, sizes); Push!("size_info")(ctx, sizes);
ctx.root = MakeItem("###root");
ctx.root = MakeItem("###root");
ctx.window_root = MakeItem("###window_root");
Pop!("size_info")(ctx);
Push!("parent")(ctx, ctx.root); Push!("parent")(ctx, ctx.root);
} }
@ -675,9 +725,45 @@ EndUI()
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
// Automatic signals
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, g_UI_NIL))
{
if(item.flags & AUTO_FLAGS)
{
Signal(item);
if(item.signal & UIS.Dragged)
{
Logf("1");
}
if(item.flags & UIF.ResizeAdjacent && item.dragged != IVec2(0))
{
UIItem* prev = !Nil(item.prev) ? item.prev : g_UI_NIL;
UIItem* next = !Nil(item.next) ? item.next : g_UI_NIL;
if(prev.flags & UIF.Resizeable && next.flags & UIF.Resizeable)
{
Axis2D axis = item.parent.layout_axis;
f32 mov_pct = Remap(item.dragged.v[axis], 0.0, item.parent.size.v[axis], 0.0, 1.0);
if(prev.resize_pct > 0.0 && prev.resize_pct < 1.0 && next.resize_pct > 0.0 && next.resize_pct < 1.0)
{
prev.resize_pct -= mov_pct;
next.resize_pct += mov_pct;
prev.resize_pct = clamp(prev.resize_pct, 0.0, 1.0);
next.resize_pct = clamp(next.resize_pct, 0.0, 1.0);
}
}
}
}
}
// Calculate item properties
static foreach(axis; A2D.min .. A2D.max) static foreach(axis; A2D.min .. A2D.max)
{ {
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, ctx.root, g_UI_NIL)) // Pixel sizes
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, g_UI_NIL))
{ {
if(item.size_info[axis].type == ST.Pixels) if(item.size_info[axis].type == ST.Pixels)
{ {
@ -685,10 +771,16 @@ EndUI()
} }
} }
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, ctx.root, g_UI_NIL)) // Percentage sizes
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, g_UI_NIL))
{ {
if(item.size_info[axis].type == ST.Percentage) if(item.size_info[axis].type == ST.Percentage)
{ {
if(item.layout_axis == axis && item.flags & UIF.Resizeable)
{
item.size_info[axis].value = item.resize_pct;
}
for(UIItem* p = item.parent; !Nil(p); p = p.parent) for(UIItem* p = item.parent; !Nil(p); p = p.parent)
{ {
if(p.size_info[axis].type == ST.Pixels) if(p.size_info[axis].type == ST.Pixels)
@ -700,7 +792,8 @@ EndUI()
} }
} }
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(false)(item, ctx.root, g_UI_NIL)) // Sum of children sizes
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(false)(item, g_UI_NIL))
{ {
if(item.size_info[axis].type == ST.ChildrenSum) if(item.size_info[axis].type == ST.ChildrenSum)
{ {
@ -712,7 +805,8 @@ EndUI()
} }
} }
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, ctx.root, g_UI_NIL)) // Violations
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, g_UI_NIL))
{ {
if(axis == item.layout_axis) if(axis == item.layout_axis)
{ {
@ -724,7 +818,6 @@ EndUI()
if(children_size > item.size[axis]) if(children_size > item.size[axis])
{ {
Logf("%s %s %s %s", axis, cast(char[])item.key.hash_text, children_size, item.size[axis]);
u64 child_count; u64 child_count;
for(UIItem* c = item.first; !Nil(c); c = c.next) for(UIItem* c = item.first; !Nil(c); c = c.next)
{ {
@ -766,6 +859,7 @@ EndUI()
} }
} }
// Calculate final sizes
{ {
f32 pos = 0.0; f32 pos = 0.0;
for(UIItem* item = ctx.root; !Nil(item);) for(UIItem* item = ctx.root; !Nil(item);)
@ -809,6 +903,11 @@ EndUI()
} }
} }
// Render Items
RenderItems(ctx.root);
RenderItems(ctx.window_root);
version(ENABLE_RENDERER) with(ctx) version(ENABLE_RENDERER) with(ctx)
{ {
BindBuffers(&rd, &buffers[f_idx].m_idx, &buffers[f_idx].m_vtx); BindBuffers(&rd, &buffers[f_idx].m_idx, &buffers[f_idx].m_vtx);
@ -817,8 +916,39 @@ EndUI()
FinishRendering(&rd); FinishRendering(&rd);
SubmitAndPresent(&rd); SubmitAndPresent(&rd);
} }
}
ctx.frame += 1; void
RenderItems(UIItem* root)
{
UICtx* ctx = GetCtx();
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, g_UI_NIL))
{
if(item.flags & UIF.DrawBackground)
{
// DrawRect
Vertex* v = GetVertex(ctx);
v.dst_start = item.rect.p0 + item.border_thickness;
v.dst_end = item.rect.p1 - item.border_thickness;
v.cols = item.bg_col;
v.corner_radius = item.corner_radius;
AddVertexCount(ctx);
}
if(item.flags & UIF.DrawBorder)
{
Vertex* v = GetVertex(ctx);
v.dst_start = item.rect.p0;
v.dst_end = item.rect.p1;
v.cols = item.border_col;
v.corner_radius = item.corner_radius;
v.border_thickness = item.border_thickness;
v.edge_softness = item.edge_softness;
AddVertexCount(ctx);
}
}
} }
u32[2] u32[2]
@ -862,11 +992,11 @@ Push(string stack_str, bool auto_pop = false, T)(UICtx* ctx, T value)
node = Alloc!(Stack!(T))(&ctx.temp_arena); node = Alloc!(Stack!(T))(&ctx.temp_arena);
} }
node.next = stack.top; node.next = stack.top;
node.value = value; node.value = value;
node.auto_pop = auto_pop;
stack.top = node; stack.top = node;
stack.auto_pop = auto_pop;
} }
auto auto
@ -902,12 +1032,12 @@ AutoPopStacks(UICtx* ctx)
{ {
{ {
enum member_info = CtxMemberInfo!(i); enum member_info = CtxMemberInfo!(i);
static if(member_info.is_stack && !member_info.is_top) static if(member_info.is_stack)
{ {
if(ctx.tupleof[i].auto_pop) if(ctx.tupleof[i].top.auto_pop)
{ {
ctx.tupleof[i].top.auto_pop = false;
Pop!(member_info.id)(ctx); Pop!(member_info.id)(ctx);
ctx.tupleof[i].auto_pop = false;
} }
} }
} }
@ -924,7 +1054,7 @@ InitStacks(UICtx* ctx)
{ {
{ {
enum member_info = CtxMemberInfo!(i); enum member_info = CtxMemberInfo!(i);
static if(member_info.is_stack && member_info.is_top) static if(member_info.is_top)
{ {
enum global_default = replace("g_@_default", "@", chomp(member_info.id, "_top")); enum global_default = replace("g_@_default", "@", chomp(member_info.id, "_top"));
@ -946,11 +1076,10 @@ ResetStacks(UICtx* ctx)
{ {
{ {
enum member_info = CtxMemberInfo!(i); enum member_info = CtxMemberInfo!(i);
static if(member_info.is_stack_top) static if(member_info.is_stack)
{ {
enum top = replace("ctx.@_top", "@", member_info.id); enum top = replace("ctx.@_top", "@", member_info.id);
ctx.tupleof[i].top = mixin(top); ctx.tupleof[i].top = mixin(top);
ctx.tupleof[i].auto_pop = false;
ctx.tupleof[i].free = null; ctx.tupleof[i].free = null;
assert(ctx.tupleof[i].top.next == null, "Top stack node next isn't null"); assert(ctx.tupleof[i].top.next == null, "Top stack node next isn't null");
@ -960,18 +1089,21 @@ ResetStacks(UICtx* ctx)
} }
T* T*
Recurse(bool pre = true, T)(T* panel, T* root, T* nil) Recurse(bool pre = true, T)(T* node, T* nil)
{ {
T* child = pre ? node.first : node.last;
T* sibling = pre ? node.next : node.prev;
T* result = nil; T* result = nil;
if(!Nil(panel.first)) if(!Nil(child))
{ {
result = pre ? panel.first : panel.last; result = child;
} }
else for(T* p = panel; !Nil(p) && p != root; p = p.parent) else for(T* p = node; !Nil(p); p = p.parent)
{ {
if(!Nil(pre ? p.next : p.prev)) if(!Nil(sibling))
{ {
result = pre ? p.next : p.prev; result = sibling;
break; break;
} }
} }
@ -1115,16 +1247,26 @@ Get(T)(T k) if(is(T: UIKey) || StringType!T)
UIKey key = MakeKey(k); UIKey key = MakeKey(k);
} }
Result!(UIItem*) result = g_ui_ctx.items[key.hash]; Result!(UIItem*) res = g_ui_ctx.items[key.hash];
if(!result.ok) if(!res.ok)
{ {
result.value = Alloc!(UIItem)(&g_ui_ctx.arena); if(!Nil(g_ui_ctx.free_items.first))
HTPush(&g_ui_ctx.items, key.hash, result.value); {
res.value = DLLPop(g_ui_ctx.free_items, g_UI_NIL);
}
else
{
res.value = Alloc!(UIItem)(&g_ui_ctx.arena);
HTPush(&g_ui_ctx.items, key.hash, res.value);
}
res.value.last_frame = g_ui_ctx.frame-2;
} }
result.value.key = key; res.value.key = key;
res.value.next = res.value.prev = res.value.first = res.value.last = res.value.parent = g_UI_NIL;
return result.value; return res.value;
} }
f32 f32
@ -1283,7 +1425,7 @@ DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y)
v.src_end.y = gb.atlas_b; v.src_end.y = gb.atlas_b;
} }
AddUIIndices(ctx); AddVertexCount(ctx);
*x_pos += glyph.advance * scale; *x_pos += glyph.advance * scale;
@ -1382,7 +1524,7 @@ DrawRect(f32 x, f32 y, f32 w, f32 h, f32 corner_radius, f32 border, Vec4[4] cols
v.dst_end -= border; v.dst_end -= border;
} }
AddUIIndices(ctx); AddVertexCount(ctx);
} }
void void
@ -1399,21 +1541,13 @@ DrawBorder(Vec2 pos, Vec2 size, f32 border, f32 radius, f32 softness, Vec4[4] co
v.edge_softness = softness; v.edge_softness = softness;
v.raised = 0.0; v.raised = 0.0;
AddUIIndices(ctx); AddVertexCount(ctx);
} }
pragma(inline) void pragma(inline) void
AddUIIndices(UICtx* ctx) AddVertexCount(UICtx* ctx)
{ {
ctx.buffers[ctx.f_idx].idx[0] = 0;
ctx.buffers[ctx.f_idx].idx[1] = 1;
ctx.buffers[ctx.f_idx].idx[2] = 2;
ctx.buffers[ctx.f_idx].idx[3] = 2;
ctx.buffers[ctx.f_idx].idx[4] = 1;
ctx.buffers[ctx.f_idx].idx[5] = 3;
ctx.buffers[ctx.f_idx].count += 1; ctx.buffers[ctx.f_idx].count += 1;
assert(ctx.buffers[ctx.f_idx].count < VERTEX_MAX_COUNT); assert(ctx.buffers[ctx.f_idx].count < VERTEX_MAX_COUNT);
} }