begin work on UI

This commit is contained in:
Matthew 2025-08-28 06:57:25 +10:00
parent 5eca5811fb
commit ef5ea1d90a
3 changed files with 244 additions and 122 deletions

@ -1 +1 @@
Subproject commit 69f5cd1a76ce0ee4f61201a5cae460664a5d8ca9 Subproject commit 6a757dd36b29f553c290d7188e8e02400cd25ba9

View File

@ -6,6 +6,7 @@ import vulkan;
import dlib.math; import dlib.math;
import dlib.alloc; import dlib.alloc;
import buffer; import buffer;
import ui;
import std.stdio; import std.stdio;
import std.exception; import std.exception;
@ -22,6 +23,8 @@ struct Editor
Arena arena; Arena arena;
Arena temp_arena; Arena temp_arena;
PlatformWindow* window; PlatformWindow* window;
UIContext ctx;
Renderer rd; Renderer rd;
ImageView font_atlas; ImageView font_atlas;
@ -32,9 +35,6 @@ struct Editor
PushConst pc; PushConst pc;
MappedBuffer!(Vertex) m_vertex_buf; MappedBuffer!(Vertex) m_vertex_buf;
MappedBuffer!(u32) m_index_buf; MappedBuffer!(u32) m_index_buf;
Vertex[] vertices;
u32[] indices;
u32 ui_count;
FlatBuffer[] buffers; FlatBuffer[] buffers;
Tokenizer[] tokenizers; Tokenizer[] tokenizers;
@ -50,6 +50,13 @@ struct Editor
UVec2 res; UVec2 res;
} }
struct UIBuffer
{
Vertex[] vtx;
u32[] idx;
u32 count;
}
struct PushConst struct PushConst
{ {
Mat4 projection; Mat4 projection;
@ -72,19 +79,17 @@ struct Vertex
void void
Cycle(Editor* ed) Cycle(Editor* ed)
{ {
ed.ui_count = 0;
Reset(&ed.temp_arena); Reset(&ed.temp_arena);
Reset(&ed.ctx);
//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, 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));
//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));
Vec4 col = Vec4(0.2, 0.5, 0.9, 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 col2 = Vec4(0.2, 0.4, 0.7, 1.0);
Vec4 col3 = Vec4(0.2, 0.3, 0.65, 1.0); Vec4 col3 = Vec4(0.2, 0.3, 0.65, 1.0);
Vec4 black = Vec4(0.0, 0.0, 0.0, 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.ctx, -1000.0, -1000.0, 3000.0, 3000.0, 0.0, 0.0, 0.0, 0.0, col);
//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, 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 pos = 0.0;
f32 h = ed.atlas_buf.atlas.size; 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); 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); 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_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.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); 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); 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 void
DrawBuffer(Editor* ed, f32 x, f32 y, f32 px, FlatBuffer* fb) 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; g.atlas_left = g.atlas_right = 0.0;
foreach(j; 0 .. tab_count) 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') else if (ch == '\n')
{ {
g.atlas_left = g.atlas_right = 0.0; 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; y_pos += px;
x_pos = 0.0; x_pos = 0.0;
} }
else 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;
}

184
src/editor/ui.d Normal file
View File

@ -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;
}