basic layout system functional

This commit is contained in:
Matthew 2025-09-02 07:07:03 +10:00
parent 1bbf142039
commit 34b7501007
5 changed files with 165 additions and 42 deletions

@ -1 +1 @@
Subproject commit a7d571bd5a1bdbe7aa15e5cacfba8795f2e5979b Subproject commit ad1b167e2b527a2386f7a42e6f908a0f667b5089

View File

@ -4,9 +4,11 @@ import dlib.platform;
import dlib.fonts; import dlib.fonts;
import vulkan; import vulkan;
import dlib.math; import dlib.math;
import std.format : sformat;
import dlib.alloc; import dlib.alloc;
import buffer; import buffer;
import ui; import ui;
import widgets;
import std.stdio; import std.stdio;
import std.exception; import std.exception;
@ -174,12 +176,10 @@ CreateEditor(PlatformWindow* window, u8[] buffer_data, u8[] buffer_name)
} }
void void
Cycle(Editor* ed) Cycle(Editor* ed, Inputs* inputs)
{ {
Reset(&ed.temp_arena); Reset(&ed.temp_arena);
Logf("begin");
UIBeginBuild(); UIBeginBuild();
Logf("end");
/* /*
f32 pos = 0.0; f32 pos = 0.0;
@ -187,6 +187,36 @@ Cycle(Editor* ed)
DrawBuffer(ed, 0.0, 0.0, h, ed.active_buffer); DrawBuffer(ed, 0.0, 0.0, h, ed.active_buffer);
*/ */
u32 count = 0;
static u32 box_count = 1;
for(auto ev = inputs.list.first; ev != null; ev = ev.next)
{
count += 1;
Logf("%s", ev.value.key);
switch(ev.value.key)
{
case KBI.One: box_count = 1; break;
case KBI.Two: box_count = 2; break;
case KBI.Three: box_count = 3; break;
case KBI.Four: box_count = 4; break;
case KBI.Five: box_count = 5; break;
case KBI.Six: box_count = 6; break;
case KBI.Seven: box_count = 7; break;
case KBI.Eight: box_count = 8; break;
case KBI.Nine: box_count = 9; break;
default: break;
}
}
char[128] buf = 0;
f32 x_pct = 1.0/box_count;
foreach(i; 0 .. box_count)
{
buf.sformat("##%s", i);
Logf("%r %s %s %f", buf, i, box_count, x_pct);
WindowItem(cast(u8[])buf, x_pct, 1.0, Vec4(Vec3(x_pct * i), 1.0));
}
BeginFrame(&ed.rd); BeginFrame(&ed.rd);
UVec2 ext = UVec2(GetExtent(&ed.rd)); UVec2 ext = UVec2(GetExtent(&ed.rd));

View File

@ -30,6 +30,6 @@ void main(string[] argv)
break; break;
} }
Cycle(&editor); Cycle(&editor, &window.inputs);
} }
} }

View File

@ -31,6 +31,7 @@ struct UIContext
u32 tab_width; u32 tab_width;
f32 text_scale; f32 text_scale;
Stack!(UIItem) parent_stack; Stack!(UIItem) parent_stack;
Axis2D layout_axis;
} }
struct Stack(T) struct Stack(T)
@ -61,6 +62,7 @@ enum CtxProperty
Padding, Padding,
TextScale, TextScale,
TabWidth, TabWidth,
LayoutAxis,
} }
alias CtxP = CtxProperty; alias CtxP = CtxProperty;
@ -108,6 +110,7 @@ alias SK = SizeKind;
union Rect union Rect
{ {
Vec2[2] v;
struct struct
{ {
Vec2 vec0; Vec2 vec0;
@ -133,6 +136,13 @@ struct UISize
f32 strictness; f32 strictness;
} }
struct UIItemRec
{
UIItem* next;
i32 push_count;
i32 pop_count;
}
struct UIItem struct UIItem
{ {
UIProperties flags; UIProperties flags;
@ -153,6 +163,7 @@ struct UIItem
f32[A2D.max] fixed_position; f32[A2D.max] fixed_position;
Rect rect; Rect rect;
f32 text_scale; f32 text_scale;
Axis2D layout_axis;
} }
struct UIKey struct UIKey
@ -194,7 +205,7 @@ UIFinishBuild()
// 1. Pixel/Text sizes (any order) // 1. Pixel/Text sizes (any order)
for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root)) for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root).next)
{ {
foreach(i, ref s; item.size) foreach(i, ref s; item.size)
{ {
@ -202,34 +213,87 @@ UIFinishBuild()
{ {
item.computed_size[i] = s.value; item.computed_size[i] = s.value;
} }
else if (s.kind == SK.TextContent)
{
// Text size + padding
}
} }
} }
// 2. PercentOfParent (Upward dependant) size (pre-order) (Ignore downward dependant sizes) // 2. PercentOfParent (Upward dependant) size (pre-order) (Ignore downward dependant sizes)
// 3. ChildrenSum (Downward dependant) (post-order) u32 count = 0;
// 4. Solve violations e.g. extending past boundaries of parent (pre-order) for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root).next)
// 5. Compute relative positions of each widgets (pre-order)
for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root))
{ {
if (Nil(item.parent)) for(UIItem* child = item.first; !Nil(child); child = child.next)
{ {
item.computed_rel_pos = item.fixed_position; foreach(i, ref s; child.size)
{
if (s.kind == SK.PercentOfParent)
{
UIItem* fixed_parent = g_UI_NIL;
for(UIItem* p = child.parent; !Nil(p); p = p.parent)
{
if (p.size[i].kind == SK.Pixels || p.size[i].kind == SK.TextContent)
{
fixed_parent = p;
break;
}
}
item.rect.x0 = item.computed_rel_pos[A2D.X]; child.computed_size[i] = fixed_parent.computed_size[i] * s.value;
item.rect.y0 = item.computed_rel_pos[A2D.Y]; }
item.rect.x1 = item.computed_size[A2D.X]; }
item.rect.y1 = item.computed_size[A2D.Y];
} }
} }
for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root)) // 3. ChildrenSum (Downward dependant) (post-order)
{ {
if (!Nil(item)) UIItemRec rec;
foreach(axis; A2D.min .. A2D.max)
{ {
if (item.flags & UIP.DrawBackground) for(UIItem* item = ui.root; !Nil(item); item = rec.next)
{ {
DrawRect(ui, item); rec = Recurse!(UIR.PreOrder)(item, ui.root);
i32 pop_index = 0;
for(UIItem* i = item; !Nil(item) && pop_index <= rec.pop_count; i = i.parent, pop_index += 1)
{
}
}
}
}
// 4. Solve violations e.g. extending past boundaries of parent (pre-order)
// 5. Compute relative positions of each widgets (pre-order)
foreach(axis; A2D.min .. A2D.max)
{
for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root).next)
{
f32 layout_position = 0.0;
for(UIItem* child = item.first; !Nil(child); child = child.next)
{
child.computed_rel_pos[axis] = layout_position;
child.rect.vec0.v[axis] = layout_position;
child.rect.vec1.v[axis] = layout_position + child.computed_size[axis];
if (axis == item.layout_axis)
{
layout_position += child.computed_size[axis];
}
}
}
}
for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root).next)
{
for(UIItem* child = item.first; !Nil(child); child = child.next)
{
if (!Nil(child))
{
if (child.flags & UIP.DrawBackground)
{
DrawRect(ui, child);
}
} }
} }
} }
@ -244,10 +308,10 @@ BuildItem(UIKey key, UIProperties flags)
item.first = item.last = item.next = item.prev = item.parent = g_UI_NIL; item.first = item.last = item.next = item.prev = item.parent = g_UI_NIL;
item.computed_rel_pos = item.computed_size = 0; item.computed_rel_pos = item.computed_size = 0;
item.flags = flags; item.flags = flags;
item.size = ui.size_axes; item.size = ui.size_axes;
item.padding = ui.padding; item.padding = ui.padding;
item.layout_axis = ui.layout_axis;
if (item.flags & UIP.DrawBackground) if (item.flags & UIP.DrawBackground)
{ {
@ -262,6 +326,12 @@ BuildItem(UIKey key, UIProperties flags)
item.text_scale = ui.text_scale; item.text_scale = ui.text_scale;
} }
item.parent = ui.parent_stack.first;
if (!Nil(item.parent))
{
DLLPush(item.parent, item, g_UI_NIL);
}
return item; return item;
} }
@ -473,17 +543,19 @@ Nil(UIItem* item)
return item == null || item == g_UI_NIL; return item == null || item == g_UI_NIL;
} }
UIItem* UIItemRec
Recurse(alias mode)(UIItem* item, UIItem* root) Recurse(alias mode)(UIItem* item, UIItem* root)
{ {
UIItem* sibling, child, result = g_UI_NIL; UIItem* sibling, child;
UIItemRec result = UIItemRec(next: g_UI_NIL);
static if (mode == UIR.PostOrder) sibling = item.next; else sibling = item.prev; static if (mode == UIR.PostOrder) sibling = item.next; else sibling = item.prev;
static if (mode == UIR.PostOrder) child = item.prev; else child = item.last; static if (mode == UIR.PostOrder) child = item.prev; else child = item.last;
if (!Nil(child)) if (!Nil(child))
{ {
result = sibling; result.next = child;
result.push_count = 1;
} }
else for(UIItem* i = sibling; !Nil(i) && i != root; i = i.parent) else for(UIItem* i = sibling; !Nil(i) && i != root; i = i.parent)
{ {
@ -491,9 +563,11 @@ Recurse(alias mode)(UIItem* item, UIItem* root)
if (!Nil(sibling)) if (!Nil(sibling))
{ {
result = sibling; result.next = sibling;
break; break;
} }
result.pop_count += 1;
} }
return result; return result;
@ -503,17 +577,20 @@ Recurse(alias mode)(UIItem* item, UIItem* root)
pragma(inline) void pragma(inline) void
SetProp(alias P, T)(T value) SetProp(alias P, T)(T value)
{ {
static if (is(T: Vec4)) static if (is(T: Axis2D))
{ {
static if (0) {} static if (P == CtxP.LayoutAxis) g_ui_ctx.layout_axis = value;
else static if (P == CtxP.BGColor) g_ui_ctx.bg_cols = [value, value, value, value]; else static assert(false, "Unknown property for type Axis2D");
}
else static if (is(T: Vec4))
{
static if (P == CtxP.BGColor) g_ui_ctx.bg_cols = [value, value, value, value];
else static if (P == CtxP.BorderColor) g_ui_ctx.border_cols = [value, value, value, value]; else static if (P == CtxP.BorderColor) g_ui_ctx.border_cols = [value, value, value, value];
else static assert(false, "Unknown Property for type Vec4"); else static assert(false, "Unknown Property for type Vec4");
} }
else static if (is(T: Vec4[4])) else static if (is(T: Vec4[4]))
{ {
static if (0) {} static if (P == CtxP.BGColor) g_ui_ctx.bg_cols = value;
else static if (P == CtxP.BGColor) g_ui_ctx.bg_cols = value;
else static if (P == CtxP.BorderColor) g_ui_ctx.border_cols = value; else static if (P == CtxP.BorderColor) g_ui_ctx.border_cols = value;
else static assert(false, "Unknown Property for type Vec4[4]"); else static assert(false, "Unknown Property for type Vec4[4]");
} }
@ -523,32 +600,28 @@ SetProp(alias P, T)(T value)
} }
else static if (is(T: UISize)) else static if (is(T: UISize))
{ {
static if (0) {} static if (P == CtxP.AxisX) g_ui_ctx.size_axes[A2D.X] = value;
else static if (P == CtxP.AxisX) g_ui_ctx.size_axes[A2D.X] = value;
else static if (P == CtxP.AxisY) g_ui_ctx.size_axes[A2D.Y] = value; else static if (P == CtxP.AxisY) g_ui_ctx.size_axes[A2D.Y] = value;
else static assert(false, "Unknown Property for type UISize"); else static assert(false, "Unknown Property for type UISize");
} }
else static if (is(T: FontAtlas)) else static if (is(T: FontAtlas))
{ {
static if (0) {} static if (P == CtxP.FontAtlas) g_ui_ctx.atlas = value;
else static if (P == CtxP.FontAtlas) g_ui_ctx.atlas = value;
else static assert(false, "Unknown Property for type FontAtlas"); else static assert(false, "Unknown Property for type FontAtlas");
} }
else static if (is(T: Rect)) else static if (is(T: Rect))
{ {
static if (0) {} static if (P == CtxP.Padding) g_ui_ctx.padding = value;
else static if (P == CtxP.Padding) g_ui_ctx.padding = value; else static assert(false, "Unknown property for type Rect");
} }
else static if (is(T: f32)) else static if (is(T: f32))
{ {
static if (0) {} static if (P == CtxP.TextScale) g_ui_ctx.text_scale = value;
else static if (P == CtxP.TextScale) g_ui_ctx.text_scale = value;
else static assert(false, "Unknown property for type f32"); else static assert(false, "Unknown property for type f32");
} }
else static if (is(T: u32)) else static if (is(T: u32))
{ {
static if (0) {} static if (P == CtxP.TabWidth) g_ui_ctx.tab_width = value;
else static if (P == CtxP.TabWidth) g_ui_ctx.tab_width = value;
else static assert(false, "Unknown property for type u32"); else static assert(false, "Unknown property for type u32");
} }
else static assert(false, "Unknown Type"); else static assert(false, "Unknown Type");

View File

@ -2,6 +2,11 @@ import dlib;
import ui; import ui;
struct Window
{
}
UIItem* UIItem*
RootItem() RootItem()
{ {
@ -12,8 +17,23 @@ RootItem()
SetProp!(CtxP.AxisX)(UISize(SK.Pixels, size.x, 1.0)); SetProp!(CtxP.AxisX)(UISize(SK.Pixels, size.x, 1.0));
SetProp!(CtxP.AxisY)(UISize(SK.Pixels, size.y, 1.0)); SetProp!(CtxP.AxisY)(UISize(SK.Pixels, size.y, 1.0));
SetProp!(CtxP.BGColor)(Vec4(0.2, 0.5, 0.85, 1.0)); SetProp!(CtxP.BGColor)(Vec4(0.2, 0.5, 0.85, 1.0));
SetProp!(CtxP.LayoutAxis)(A2D.X);
UIItem* item = BuildItem(key, UIP.DrawBackground); UIItem* item = BuildItem(key, UIP.DrawBackground);
return item; return item;
}
UIItem*
WindowItem(u8[] str, f32 x_pct, f32 y_pct, Vec4 col)
{
UIKey key = MakeKey(str);
SetProp!(CtxP.AxisX)(UISize(SK.PercentOfParent, x_pct));
SetProp!(CtxP.AxisY)(UISize(SK.PercentOfParent, y_pct));
SetProp!(CtxP.BGColor)(col);
UIItem* item = BuildItem(key, UIP.DrawBackground);
return item;
} }