diff --git a/src/dlib b/src/dlib index 263cd84..a7d571b 160000 --- a/src/dlib +++ b/src/dlib @@ -1 +1 @@ -Subproject commit 263cd8435ddd7a1cf67b39c2933e606a891bfe7b +Subproject commit a7d571bd5a1bdbe7aa15e5cacfba8795f2e5979b diff --git a/src/editor/editor.d b/src/editor/editor.d index e237592..21b04c9 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -16,7 +16,9 @@ const u8[] FONT_BYTES = import("pc-9800.ttf"); const u8[] VERTEX_BYTES = import("gui.vert.spv"); const u8[] FRAGMENT_BYTES = import("gui.frag.spv"); -const UI_COUNT = 50000; +const UI_COUNT = 5000; + +Editor* g_editor; struct Editor { @@ -175,11 +177,15 @@ void Cycle(Editor* ed) { Reset(&ed.temp_arena); - UIBeginFrame(); + Logf("begin"); + UIBeginBuild(); + Logf("end"); + /* f32 pos = 0.0; f32 h = ed.atlas_buf.atlas.size; DrawBuffer(ed, 0.0, 0.0, h, ed.active_buffer); + */ BeginFrame(&ed.rd); @@ -196,6 +202,10 @@ Cycle(Editor* ed) Bind(&ed.rd, ed.pipeline, ed.desc_set); + UIFinishBuild(); + + DrawUI(); + FinishRendering(&ed.rd); SubmitAndPresent(&ed.rd); diff --git a/src/editor/ui.d b/src/editor/ui.d index cfc4f07..08a0c81 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -4,6 +4,7 @@ import dlib.util; import dlib.alloc; import dlib.fonts; import vulkan; +import widgets; import editor; @@ -29,6 +30,12 @@ struct UIContext Rect padding; u32 tab_width; f32 text_scale; + Stack!(UIItem) parent_stack; +} + +struct Stack(T) +{ + T* first; } struct UIBuffer @@ -43,7 +50,7 @@ struct UIBuffer enum CtxProperty { None, - BackgroundColor, + BGColor, BorderColor, SizeKind, SizeValue, @@ -129,6 +136,7 @@ struct UISize struct UIItem { UIProperties flags; + UIKey key; UIItem* first; UIItem* last; @@ -137,9 +145,12 @@ struct UIItem UIItem* parent; UISize[A2D.max] size; + Vec4[4] bg_color; + Vec4[4] border_color; Rect padding; f32[A2D.max] computed_rel_pos; f32[A2D.max] computed_size; + f32[A2D.max] fixed_position; Rect rect; f32 text_scale; } @@ -150,14 +161,130 @@ struct UIKey u64 hash; } -void -UIBeginFrame() +UIContext* +GetCtx() { - g_ui_ctx.buffer.count = 0; + return &g_ui_ctx; +} + +Vec2 +RootSize() +{ + Vec2 size = GetExtent(GetCtx().rd); + return size; } void -UIFrameSubmit() +UIBeginBuild() +{ + UIContext* ui = GetCtx(); + + ui.buffer.count = 0; + + ClearStack(&g_ui_ctx.parent_stack, g_UI_NIL); + + ui.root = RootItem(); + PushParent(ui.root); +} + +void +UIFinishBuild() +{ + UIContext* ui = GetCtx(); + + + // 1. Pixel/Text sizes (any order) + for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root)) + { + foreach(i, ref s; item.size) + { + if (s.kind == SK.Pixels) + { + item.computed_size[i] = s.value; + } + } + } + + // 2. PercentOfParent (Upward dependant) size (pre-order) (Ignore downward dependant sizes) + // 3. ChildrenSum (Downward dependant) (post-order) + // 4. Solve violations e.g. extending past boundaries of parent (pre-order) + + // 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)) + { + item.computed_rel_pos = item.fixed_position; + + item.rect.x0 = item.computed_rel_pos[A2D.X]; + 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)) + { + if (!Nil(item)) + { + if (item.flags & UIP.DrawBackground) + { + DrawRect(ui, item); + } + } + } +} + +UIItem* +BuildItem(UIKey key, UIProperties flags) +{ + UIContext* ui = GetCtx(); + + UIItem* item = Get(key); + item.first = item.last = item.next = item.prev = item.parent = g_UI_NIL; + item.computed_rel_pos = item.computed_size = 0; + + + item.flags = flags; + item.size = ui.size_axes; + item.padding = ui.padding; + + if (item.flags & UIP.DrawBackground) + { + item.bg_color = ui.bg_cols; + } + if (item.flags & UIP.DrawBorder) + { + item.border_color = ui.border_cols; + } + if (item.flags & UIP.DrawText) + { + item.text_scale = ui.text_scale; + } + + return item; +} + +void +ClearStack(T)(Stack!(T)* stack, T* nil) +{ + while(SPop(stack, nil) != nil) {} +} + +void +PushParent(UIItem* parent) +{ + SPush(&g_ui_ctx.parent_stack, parent, g_UI_NIL); +} + +UIItem* +PopParent() +{ + return SPop(&g_ui_ctx.parent_stack, g_UI_NIL); +} + +void +DrawUI() { BindBuffers(g_ui_ctx.rd, &g_ui_ctx.buffer.mapped_idx, &g_ui_ctx.buffer.mapped_vtx); DrawIndexed(g_ui_ctx.rd, 6, g_ui_ctx.buffer.count, 0); @@ -179,6 +306,14 @@ InitUIContext(Renderer* rd) g_ui_ctx.buffer.mapped_idx = idx; g_ui_ctx.buffer.vtx = vtx.data; g_ui_ctx.buffer.idx = idx.data; + g_ui_ctx.parent_stack.first = g_UI_NIL; +} + +UIKey +MakeKey(string str) +{ + u8[] id = (cast(u8*)str)[0 .. str.length]; + return MakeKey(id); } UIKey @@ -231,27 +366,18 @@ MakeKey(u8[] id) } UIItem* -Find(u8[] id) +Get(UIKey key) { - u64 hash = Hash(id); - Result!(UIItem*) result = g_ui_ctx.items[hash]; + Result!(UIItem*) result = g_ui_ctx.items[key.hash]; if (!result.ok) { result.value = Alloc!(UIItem)(&g_ui_ctx.arena); - Push(&g_ui_ctx.items, hash, result.value); + Push(&g_ui_ctx.items, key.hash, result.value); } return result.value; } -bool -UIWindow(Rect rect, f32 border_px, u8[] text) -{ - UIItem* item = Find(text); - - return false; -} - f32 CalcTextWidth(u8[] str) { @@ -312,25 +438,21 @@ DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, Vec4 col = Vec4(1.0)) *x_pos += glyph.advance * scale; } -/* pragma(inline) void -DrawRect(UIContext* ctx, f32 p0_x, f32 p0_y, f32 p1_x, f32 p1_y, f32 border, f32 corner, f32 softness, f32 raised, Vec4[4] cols) +DrawRect(UIContext* ctx, UIItem* item) { // Y reversed - Vertex* v = g_ui_ctx.buffer.vtx.ptr + g_ui_ctx.buffer.count; - v.dst_start.x = p0_x; - v.dst_start.y = p0_y; - v.dst_end.x = p1_x; - v.dst_end.y = p1_y; - v.cols = cols; - v.border_thickness = border; - v.corner_radius = corner; - v.edge_softness = softness; - v.raised = raised; + Vertex* v = ctx.buffer.vtx.ptr + ctx.buffer.count; + v.dst_start = item.rect.vec0; + v.dst_end = item.rect.vec1; + v.cols = item.bg_color; + v.border_thickness = 0.0; + v.corner_radius = 0.0; + v.edge_softness = 0.0; + v.raised = 0.0; - AddUIIndices(ctx); + AddUIIndices(); } -*/ void AddUIIndices() @@ -381,11 +503,18 @@ Recurse(alias mode)(UIItem* item, UIItem* root) pragma(inline) void SetProp(alias P, T)(T value) { - static if (is(T: Vec4[4])) + static if (is(T: Vec4)) { static if (0) {} - else static if (P == CtxP.BackgroundColor) { g_ui_ctx.bg_cols = value; } - else static if (P == CtxP.BorderColor) g_ui_ctx.border_cols = value; + else 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 assert(false, "Unknown Property for type Vec4"); + } + else static if (is(T: Vec4[4])) + { + static if (0) {} + 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 assert(false, "Unknown Property for type Vec4[4]"); } else static if (is(T: SizeKind) && P == CtxP.SizeKind) diff --git a/src/editor/widgets.d b/src/editor/widgets.d new file mode 100644 index 0000000..4355731 --- /dev/null +++ b/src/editor/widgets.d @@ -0,0 +1,19 @@ +import dlib; + +import ui; + +UIItem* +RootItem() +{ + Vec2 size = RootSize(); + + UIKey key = MakeKey("##root_key"); + + SetProp!(CtxP.AxisX)(UISize(SK.Pixels, size.x, 1.0)); + SetProp!(CtxP.AxisY)(UISize(SK.Pixels, size.y, 1.0)); + SetProp!(CtxP.BGColor)(Vec4(0.2, 0.5, 0.85, 1.0)); + + UIItem* item = BuildItem(key, UIP.DrawBackground); + + return item; +}