From de90479aa7ce7508f7cc5e84196774124d0d44cb Mon Sep 17 00:00:00 2001 From: Matthew Date: Wed, 10 Sep 2025 06:18:53 +1000 Subject: [PATCH] input handling added, panels now resizeable, panels now have persistent state --- src/dlib | 2 +- src/editor/editor.d | 119 +++++++++++++++++++++++++++++++++---------- src/editor/ui.d | 89 ++++++++++++++++++++++++-------- src/editor/widgets.d | 101 +++++++++++++++++++++++++++++++++--- 4 files changed, 257 insertions(+), 54 deletions(-) diff --git a/src/dlib b/src/dlib index b2b6fef..f30edc3 160000 --- a/src/dlib +++ b/src/dlib @@ -1 +1 @@ -Subproject commit b2b6fef26c0b0e15575ee431eea60235d4565c34 +Subproject commit f30edc3bbe9fdfda5eb429a214fdf050a0012af0 diff --git a/src/editor/editor.d b/src/editor/editor.d index a352c34..95e02fb 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -182,40 +182,107 @@ Cycle(Editor* ed, Inputs* inputs) BeginBuild(inputs); - Panel("##panel_1", 1.0, 0.3, A2D.X, Vec4(0.2, 0.5, 0.8, 1.0)); - { - Panel("##sub_panel_1", 0.125, 1.0, A2D.X, Vec4(0.2, 0.4, 0.5, 1.0)); - EndPanel(); - - Panel("##sub_panel_2", 0.675, 1.0, A2D.X, Vec4(0.9, 0.6, 0.5, 1.0)); - EndPanel(); - - Panel("##sub_panel_3", 0.2, 1.0, A2D.X, Vec4(1.0, 0.4, 0.5, 1.0)); - EndPanel(); - } - EndPanel(); - - Panel("##panel_2", 1.0, 0.3, A2D.X, Vec4(0.5, 0.2, 0.45, 1.0)); - { - - } - EndPanel(); - - Panel("##panel_3", 1.0, 0.4, A2D.X, Vec4(0.3, 0.7, 0.6, 1.0)); - { - Panel("##sub_panel_6", 0.25, 1.0, A2D.Y, Vec4(0.33, 0.4, 0.8, 1.0)); + static UIPanel[11] panels = [ { - Panel("##sub_sub_panel_1", 1.0, 0.4, A2D.X, Vec4(1.0, 0.0, 0.0, 1.0)); + id: CastStr!(u8)("##panel_1"), + pct: 0.3, + axis: A2D.X, + color: Vec4(0.2, 0.5, 0.8, 1.0), + }, + { + id: CastStr!(u8)("##sub_panel_1"), + pct: 0.125, + axis: A2D.X, + color: Vec4(0.2, 0.4, 0.5, 1.0), + }, + { + id: CastStr!(u8)("##sub_panel_2"), + pct: 0.675, + axis: A2D.X, + color: Vec4(0.9, 0.6, 0.5, 1.0), + }, + { + id: CastStr!(u8)("##sub_panel_3"), + pct: 0.2, + axis: A2D.X, + color: Vec4(1.0, 0.4, 0.5, 1.0), + }, + { + id: CastStr!(u8)("##panel_2"), + pct: 0.3, + axis: A2D.X, + color: Vec4(0.5, 0.2, 0.45, 1.0), + }, + { + id: CastStr!(u8)("##panel_3"), + pct: 0.4, + axis: A2D.X, + color: Vec4(0.3, 0.7, 0.6, 1.0), + }, + { + id: CastStr!(u8)("##sub_panel_4"), + pct: 0.25, + axis: A2D.Y, + color: Vec4(0.33, 0.4, 0.8, 1.0), + }, + { + id: CastStr!(u8)("##sub_sub_panel_1"), + pct: 0.4, + axis: A2D.X, + color: Vec4(1.0, 0.0, 0.0, 1.0), + }, + { + id: CastStr!(u8)("##sub_sub_panel_2"), + pct: 0.6, + axis: A2D.X, + color: Vec4(1.0, 1.0, 0.0, 1.0), + }, + { + id: CastStr!(u8)("##sub_panel_5"), + pct: 0.55, + axis: A2D.X, + color: Vec4(0.9, 0.2, 0.3, 1.0), + }, + { + id: CastStr!(u8)("##sub_panel_6"), + pct: 0.2, + axis: A2D.X, + color: Vec4(0.2, 0.76, 0.5, 1.0), + }, + ]; + + Panel(&panels[0]); + { + Panel(&panels[1]); + EndPanel(); + + Panel(&panels[2]); + EndPanel(); + + Panel(&panels[3]); + EndPanel(); + } + EndPanel(); + + Panel(&panels[4]); + EndPanel(); + + Panel(&panels[5]); + { + Panel(&panels[6]); + { + Panel(&panels[7]); EndPanel(); - Panel("##sub_sub_panel_2", 1.0, 0.6, A2D.X, Vec4(1.0, 1.0, 0.0, 1.0)); + + Panel(&panels[8]); EndPanel(); } EndPanel(); - Panel("##sub_panel_7", 0.55, 1.0, A2D.X, Vec4(0.9, 0.2, 0.3, 1.0)); + Panel(&panels[9]); EndPanel(); - Panel("##sub_panel_8", 0.2, 1.0, A2D.X, Vec4(0.2, 0.76, 0.5, 1.0)); + Panel(&panels[10]); EndPanel(); } EndPanel(); diff --git a/src/editor/ui.d b/src/editor/ui.d index e10f9c1..ab04b8e 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -10,7 +10,7 @@ import std.stdio; import editor; -UIContext g_ui_ctx; +UICtx g_ui_ctx; const UIItem g_ui_nil_item; UIItem* g_UI_NIL; @@ -54,7 +54,7 @@ enum SizeType alias ST = SizeType; -struct UIContext +struct UICtx { HashTable!(UIHash, UIItem*) items; Arena arena; @@ -104,6 +104,7 @@ struct UIItem UIItem* parent; u32 level; + Vec2 dragged; // Build Parameters Axis2D layout_axis; @@ -169,7 +170,7 @@ InitUICtx(Renderer* rd, FontAtlas atlas) MappedBuffer!(Vertex) m_vtx = CreateMappedBuffer!(Vertex)(rd, BT.Vertex, 5000); MappedBuffer!(u32) m_idx = CreateMappedBuffer!(u32)(rd, BT.Index, 15000); - UIContext ctx = { + UICtx ctx = { rd: rd, items: CreateHashTable!(UIHash, UIItem*)(12), arena: CreateArena(MB(4)), @@ -187,12 +188,14 @@ InitUICtx(Renderer* rd, FontAtlas atlas) }; g_ui_ctx = ctx; + + InitWidgets(); } UIItemNode* PopItemNode() { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); UIItemNode* node; if (!CheckNil(g_UI_NIL_NODE, ctx.stack_free_list.first)) @@ -213,14 +216,14 @@ PopItemNode() void PushItemNode(UIItemNode* node) { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); SLLPushFront(&ctx.stack_free_list, node, g_UI_NIL_NODE); } void PushSiblingPanel(UIItem* panel) { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); UIItemNode* node = PopItemNode(); node.item = panel; @@ -231,7 +234,7 @@ PushSiblingPanel(UIItem* panel) UIItem* PopSiblingPanel() { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); UIItem* panel = g_UI_NIL; UIItemNode* node = ctx.prev_sibling; @@ -264,7 +267,7 @@ PeekSiblingPanel() UIItem* PrevSiblingPanel(UIItem* panel) { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); UIItem* result = g_UI_NIL; UIItemNode* prev = ctx.prev_sibling; for(;;) @@ -305,7 +308,7 @@ PeekParent() UIItem* PopParent() { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); UIItemNode* node = ctx.top_parent; UIItem* item = node.item; @@ -320,7 +323,7 @@ PopParent() void PushParent(UIItem* parent) { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); UIItemNode* node = PopItemNode(); node.item = parent; @@ -344,7 +347,7 @@ SetColor(Vec4 col) void SetColor(Vec4 col0, Vec4 col1, Vec4 col2, Vec4 col3) { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); ctx.color[0] = col0; ctx.color[1] = col1; ctx.color[2] = col2; @@ -354,14 +357,14 @@ SetColor(Vec4 col0, Vec4 col1, Vec4 col2, Vec4 col3) void SetLayoutAxis(Axis2D axis) { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); ctx.layout_axis = axis; } void BeginBuild(Inputs* inputs) { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); ctx.inputs = inputs; ctx.buffer.count = 0; @@ -374,7 +377,7 @@ BeginBuild(Inputs* inputs) void EndBuild() { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); PopParent(); PopSiblingPanels(); @@ -390,10 +393,29 @@ EndBuild() } DrawUI(ctx, ctx.root); + static bool first = false; + if (!first) + { + DrawNodes(ctx.root); + first = true; + } + 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); } +void +DrawNodes(UIItem* item) +{ + if (!Nil(item)) + { + Logf("x0 %s x1 %s y0 %s y1 %s", item.rect.x0, item.rect.x1, item.rect.y0, item.rect.y1); + + DrawNodes(item.first); + DrawNodes(item.next); + } +} + void CalcFixedSizes(UIItem* item) { @@ -447,7 +469,7 @@ CalcPositions(alias axis)(UIItem* item, f32 pos = 0.0) } void -DrawUI(UIContext* ctx, UIItem* item) +DrawUI(UICtx* ctx, UIItem* item) { if (!Nil(item)) { @@ -461,7 +483,7 @@ DrawUI(UIContext* ctx, UIItem* item) void BuildItem(UIItem* item, UISize size_x, UISize size_y, UIFlags properties) { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); item.first = item.last = item.next = item.prev = g_UI_NIL; @@ -486,28 +508,53 @@ BuildItem(UIItem* item, UISize size_x, UISize size_y, UIFlags properties) void Signal(UIItem* item) { - UIContext* ctx = GetCtx(); + UICtx* ctx = GetCtx(); item.signal = UIS.None; + item.dragged = 0.0; for(DNode!(InputEvent)* n = ctx.inputs.list.first; !CheckNil(null, n); n = n.next) { + bool taken = false; if (item.flags & UIF.Clickable && Clicked(item, n)) { Logf("Clicked"); - DLLRemove(&ctx.inputs.list, n, null); + item.signal |= UIS.Clicked; + taken = true; } - if (item.flags & UIF.Draggable && Clicked(item, n)) + if (item.flags & UIF.Draggable && Clicked(item, n) && Nil(ctx.drag_item)) { Logf("Dragged"); + item.signal |= UIS.Dragged; + ctx.drag_item = item; + taken = true; + } + + if (ctx.drag_item == item && n.value.key == Input.LeftClick && !n.value.pressed) + { + Logf("Released"); + ctx.drag_item = g_UI_NIL; + taken = true; + } + + if (ctx.drag_item == item && n.value.key == Input.MouseMotion) + { + item.signal |= UIS.Dragged; + item.dragged.x += cast(f32)n.value.rel_x; + item.dragged.y += cast(f32)n.value.rel_y; + taken = true; + } + + if (taken) + { DLLRemove(&ctx.inputs.list, n, null); } } } -UIContext* +UICtx* GetCtx() { return &g_ui_ctx; @@ -653,7 +700,7 @@ DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, Vec4 col = Vec4(1.0)) } pragma(inline) void -DrawRect(UIContext* ctx, UIItem* item) +DrawRect(UICtx* ctx, UIItem* item) { // Y reversed Vertex* v = ctx.buffer.vtx.ptr + ctx.buffer.count; diff --git a/src/editor/widgets.d b/src/editor/widgets.d index f561429..23fb178 100644 --- a/src/editor/widgets.d +++ b/src/editor/widgets.d @@ -3,6 +3,58 @@ import dlib; import ui; import std.format : sformat; +const UIPanel g_ui_nil_panel; +UIPanel* g_UI_NIL_PANEL; + +UIPanel g_root_panel; + +WidgetCtx g_widget_ctx; + +struct WidgetCtx +{ + UIPanel* parent; +} + +struct UIPanel +{ + u8[] id; + + UIPanel* parent; + UIPanel* next; + UIPanel* prev; + UIPanel* first; + UIPanel* last; + + UIPanel* list_next; + + Axis2D axis; + f32 pct; + Vec4 color; +} + +void +PushPanel(UIPanel* parent) +{ + parent.list_next = g_widget_ctx.parent; + g_widget_ctx.parent = parent; +} + +UIPanel* +PopPanel() +{ + UIPanel* parent = g_widget_ctx.parent; + g_widget_ctx.parent = parent.list_next; + parent.list_next = g_UI_NIL_PANEL; + return parent; +} + +void +InitWidgets() +{ + g_UI_NIL_PANEL = cast(UIPanel*)&g_ui_nil_panel; + g_widget_ctx.parent = g_UI_NIL_PANEL; +} + UIItem* Root() { @@ -11,47 +63,81 @@ Root() UIItem* root = Get("##root_item"); BuildItem(root, UISize(ST.Pixels, d.x), UISize(ST.Pixels, d.y), UIF.DrawBackground); + g_root_panel.pct = 1.0; + g_root_panel.axis = A2D.Y; + g_root_panel.color = Vec4(Vec3(0.0), 1.0); + g_root_panel.list_next = g_root_panel.parent = g_root_panel.next = g_root_panel.prev = g_root_panel.first = g_root_panel.last = g_UI_NIL_PANEL; + + g_widget_ctx.parent = &g_root_panel; + return root; } UIItem* -Panel(string id, f32 x_pct, f32 y_pct, Axis2D layout_axis, Vec4 color) +Panel(UIPanel* panel) { - UIItem* item = Get(id); + UIItem* item = Get(panel.id); UIItem* separator = g_UI_NIL; UIItem* prev_panel = PeekSiblingPanel(); UIItem* parent = PeekParent(); + UIPanel* parent_pn = g_widget_ctx.parent; + + DLLPush(parent_pn, panel, g_UI_NIL_PANEL); + + f32 x_pct = 1.0, y_pct = 1.0; + if (parent_pn != g_UI_NIL_PANEL) + { + x_pct = parent_pn.axis == A2D.X ? panel.pct : 1.0; + y_pct = parent_pn.axis == A2D.Y ? panel.pct : 1.0; + } + Signal(item); f32 adj_x = 0.0, adj_y = 0.0; if (!Nil(prev_panel) && parent != prev_panel) { - f32 sep_y = 1.0, sep_x = 1.0; + f32 sep_y = 1.0, sep_x = 1.0, parent_start = 0.0, parent_end = 0.0; if (parent.layout_axis == A2D.X) { sep_x = 5.0; adj_x = -5.0; + parent_start = parent.rect.vec0.x; + parent_end = parent.rect.vec1.x; } else { sep_y = 5.0; adj_y = -5.0; + parent_start = parent.rect.vec0.y; + parent_end = parent.rect.vec1.y; } u8[128] buf = 0; - (cast(char[])buf).sformat("sep_%s", id); + (cast(char[])buf).sformat("sep_%s", cast(char[])panel.id); separator = Get(buf); Separator(separator, sep_x, sep_y, parent.layout_axis); Signal(separator); + + f32 pos = separator.dragged.v[parent.layout_axis]; + if (separator.signal & UIS.Dragged && pos != 0.0) + { + f32 pct = Remap(pos, 0.0, parent_start-parent_end, 0.0, 1.0); + panel.pct -= pct; + panel.prev.pct += pct; + + Logf("%f", pct); + + Logf("%s %f %s %f", cast(char[])panel.id, panel.pct, cast(char[])panel.prev.id, panel.prev.pct); + } } - SetColor(color); - SetLayoutAxis(layout_axis); + SetColor(panel.color); + SetLayoutAxis(panel.axis); BuildItem(item, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground); @@ -60,6 +146,8 @@ Panel(string id, f32 x_pct, f32 y_pct, Axis2D layout_axis, Vec4 color) PushSiblingPanel(item); PushParent(item); + PushPanel(panel); + return item; } @@ -78,4 +166,5 @@ void EndPanel() { PopParent(); + PopPanel(); }