From ef5ea1d90a6b95deec6e00bc975150b013d2e51a Mon Sep 17 00:00:00 2001 From: Matthew Date: Thu, 28 Aug 2025 06:57:25 +1000 Subject: [PATCH] begin work on UI --- src/dlib | 2 +- src/editor/editor.d | 180 ++++++++++++++----------------------------- src/editor/ui.d | 184 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 122 deletions(-) create mode 100644 src/editor/ui.d diff --git a/src/dlib b/src/dlib index 69f5cd1..6a757dd 160000 --- a/src/dlib +++ b/src/dlib @@ -1 +1 @@ -Subproject commit 69f5cd1a76ce0ee4f61201a5cae460664a5d8ca9 +Subproject commit 6a757dd36b29f553c290d7188e8e02400cd25ba9 diff --git a/src/editor/editor.d b/src/editor/editor.d index 7f53c7e..d51f3e1 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -6,6 +6,7 @@ import vulkan; import dlib.math; import dlib.alloc; import buffer; +import ui; import std.stdio; import std.exception; @@ -22,6 +23,8 @@ struct Editor Arena arena; Arena temp_arena; PlatformWindow* window; + + UIContext ctx; Renderer rd; ImageView font_atlas; @@ -32,9 +35,6 @@ struct Editor PushConst pc; MappedBuffer!(Vertex) m_vertex_buf; MappedBuffer!(u32) m_index_buf; - Vertex[] vertices; - u32[] indices; - u32 ui_count; FlatBuffer[] buffers; Tokenizer[] tokenizers; @@ -50,6 +50,13 @@ struct Editor UVec2 res; } +struct UIBuffer +{ + Vertex[] vtx; + u32[] idx; + u32 count; +} + struct PushConst { Mat4 projection; @@ -72,19 +79,17 @@ struct Vertex void Cycle(Editor* ed) { - ed.ui_count = 0; - Reset(&ed.temp_arena); + Reset(&ed.ctx); - - //DrawRect(ed, 200.0, 200.0, 300.0, 300.0, 0.0, 0.0, 0.0, Vec4(0.2, 0.4, 0.8, 1.0)); - //DrawRect(ed, 330.0, 330.0, 430.0, 430.0, 0.0, 0.0, 0.0, Vec4(0.2, 0.4, 0.8, 1.0)); + //DrawRect(&ed.ctx, 200.0, 200.0, 300.0, 300.0, 0.0, 0.0, 0.0, Vec4(0.2, 0.4, 0.8, 1.0)); + //DrawRect(&ed.ctx, 330.0, 330.0, 430.0, 430.0, 0.0, 0.0, 0.0, Vec4(0.2, 0.4, 0.8, 1.0)); Vec4 col = Vec4(0.2, 0.5, 0.9, 1.0); Vec4 col2 = Vec4(0.2, 0.4, 0.7, 1.0); Vec4 col3 = Vec4(0.2, 0.3, 0.65, 1.0); Vec4 black = Vec4(0.0, 0.0, 0.0, 1.0); - DrawRect(ed, -1000.0, -1000.0, 3000.0, 3000.0, 0.0, 0.0, 0.0, 0.0, [black, black, black, black]); - //DrawRect(ed, 450.0, 450.0, 550.0, 550.0, 2.0, 5.0, 0.2, 20.0, [black, black, black, black]); + DrawRect(&ed.ctx, -1000.0, -1000.0, 3000.0, 3000.0, 0.0, 0.0, 0.0, 0.0, col); + //DrawRect(&ed.ctx, 450.0, 450.0, 550.0, 550.0, 2.0, 5.0, 0.2, 20.0, [black, black, black, black]); f32 pos = 0.0; f32 h = ed.atlas_buf.atlas.size; @@ -107,7 +112,7 @@ Cycle(Editor* ed) BindBuffers(&ed.rd, &ed.m_index_buf, &ed.m_vertex_buf); - DrawIndexed(&ed.rd, 6, ed.ui_count, 0); + DrawIndexed(&ed.rd, 6, ed.ctx.buffer.count, 0); FinishRendering(&ed.rd); @@ -228,8 +233,8 @@ CreateEditor(PlatformWindow* window, u8[] buffer_data, u8[] buffer_name) editor.m_vertex_buf = CreateMappedBuffer!(Vertex)(&editor.rd, BT.Vertex, Vertex.sizeof * UI_COUNT); editor.m_index_buf = CreateMappedBuffer!(u32)(&editor.rd, BT.Index, u32.sizeof * UI_COUNT); - editor.vertices = editor.m_vertex_buf.data; - editor.indices = editor.m_index_buf.data; + + editor.ctx = CreateUIContext(editor.m_vertex_buf.data, editor.m_index_buf.data); CreateImageView(&editor.rd, &editor.font_atlas, editor.atlas_buf.atlas.width, editor.atlas_buf.atlas.height, 4, editor.atlas_buf.data); @@ -249,6 +254,44 @@ DrawText(Editor* ed, f32 x, f32 y, f32 px, string str) DrawText(ed, x, y, px, str_buf); } +void +DrawText(Editor* ed, f32 x, f32 y, f32 px, u8[] str) +{ + u32 tab_count = 2; + f32 x_pos = x; + f32 scale = px / ed.atlas_buf.atlas.size; + foreach(ch; str) + { + foreach(glyph; ed.atlas_buf.atlas.glyphs) + { + if (ch == glyph.ch) + { + if (ch == '\t') + { + Glyph g = glyph; + g.atlas_left = g.atlas_right = 0.0; + foreach(i; 0 .. tab_count) + { + DrawGlyph(&ed.ctx, &g, scale, &x_pos, y); + } + } + else if (ch == '\n') + { + Glyph g = glyph; + g.atlas_left = g.atlas_right = 0.0; + DrawGlyph(&ed.ctx, &g, scale, &x_pos, y); + } + else + { + DrawGlyph(&ed.ctx, &glyph, scale, &x_pos, y); + } + + break; + } + } + } +} + void DrawBuffer(Editor* ed, f32 x, f32 y, f32 px, FlatBuffer* fb) { @@ -288,126 +331,21 @@ DrawBuffer(Editor* ed, f32 x, f32 y, f32 px, FlatBuffer* fb) g.atlas_left = g.atlas_right = 0.0; foreach(j; 0 .. tab_count) { - DrawGlyph(ed, g, scale, &x_pos, y_pos); + DrawGlyph(&ed.ctx, g, scale, &x_pos, y_pos); } } else if (ch == '\n') { g.atlas_left = g.atlas_right = 0.0; - DrawGlyph(ed, g, scale, &x_pos, y_pos); + DrawGlyph(&ed.ctx, g, scale, &x_pos, y_pos); y_pos += px; x_pos = 0.0; } else { - DrawGlyph(ed, g, scale, &x_pos, y_pos, cols[fb.tk.buffer[i]]); + DrawGlyph(&ed.ctx, g, scale, &x_pos, y_pos, cols[fb.tk.buffer[i]]); } } } } } - -void -DrawText(Editor* ed, f32 x, f32 y, f32 px, u8[] str) -{ - u32 tab_count = 2; - f32 x_pos = x; - f32 scale = px / ed.atlas_buf.atlas.size; - foreach(ch; str) - { - foreach(glyph; ed.atlas_buf.atlas.glyphs) - { - if (ch == glyph.ch) - { - if (ch == '\t') - { - Glyph g = glyph; - g.atlas_left = g.atlas_right = 0.0; - foreach(i; 0 .. tab_count) - { - DrawGlyph(ed, &g, scale, &x_pos, y); - } - } - else if (ch == '\n') - { - Glyph g = glyph; - g.atlas_left = g.atlas_right = 0.0; - DrawGlyph(ed, &g, scale, &x_pos, y); - } - else - { - DrawGlyph(ed, &glyph, scale, &x_pos, y); - } - - break; - } - } - } -} - -pragma(inline): void -DrawGlyph(Editor* ed, Glyph* glyph, f32 scale, f32* x_pos, f32 y, Vec4 col = Vec4(1.0)) -{ - if (glyph.atlas_left != glyph.atlas_right) - { - Vertex* v = ed.vertices.ptr + ed.ui_count; - - f32 r = glyph.plane_right * scale; - f32 l = glyph.plane_left * scale; - f32 w = r - l; - f32 h = (glyph.plane_bottom - glyph.plane_top) * scale; - f32 y_pos = glyph.plane_top * scale; - - v.dst_start.x = *x_pos + l; - v.dst_start.y = y - y_pos; - v.dst_end.x = *x_pos + w + l; - v.dst_end.y = y + h - y_pos; - - v.src_start.x = glyph.atlas_left; - v.src_start.y = glyph.atlas_top; - v.src_end.x = glyph.atlas_right; - v.src_end.y = glyph.atlas_bottom; - - v.cols[0] = col; - v.cols[1] = col; - v.cols[2] = col; - v.cols[3] = col; - - v.texture = 1; - - AddUIIndices(ed); - } - - *x_pos += glyph.advance * scale; -} - -pragma(inline) void -DrawRect(Editor* ed, f32 p0_x, f32 p0_y, f32 p1_x, f32 p1_y, f32 border, f32 corner, f32 softness, f32 raised, Vec4[4] cols) -{ - // Y reversed - Vertex* v = ed.vertices.ptr + ed.ui_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; - - AddUIIndices(ed); -} - -void -AddUIIndices(Editor* ed) -{ - ed.indices[0] = 0; - ed.indices[1] = 1; - ed.indices[2] = 2; - ed.indices[3] = 2; - ed.indices[4] = 1; - ed.indices[5] = 3; - - ed.ui_count += 1; -} diff --git a/src/editor/ui.d b/src/editor/ui.d new file mode 100644 index 0000000..d2934d4 --- /dev/null +++ b/src/editor/ui.d @@ -0,0 +1,184 @@ +import dlib.aliases; +import dlib.math; +import dlib.util; +import dlib.alloc; +import dlib.fonts; +import vulkan; + +import editor; + +enum BaseFlags : u32 +{ + Clickable = (1<<0), + ViewScroll = (1<<1), + DrawText = (1<<2), + DrawBackground = (1<<3), + DrawBorder = (1<<4), + Resizeable = (1<<5), + Draggable = (1<<6), +} + +union Rect +{ + struct + { + Vec2 vec0; + Vec2 vec1; + }; + struct + { + f32 x0; + f32 y0; + f32 x1; + f32 y1; + }; +} + +alias UIHash = u64; + +alias UIPair = KVPair!(UIHash, UIItem*); + +struct UIItem +{ + BaseFlags flags; + Rect rect; +} + +struct UIContext +{ + HashTable!(UIHash, UIItem*) items; + Arena arena; + + UIBuffer buffer; + + Vec4[4] bg_cols; + Vec4[4] border_color; +} + +void +Reset(UIContext* ctx) +{ + ctx.buffer.count = 0; +} + +UIContext +CreateUIContext(Vertex[] vtx, u32[] idx) +{ + UIContext ctx = { + items: CreateHashTable!(UIHash, UIItem*)(16), + arena: CreateArena(MB(8)), + buffer: { + vtx: vtx, + idx: idx, + }, + }; + + return ctx; +} + +void +SetBgCols(UIContext* ctx, Vec4[4] cols) +{ + ctx.bg_cols = cols; +} + +void +SetBorderCols(UIContext* ctx, Vec4[4] cols) +{ + ctx.border_color = cols; +} + +UIItem* +Find(UIContext* ctx, u8[] id) +{ + u64 hash = Hash(id); + Result!(UIItem*) result = ctx.items[hash]; + if (!result.ok) + { + result.value = Alloc!(UIItem)(&ctx.arena); + Push(&ctx.items, hash, result.value); + } + + return result.value; +} + +bool +UIWindow(UIContext* ctx, Rect rect, f32 border_px, u8[] text) +{ + UIItem* item = Find(ctx, text); + + return false; +} + +pragma(inline): void +DrawGlyph(UIContext* ctx, Glyph* glyph, f32 scale, f32* x_pos, f32 y, Vec4 col = Vec4(1.0)) +{ + if (glyph.atlas_left != glyph.atlas_right) + { + Vertex* v = ctx.buffer.vtx.ptr + ctx.buffer.count; + + f32 r = glyph.plane_right * scale; + f32 l = glyph.plane_left * scale; + f32 w = r - l; + f32 h = (glyph.plane_bottom - glyph.plane_top) * scale; + f32 y_pos = glyph.plane_top * scale; + + v.dst_start.x = *x_pos + l; + v.dst_start.y = y - y_pos; + v.dst_end.x = *x_pos + w + l; + v.dst_end.y = y + h - y_pos; + + v.src_start.x = glyph.atlas_left; + v.src_start.y = glyph.atlas_top; + v.src_end.x = glyph.atlas_right; + v.src_end.y = glyph.atlas_bottom; + + v.cols[0] = col; + v.cols[1] = col; + v.cols[2] = col; + v.cols[3] = col; + + v.texture = 1; + + AddUIIndices(ctx); + } + + *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 col) +{ + DrawRect(ctx, p0_x, p0_y, p1_x, p1_y, border, corner, softness, raised, [col, col, col, col]); +} + +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) +{ + // Y reversed + Vertex* v = ctx.buffer.vtx.ptr + 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; + + AddUIIndices(ctx); +} + +void +AddUIIndices(UIContext* ctx) +{ + ctx.buffer.idx[0] = 0; + ctx.buffer.idx[1] = 1; + ctx.buffer.idx[2] = 2; + ctx.buffer.idx[3] = 2; + ctx.buffer.idx[4] = 1; + ctx.buffer.idx[5] = 3; + + ctx.buffer.count += 1; +}