restructure

This commit is contained in:
Matthew 2025-09-14 17:33:02 +10:00
parent d96273ab5b
commit 439aa2b526
6 changed files with 377 additions and 346 deletions

@ -1 +1 @@
Subproject commit 5c2a3ab5efa1aaa15ca6f3144655550dd024823a Subproject commit 65ac576cb0f37e797030365ef25d11a771468654

View File

@ -108,7 +108,7 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, Range pos)
// TODO: handle case for when lines are longer than line buffer // TODO: handle case for when lines are longer than line buffer
void void
GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length, bool add_hash = false) GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length)
{ {
assert(linebufs != null, "GetLines failure: linebufs is null"); assert(linebufs != null, "GetLines failure: linebufs is null");
length = length > buffer.line_count ? buffer.line_count : length; length = length > buffer.line_count ? buffer.line_count : length;
@ -116,7 +116,6 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length,
Reset(&linebufs.arena); Reset(&linebufs.arena);
linebufs.lines = AllocArray!(u8[])(&linebufs.arena, length); linebufs.lines = AllocArray!(u8[])(&linebufs.arena, length);
u64 extra_buffer = add_hash ? 10 : 0;
i64 start = -1; i64 start = -1;
u64 line = 0; u64 line = 0;
u64 current_line = 0; u64 current_line = 0;
@ -136,7 +135,7 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length,
if (start < 0 && new_line) if (start < 0 && new_line)
{ {
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, 1 + extra_buffer); linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, 1);
linebufs.lines[current_line][0] = '\n'; linebufs.lines[current_line][0] = '\n';
current_line += 1; current_line += 1;
continue; continue;
@ -150,7 +149,7 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length,
if (new_line) if (new_line)
{ {
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (i-start) + extra_buffer); linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (i-start));
linebufs.lines[current_line][0 .. i-start] = buffer.data[start .. i]; linebufs.lines[current_line][0 .. i-start] = buffer.data[start .. i];
current_line += 1; current_line += 1;
start = -1; start = -1;
@ -159,23 +158,10 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length,
if (i == buffer.length-1) if (i == buffer.length-1)
{ {
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (buffer.length-start) + extra_buffer); linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (buffer.length-start));
linebufs.lines[current_line][0 .. buffer.length-start] = buffer.data[start .. buffer.length]; linebufs.lines[current_line][0 .. buffer.length-start] = buffer.data[start .. buffer.length];
} }
} }
if (add_hash)
{
for (u64 i = 0; i < linebufs.lines.length; i += 1)
{
if (linebufs.lines[i].length > 0)
{
u8[10] buf = 0;
(cast(char[])buf).sformat("##%08s", i);
linebufs.lines[i][linebufs.lines[i].length - extra_buffer .. $] = buf[0 .. $];
}
}
}
} }
u64 u64

View File

@ -13,289 +13,58 @@ import widgets;
import std.stdio; import std.stdio;
import std.exception; import std.exception;
//const u8[] FONT_BYTES = import("Roboto.ttf");
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 = 5000;
f32 g_delta = 0.0; f32 g_delta = 0.0;
Editor* g_editor;
EditorCtx g_ed_ctx;
struct EditorCtx
{
Arena arena;
UIPanel* base_panel;
}
struct Editor struct Editor
{ {
Arena arena; Arena arena;
Arena temp_arena;
PlatformWindow* window;
Renderer rd; u8[] filename;
ImageView font_atlas; FlatBuffer buf;
Pipeline pipeline; Tokenizer tk;
DescSetLayout desc_set_layout; LineBuffers linebufs;
DescSet desc_set;
PipelineLayout pipeline_layout;
PushConst pc;
FlatBuffer[] buffers; Vec2 cursor_pos;
Tokenizer[] tokenizers; Vec2 select_start;
u8[][] buffer_names; Vec2 select_end;
u32 buffer_count;
FlatBuffer* active_buffer;
LineBuffers linebufs;
u8[] font_data; f32 text_size;
FontFace font; u64 line_offset;
FontAtlasBuf atlas_buf;
Vec2 res;
}
struct PushConst
{
Mat4 projection;
}
struct Vertex
{
Vec4[4] cols;
Vec2 dst_start;
Vec2 dst_end;
Vec2 src_start;
Vec2 src_end;
f32 border_thickness;
f32 corner_radius;
f32 edge_softness;
f32 raised;
u32 texture;
} }
void void
Cycle(Editor* ed, Inputs* inputs) Cycle(Inputs* inputs)
{ {
Reset(&ed.temp_arena);
ResetScratch(MB(4)); ResetScratch(MB(4));
BeginBuild(inputs); BeginBuild(inputs);
static UIPanel panel = {
id: CastStr!(u8)("##main_panel"),
pct: 1.0,
axis: A2D.X,
color: Vec4(0.2, 0.4, 0.8, 1.0),
};
static UIPanel panel_l = {
id: CastStr!(u8)("##panel_l"),
pct: 0.5,
axis: A2D.Y,
color: Vec4(0.2, 0.4, 0.8, 1.0),
};
static UIPanel panel_r = {
id: CastStr!(u8)("##panel_r"),
pct: 0.5,
axis: A2D.Y,
color: Vec4(0.4, 0.3, 0.7, 1.0),
};
BeginFrame(&ed.rd);
Vec2 ext = GetExtent(&ed.rd);
if (ext != ed.res)
{
ed.res = ext;
Ortho(&ed.pc.projection, 0.0, 0.0, ext.x, ext.y, 1000.0, 0.1);
}
u32 rows = cast(u32)(ext.y / ed.atlas_buf.atlas.size) + 5;
Panel(&panel);
{
Panel(&panel_l);
{
if (ed.active_buffer != null)
{
GetLines(&ed.buffers[0], &ed.linebufs, 0, rows);
for(u64 i = 0; i < ed.linebufs.lines.length; i += 1)
{
TextLine(ed.linebufs.lines[i], ed.atlas_buf.atlas.size, i);
}
}
}
EndPanel();
Panel(&panel_r);
{
}
EndPanel();
}
EndPanel();
BeginRendering(&ed.rd);
PushConstants(&ed.rd, ed.pipeline, &ed.pc);
Bind(&ed.rd, ed.pipeline, ed.desc_set);
EndBuild(); EndBuild();
FinishRendering(&ed.rd);
SubmitAndPresent(&ed.rd);
} }
Editor void
CreateEditor(PlatformWindow* window, u8[] buffer_data, u8[] buffer_name) InitEditorCtx(PlatformWindow* window)
{ {
InitFreeType(); InitFreeType();
version(linux)
{
PlatformHandles handles = {
window: window.window,
conn: window.conn,
};
}
version(Windows)
{
}
Arena arena = CreateArena(MB(32));
FontFace font = OpenFont(cast(u8[])FONT_BYTES);
FontAtlasBuf atlas_buf = CreateAtlas(&arena, font, 16.0, 256);
Editor editor = { InitUICtx(window);
window: window,
arena: arena, EditorCtx ctx = {
temp_arena: CreateArena(MB(8)), arena: CreateArena(MB(2)),
rd: InitRenderer(handles, MB(32), MB(16)),
font_data: cast(u8[])FONT_BYTES,
font: font,
atlas_buf: atlas_buf,
buffers: MAllocArray!(FlatBuffer)(32),
buffer_names: MAllocArray!(u8[])(32),
linebufs: CreateLineBuffers(MB(32)),
}; };
if (buffer_data != null) g_ed_ctx = ctx;
{
assert(buffer_name != null, "CreateEditor failure: buffer_name is null while buffer_data is not");
editor.buffers[0] = CreateFlatBuffer(buffer_data);
editor.buffer_names[0] = AllocArray!(u8)(&editor.arena, buffer_name.length);
editor.buffer_names[0][0 .. $] = buffer_name[0 .. $];
editor.buffer_count += 1;
editor.active_buffer = &editor.buffers[0];
}
editor.active_buffer = &editor.buffers[0];
DescLayoutBinding[2] layout_bindings = [
{ binding: 0, descriptorType: DT.Image, descriptorCount: 1, stageFlags: SS.All },
{ binding: 1, descriptorType: DT.Storage, descriptorCount: 1, stageFlags: SS.All },
];
editor.desc_set_layout = CreateDescSetLayout(&editor.rd, layout_bindings);
editor.desc_set = AllocDescSet(&editor.rd, editor.desc_set_layout);
editor.pipeline_layout = CreatePipelineLayout(&editor.rd, editor.desc_set_layout, PushConst.sizeof);
Attribute[13] attributes = [
{ binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 0},
{ binding: 0, location: 1, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 1},
{ binding: 0, location: 2, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 2},
{ binding: 0, location: 3, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 3},
{ binding: 0, location: 4, format: FMT.RG_F32, offset: Vertex.dst_start.offsetof },
{ binding: 0, location: 5, format: FMT.RG_F32, offset: Vertex.dst_end.offsetof },
{ binding: 0, location: 6, format: FMT.RG_F32, offset: Vertex.src_start.offsetof },
{ binding: 0, location: 7, format: FMT.RG_F32, offset: Vertex.src_end.offsetof },
{ binding: 0, location: 8, format: FMT.R_F32, offset: Vertex.border_thickness.offsetof },
{ binding: 0, location: 9, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof },
{ binding: 0, location: 10, format: FMT.R_F32, offset: Vertex.edge_softness.offsetof },
{ binding: 0, location: 11, format: FMT.R_F32, offset: Vertex.raised.offsetof },
{ binding: 0, location: 12, format: FMT.R_U32, offset: Vertex.texture.offsetof },
];
GfxPipelineInfo ui_info = {
vertex_shader: cast(u8[])VERTEX_BYTES,
frag_shader: cast(u8[])FRAGMENT_BYTES,
input_rate: IR.Instance,
input_rate_stride: Vertex.sizeof,
layout: editor.pipeline_layout,
vertex_attributes: attributes,
src_color: BF.SrcAlpha,
dst_color: BF.OneMinusSrcAlpha,
color_op: BO.Add,
src_alpha: BF.One,
dst_alpha: BF.Zero,
alpha_op: BO.Add,
};
assert(CreateGraphicsPipeline(&editor.rd, &editor.pipeline, &ui_info), "Unable to build UI pipeline");
InitUICtx(&editor.rd, editor.atlas_buf.atlas);
CreateImageView(&editor.rd, &editor.font_atlas, editor.atlas_buf.atlas.width, editor.atlas_buf.atlas.height, 4, editor.atlas_buf.data);
Write(&editor.rd, editor.desc_set, &editor.font_atlas, 0, DT.Image);
SetClearColors(&editor.rd, [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0]);
Reset(&editor.temp_arena);
return editor;
}
void
DrawText(Editor* ed, f32 x, f32 y, f32 px, string str)
{
u8[] str_buf = (cast(u8*)str.ptr)[0 .. str.length];
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(&g, scale, &x_pos, y);
}
}
else if (ch == '\n')
{
Glyph g = glyph;
g.atlas_left = g.atlas_right = 0.0;
DrawGlyph(&g, scale, &x_pos, y);
}
else
{
DrawGlyph(&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)
{ {
@ -347,9 +116,10 @@ DrawBuffer(Editor* ed, f32 x, f32 y, f32 px, FlatBuffer* fb)
} }
else else
{ {
DrawGlyph(g, scale, &x_pos, y_pos, cols[fb.tk.buffer[i]]); DrawGlyph(g, scale, &x_pos, y_pos, false, cols[fb.tk.buffer[i]]);
} }
} }
} }
} }
} }
*/

View File

@ -21,7 +21,7 @@ void main(string[] argv)
PlatformWindow window = CreateWindow("Editor", 1920, 1080); PlatformWindow window = CreateWindow("Editor", 1920, 1080);
StartPlatformThread(&window); StartPlatformThread(&window);
Editor editor = CreateEditor(&window, buffer, buffer_name); InitEditorCtx(&window);
while (true) while (true)
{ {
@ -31,6 +31,6 @@ void main(string[] argv)
break; break;
} }
Cycle(&editor, inputs); Cycle(inputs);
} }
} }

View File

@ -13,6 +13,12 @@ import core.stdc.string : memset;
import editor; import editor;
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 = 5000;
UICtx g_ui_ctx; UICtx g_ui_ctx;
const UIItem g_ui_nil_item; const UIItem g_ui_nil_item;
@ -35,8 +41,9 @@ enum UIFlags
None = 0x00, None = 0x00,
DrawBackground = 0x01, DrawBackground = 0x01,
DrawText = 0x02, DrawText = 0x02,
Clickable = 0x04, DrawBorder = 0x04,
Draggable = 0x08, Clickable = 0x08,
Draggable = 0x10,
} }
alias UIF = UIFlags; alias UIF = UIFlags;
@ -62,11 +69,24 @@ struct UICtx
{ {
HashTable!(UIHash, UIItem*) items; HashTable!(UIHash, UIItem*) items;
Arena arena; Arena arena;
Renderer* rd;
Inputs* inputs; Inputs* inputs;
u64 frame; u64 frame;
u64 f_idx; u64 f_idx;
PlatformWindow* window;
Renderer rd;
ImageView font_atlas;
Pipeline pipeline;
DescSetLayout desc_set_layout;
DescSet desc_set;
PipelineLayout pipeline_layout;
PushConst pc;
Vec2 res;
u8[] font_data;
FontFace font;
FontAtlasBuf atlas_buf;
UIBuffer[FRAME_OVERLAP] buffers; UIBuffer[FRAME_OVERLAP] buffers;
UIItem* root; UIItem* root;
@ -85,6 +105,9 @@ struct UICtx
Vec2 adjustment; Vec2 adjustment;
u32 tab_width; u32 tab_width;
f32 text_size; f32 text_size;
f32 border_thickness;
f32 corner_radius;
f32 edge_softness;
} }
struct UIItemStackList struct UIItemStackList
@ -123,6 +146,11 @@ struct UIItem
Vec2 size; Vec2 size;
Rect rect; Rect rect;
Vec4[4] color; Vec4[4] color;
Vec4[4] border_color;
f32 border_thickness;
f32 corner_radius;
f32 edge_softness;
Vec2 last_click_pos;
} }
struct UISize struct UISize
@ -140,6 +168,25 @@ struct UIBuffer
u32 count; u32 count;
} }
struct PushConst
{
Mat4 projection;
}
struct Vertex
{
Vec4[4] cols;
Vec2 dst_start;
Vec2 dst_end;
Vec2 src_start;
Vec2 src_end;
f32 border_thickness;
f32 corner_radius;
f32 edge_softness;
f32 raised;
u32 texture;
}
union Rect union Rect
{ {
Vec2[2] v; Vec2[2] v;
@ -168,8 +215,21 @@ struct UIKey
} }
void void
InitUICtx(Renderer* rd, FontAtlas atlas) InitUICtx(PlatformWindow* window)
{ {
version(linux)
{
PlatformHandles handles = {
window: window.window,
conn: window.conn,
};
}
version(Windows)
{
}
g_UI_NIL = cast(UIItem*)&g_ui_nil_item; g_UI_NIL = cast(UIItem*)&g_ui_nil_item;
g_UI_NIL_NODE = cast(UIItemNode*)&g_ui_nil_item_node; g_UI_NIL_NODE = cast(UIItemNode*)&g_ui_nil_item_node;
@ -177,29 +237,88 @@ InitUICtx(Renderer* rd, FontAtlas atlas)
UIBuffer[FRAME_OVERLAP] buffers; UIBuffer[FRAME_OVERLAP] buffers;
u64 vertex_size = 10000; FontFace font = OpenFont(cast(u8[])FONT_BYTES);
for(u64 i = 0; i < FRAME_OVERLAP; i += 1) FontAtlasBuf atlas_buf = CreateAtlas(&arena, font, 16.0, 256);
{
buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(rd, BT.Vertex, vertex_size); Vec4 b = Vec4(Vec3(0.0), 1.0);
buffers[i].m_idx = CreateMappedBuffer!(u32)(rd, BT.Index, cast(u64)(ceil(vertex_size * 1.5)));
buffers[i].vtx = buffers[i].m_vtx.data;
buffers[i].idx = buffers[i].m_idx.data;
}
UICtx ctx = { UICtx ctx = {
rd: rd, rd: InitRenderer(handles, MB(16), MB(8)),
items: CreateHashTable!(UIHash, UIItem*)(12), items: CreateHashTable!(UIHash, UIItem*)(12),
arena: CreateArena(MB(4)), arena: arena,
atlas: atlas,
root: g_UI_NIL, root: g_UI_NIL,
top_parent: g_UI_NIL_NODE, top_parent: g_UI_NIL_NODE,
prev_sibling: g_UI_NIL_NODE, prev_sibling: g_UI_NIL_NODE,
drag_item: g_UI_NIL, drag_item: g_UI_NIL,
text_size: 14.0, text_size: 14.0,
tab_width: 2, tab_width: 2,
buffers: buffers, border_thickness: 0.0,
corner_radius: 0.0,
edge_softness: 0.0,
color: [b, b, b, b],
border_color: [b, b, b, b],
atlas_buf: atlas_buf,
font: font,
font_data: cast(u8[])FONT_BYTES,
}; };
u64 vertex_size = 10000;
for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
{
ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(&ctx.rd, BT.Vertex, vertex_size);
ctx.buffers[i].m_idx = CreateMappedBuffer!(u32)(&ctx.rd, BT.Index, cast(u64)(ceil(vertex_size * 1.5)));
ctx.buffers[i].vtx = ctx.buffers[i].m_vtx.data;
ctx.buffers[i].idx = ctx.buffers[i].m_idx.data;
}
DescLayoutBinding[2] layout_bindings = [
{ binding: 0, descriptorType: DT.Image, descriptorCount: 1, stageFlags: SS.All },
{ binding: 1, descriptorType: DT.Storage, descriptorCount: 1, stageFlags: SS.All },
];
ctx.desc_set_layout = CreateDescSetLayout(&ctx.rd, layout_bindings);
ctx.desc_set = AllocDescSet(&ctx.rd, ctx.desc_set_layout);
ctx.pipeline_layout = CreatePipelineLayout(&ctx.rd, ctx.desc_set_layout, PushConst.sizeof);
Attribute[13] attributes = [
{ binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 0},
{ binding: 0, location: 1, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 1},
{ binding: 0, location: 2, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 2},
{ binding: 0, location: 3, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 3},
{ binding: 0, location: 4, format: FMT.RG_F32, offset: Vertex.dst_start.offsetof },
{ binding: 0, location: 5, format: FMT.RG_F32, offset: Vertex.dst_end.offsetof },
{ binding: 0, location: 6, format: FMT.RG_F32, offset: Vertex.src_start.offsetof },
{ binding: 0, location: 7, format: FMT.RG_F32, offset: Vertex.src_end.offsetof },
{ binding: 0, location: 8, format: FMT.R_F32, offset: Vertex.border_thickness.offsetof },
{ binding: 0, location: 9, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof },
{ binding: 0, location: 10, format: FMT.R_F32, offset: Vertex.edge_softness.offsetof },
{ binding: 0, location: 11, format: FMT.R_F32, offset: Vertex.raised.offsetof },
{ binding: 0, location: 12, format: FMT.R_U32, offset: Vertex.texture.offsetof },
];
GfxPipelineInfo ui_info = {
vertex_shader: cast(u8[])VERTEX_BYTES,
frag_shader: cast(u8[])FRAGMENT_BYTES,
input_rate: IR.Instance,
input_rate_stride: Vertex.sizeof,
layout: ctx.pipeline_layout,
vertex_attributes: attributes,
src_color: BF.SrcAlpha,
dst_color: BF.OneMinusSrcAlpha,
color_op: BO.Add,
src_alpha: BF.One,
dst_alpha: BF.Zero,
alpha_op: BO.Add,
};
assert(CreateGraphicsPipeline(&ctx.rd, &ctx.pipeline, &ui_info), "Unable to build UI pipeline");
CreateImageView(&ctx.rd, &ctx.font_atlas, ctx.atlas_buf.atlas.width, ctx.atlas_buf.atlas.height, 4, ctx.atlas_buf.data);
Write(&ctx.rd, ctx.desc_set, &ctx.font_atlas, 0, DT.Image);
SetClearColors(&ctx.rd, [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0]);
g_ui_ctx = ctx; g_ui_ctx = ctx;
InitWidgets(); InitWidgets();
@ -351,6 +470,24 @@ SetAdjustment(Vec2 adj)
g_ui_ctx.adjustment = adj; g_ui_ctx.adjustment = adj;
} }
void
SetBorderThickness(f32 value)
{
g_ui_ctx.border_thickness = value;
}
void
SetCornerRadius(f32 value)
{
g_ui_ctx.corner_radius = value;
}
void
SetEdgeSoftness(f32 value)
{
g_ui_ctx.edge_softness = value;
}
void void
SetColor(Vec4 col) SetColor(Vec4 col)
{ {
@ -367,6 +504,22 @@ SetColor(Vec4 col0, Vec4 col1, Vec4 col2, Vec4 col3)
ctx.color[3] = col3; ctx.color[3] = col3;
} }
void
SetBorderColor(Vec4 col)
{
SetBorderColor(col, col, col, col);
}
void
SetBorderColor(Vec4 col0, Vec4 col1, Vec4 col2, Vec4 col3)
{
UICtx* ctx = GetCtx();
ctx.border_color[0] = col0;
ctx.border_color[1] = col1;
ctx.border_color[2] = col2;
ctx.border_color[3] = col3;
}
void void
SetLayoutAxis(Axis2D axis) SetLayoutAxis(Axis2D axis)
{ {
@ -418,28 +571,61 @@ EndBuild()
} }
DrawUI(ctx, ctx.root); DrawUI(ctx, ctx.root);
static bool first = false; PrepRendering(ctx);
if (!first)
{
DrawNodes(ctx.root);
first = true;
}
BindBuffers(ctx.rd, &ctx.buffers[ctx.f_idx].m_idx, &ctx.buffers[ctx.f_idx].m_vtx); BindBuffers(&ctx.rd, &ctx.buffers[ctx.f_idx].m_idx, &ctx.buffers[ctx.f_idx].m_vtx);
DrawIndexed(ctx.rd, 6, ctx.buffers[ctx.f_idx].count, 0); DrawIndexed(&ctx.rd, 6, ctx.buffers[ctx.f_idx].count, 0);
CompleteRendering(ctx);
ctx.frame += 1; ctx.frame += 1;
debug
{
static bool first = false;
if (!first)
{
PrintNodes(ctx.root);
first = true;
}
}
} }
void void
DrawNodes(UIItem* item) PrepRendering(UICtx* ctx)
{
BeginFrame(&ctx.rd);
BeginRendering(&ctx.rd);
Vec2 ext = GetExtent(&ctx.rd);
if (ext != ctx.res)
{
ctx.res = ext;
Ortho(&ctx.pc.projection, 0.0, 0.0, ext.x, ext.y, 1000.0, 0.1);
}
PushConstants(&ctx.rd, ctx.pipeline, &ctx.pc);
Bind(&ctx.rd, ctx.pipeline, ctx.desc_set);
}
void
CompleteRendering(UICtx* ctx)
{
FinishRendering(&ctx.rd);
SubmitAndPresent(&ctx.rd);
}
void
PrintNodes(UIItem* item)
{ {
if (!Nil(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); Logf("x0 %s x1 %s y0 %s y1 %s", item.rect.x0, item.rect.x1, item.rect.y0, item.rect.y1);
DrawNodes(item.first); PrintNodes(item.first);
DrawNodes(item.next); PrintNodes(item.next);
} }
} }
@ -516,6 +702,11 @@ DrawUI(UICtx* ctx, UIItem* item)
DrawRect(ctx, i); DrawRect(ctx, i);
} }
if (i.flags & UIF.DrawBorder)
{
DrawBorder(ctx, i);
}
if (i.flags & UIF.DrawText) if (i.flags & UIF.DrawText)
{ {
DrawLine(i); DrawLine(i);
@ -557,6 +748,10 @@ BuildItem(UIItem* item, UISize size_x, UISize size_y, UIFlags properties)
item.layout_axis = ctx.layout_axis; item.layout_axis = ctx.layout_axis;
item.level = ctx.panel_level; item.level = ctx.panel_level;
item.adjustment = ctx.adjustment; item.adjustment = ctx.adjustment;
item.border_color = ctx.border_color;
item.border_thickness = ctx.border_thickness;
item.corner_radius = ctx.corner_radius;
item.edge_softness = ctx.edge_softness;
item.parent = ctx.top_parent == g_UI_NIL_NODE ? g_UI_NIL : ctx.top_parent.item; item.parent = ctx.top_parent == g_UI_NIL_NODE ? g_UI_NIL : ctx.top_parent.item;
if (!Nil(item.parent)) if (!Nil(item.parent))
@ -583,6 +778,7 @@ Signal(UIItem* item)
if (item.flags & UIF.Clickable && Clicked(item, n)) if (item.flags & UIF.Clickable && Clicked(item, n))
{ {
Logf("Clicked"); Logf("Clicked");
item.last_click_pos = Vec2(n.value.x, n.value.y) - item.rect.vec0;
item.signal |= UIS.Clicked; item.signal |= UIS.Clicked;
taken = true; taken = true;
} }
@ -626,7 +822,7 @@ GetCtx()
Vec2 Vec2
RootSize() RootSize()
{ {
Vec2 size = GetExtent(GetCtx().rd); Vec2 size = GetExtent(&g_ui_ctx.rd);
return size; return size;
} }
@ -830,44 +1026,59 @@ DrawLine(UIItem* item)
} }
pragma(inline) void pragma(inline) void
DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, Vec4 col = Vec4(1.0)) DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool draw_bg = false, Vec4 col = Vec4(1.0))
{ {
UICtx* ctx = GetCtx();
Vertex* bg_v = null, v = null;
f32 h;
if (draw_bg)
{
bg_v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count;
AddUIIndices(ctx);
}
if (glyph.ch == '\t') if (glyph.ch == '\t')
{ {
*x_pos += glyph.advance * (GetCtx().tab_width - 1); *x_pos += glyph.advance * (GetCtx().tab_width - 1);
} }
else if (glyph.atlas_left != glyph.atlas_right && glyph.ch != '\n')
v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count;
f32 r = glyph.plane_right * scale;
f32 l = glyph.plane_left * scale;
f32 w = r - l;
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;
if (glyph.ch != '\t' && glyph.ch != '\n')
{ {
UICtx* ctx = GetCtx();
Vertex* v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].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.x = glyph.atlas_left;
v.src_start.y = glyph.atlas_top; v.src_start.y = glyph.atlas_top;
v.src_end.x = glyph.atlas_right; v.src_end.x = glyph.atlas_right;
v.src_end.y = glyph.atlas_bottom; 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);
} }
v.cols = col;
v.texture = 1;
AddUIIndices(ctx);
*x_pos += glyph.advance * scale; *x_pos += glyph.advance * scale;
if (draw_bg)
{
Vec4 white = Vec4(1.0);
bg_v.dst_start = v.dst_start;
bg_v.dst_end = v.dst_end;
bg_v.cols = white;
}
} }
pragma(inline) void pragma(inline) void
@ -886,6 +1097,21 @@ DrawRect(UICtx* ctx, UIItem* item)
AddUIIndices(ctx); AddUIIndices(ctx);
} }
pragma(inline) void
DrawBorder(UICtx* ctx, UIItem* item)
{
Vertex* v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count;
v.dst_start = item.rect.vec0;
v.dst_end = item.rect.vec1;
v.cols = item.border_color;
v.border_thickness = item.border_thickness;
v.corner_radius = item.corner_radius;
v.edge_softness = item.edge_softness;
v.raised = 0.0;
AddUIIndices(ctx);
}
pragma(inline) void pragma(inline) void
AddUIIndices(UICtx* ctx) AddUIIndices(UICtx* ctx)
{ {
@ -905,6 +1131,28 @@ Nil(UIItem* item)
return item == null || item == g_UI_NIL; return item == null || item == g_UI_NIL;
} }
void
ClickedCharIndex(UIItem* item)
{
UICtx* ctx = GetCtx();
f32 w = 0.0;
for(u64 i = 0; i < item.key.text.length; i += 1)
{
u8 ch = item.key.text[i];
if (ch < ctx.atlas.glyphs.length)
{
f32 ch_w = GlyphWidth(ctx.atlas.glyphs.ptr + ch);
if (ch_w + w > item.last_click_pos.x)
{
Logf("char clicked");
//item.clicked_char_idx = i;
break;
}
w += ch_w;
}
}
}
bool bool
Clicked(UIItem* item, DNode!(InputEvent)* n) Clicked(UIItem* item, DNode!(InputEvent)* n)
{ {

View File

@ -1,6 +1,8 @@
import dlib; import dlib;
import buffer;
import ui; import ui;
import editor;
import std.format : sformat; import std.format : sformat;
const UIPanel g_ui_nil_panel; const UIPanel g_ui_nil_panel;
@ -135,7 +137,12 @@ Panel(UIPanel* panel)
} }
} }
SetColor(panel.color); SetColor(
Vec4(0.3, 0.65, 0.86, 1.0),
Vec4(0.25, 0.60, 0.81, 1.0),
Vec4(0.25, 0.60, 0.81, 1.0),
Vec4(0.3, 0.65, 0.86, 1.0),
);
SetLayoutAxis(panel.axis); SetLayoutAxis(panel.axis);
BuildItem(item, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground); BuildItem(item, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground);
@ -159,7 +166,7 @@ CheckPanelBounds(f32 pct)
void void
Separator(UIItem* item, f32 x_size, f32 y_size, Axis2D axis) Separator(UIItem* item, f32 x_size, f32 y_size, Axis2D axis)
{ {
SetColor(Vec4(0.0, 0.0, 0.0, 1.0)); SetColor(Vec4(0.1, 0.2, 0.6, 1.0));
SizeType x_t = axis == A2D.X ? ST.Pixels : ST.Percentage; SizeType x_t = axis == A2D.X ? ST.Pixels : ST.Percentage;
SizeType y_t = axis == A2D.Y ? ST.Pixels : ST.Percentage; SizeType y_t = axis == A2D.Y ? ST.Pixels : ST.Percentage;
@ -168,24 +175,44 @@ Separator(UIItem* item, f32 x_size, f32 y_size, Axis2D axis)
} }
void void
TextLine(u8[] text, f32 text_size, u64 line_no) EditorView(Editor* ed)
{ {
UICtx* ctx = GetCtx();
UIItem* parent = PeekParent(); UIItem* parent = PeekParent();
if (!Nil(parent) && parent.size.x > 0.0) if (ed.buf.length > 0)
{ {
SetColor(Vec4(1.0));
SetTextSize(text_size);
Node!(u8[])* lines = MakeMultiline(text, parent.size.x, line_no);
for(Node!(u8[])* line = lines; line != null; line = line.next)
{
UIItem* item = Get(line.value);
BuildItem(item, UISize(ST.Percentage, 1.0), UISize(ST.Pixels, text_size), UIF.DrawText);
}
} }
} }
UIItem*
TextLine(u8[] text, f32 text_size, u64 line_no)
{
UIItem* item = g_UI_NIL;
UIItem* parent = PeekParent();
SetColor(Vec4(1.0));
SetTextSize(text_size);
Node!(u8[])* lines = MakeMultiline(text, parent.size.x, line_no);
for(Node!(u8[])* line = lines; line != null; line = line.next)
{
item = Get(line.value);
Signal(item);
if (item.signal & UIS.Clicked)
{
ClickedCharIndex(item);
}
BuildItem(item, UISize(ST.Percentage, 1.0), UISize(ST.Pixels, text_size), UIF.DrawText|UIF.Clickable|UIF.Draggable);
}
return item;
}
void void
EndPanel() EndPanel()
{ {