diff --git a/src/VulkanRenderer b/src/VulkanRenderer index 5c2a3ab..65ac576 160000 --- a/src/VulkanRenderer +++ b/src/VulkanRenderer @@ -1 +1 @@ -Subproject commit 5c2a3ab5efa1aaa15ca6f3144655550dd024823a +Subproject commit 65ac576cb0f37e797030365ef25d11a771468654 diff --git a/src/editor/buffer.d b/src/editor/buffer.d index 0c753b9..f042955 100644 --- a/src/editor/buffer.d +++ b/src/editor/buffer.d @@ -108,7 +108,7 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, Range pos) // TODO: handle case for when lines are longer than line buffer 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"); 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); linebufs.lines = AllocArray!(u8[])(&linebufs.arena, length); - u64 extra_buffer = add_hash ? 10 : 0; i64 start = -1; u64 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) { - 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'; current_line += 1; continue; @@ -150,7 +149,7 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length, 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]; current_line += 1; start = -1; @@ -159,23 +158,10 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length, 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]; } } - - 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 diff --git a/src/editor/editor.d b/src/editor/editor.d index f68faff..737048f 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -13,289 +13,58 @@ import widgets; import std.stdio; 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; -Editor* g_editor; + +EditorCtx g_ed_ctx; + +struct EditorCtx +{ + Arena arena; + UIPanel* base_panel; +} struct Editor { - Arena arena; - Arena temp_arena; - PlatformWindow* window; + Arena arena; - Renderer rd; - ImageView font_atlas; - Pipeline pipeline; - DescSetLayout desc_set_layout; - DescSet desc_set; - PipelineLayout pipeline_layout; - PushConst pc; + u8[] filename; + FlatBuffer buf; + Tokenizer tk; + LineBuffers linebufs; - FlatBuffer[] buffers; - Tokenizer[] tokenizers; - u8[][] buffer_names; - u32 buffer_count; - FlatBuffer* active_buffer; - LineBuffers linebufs; + Vec2 cursor_pos; + Vec2 select_start; + Vec2 select_end; - u8[] font_data; - FontFace font; - 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; + f32 text_size; + u64 line_offset; } void -Cycle(Editor* ed, Inputs* inputs) +Cycle(Inputs* inputs) { - Reset(&ed.temp_arena); ResetScratch(MB(4)); 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(); - - FinishRendering(&ed.rd); - - SubmitAndPresent(&ed.rd); } -Editor -CreateEditor(PlatformWindow* window, u8[] buffer_data, u8[] buffer_name) +void +InitEditorCtx(PlatformWindow* window) { 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 = { - window: window, - arena: arena, - temp_arena: CreateArena(MB(8)), - 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)), + InitUICtx(window); + + EditorCtx ctx = { + arena: CreateArena(MB(2)), }; - if (buffer_data != null) - { - 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; - } - } - } + g_ed_ctx = ctx; } +/* void 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 { - 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]]); } } } } } +*/ diff --git a/src/editor/main.d b/src/editor/main.d index 09e58b9..c8be89e 100644 --- a/src/editor/main.d +++ b/src/editor/main.d @@ -21,7 +21,7 @@ void main(string[] argv) PlatformWindow window = CreateWindow("Editor", 1920, 1080); StartPlatformThread(&window); - Editor editor = CreateEditor(&window, buffer, buffer_name); + InitEditorCtx(&window); while (true) { @@ -31,6 +31,6 @@ void main(string[] argv) break; } - Cycle(&editor, inputs); + Cycle(inputs); } } diff --git a/src/editor/ui.d b/src/editor/ui.d index c8c5c1a..7d21f2a 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -13,6 +13,12 @@ import core.stdc.string : memset; 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; const UIItem g_ui_nil_item; @@ -35,8 +41,9 @@ enum UIFlags None = 0x00, DrawBackground = 0x01, DrawText = 0x02, - Clickable = 0x04, - Draggable = 0x08, + DrawBorder = 0x04, + Clickable = 0x08, + Draggable = 0x10, } alias UIF = UIFlags; @@ -62,11 +69,24 @@ struct UICtx { HashTable!(UIHash, UIItem*) items; Arena arena; - Renderer* rd; Inputs* inputs; u64 frame; 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; UIItem* root; @@ -85,6 +105,9 @@ struct UICtx Vec2 adjustment; u32 tab_width; f32 text_size; + f32 border_thickness; + f32 corner_radius; + f32 edge_softness; } struct UIItemStackList @@ -123,6 +146,11 @@ struct UIItem Vec2 size; Rect rect; Vec4[4] color; + Vec4[4] border_color; + f32 border_thickness; + f32 corner_radius; + f32 edge_softness; + Vec2 last_click_pos; } struct UISize @@ -140,6 +168,25 @@ struct UIBuffer 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 { Vec2[2] v; @@ -168,8 +215,21 @@ struct UIKey } 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_NODE = cast(UIItemNode*)&g_ui_nil_item_node; @@ -177,29 +237,88 @@ InitUICtx(Renderer* rd, FontAtlas atlas) UIBuffer[FRAME_OVERLAP] buffers; - u64 vertex_size = 10000; - for(u64 i = 0; i < FRAME_OVERLAP; i += 1) - { - buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(rd, BT.Vertex, vertex_size); - 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; - } + FontFace font = OpenFont(cast(u8[])FONT_BYTES); + FontAtlasBuf atlas_buf = CreateAtlas(&arena, font, 16.0, 256); + + Vec4 b = Vec4(Vec3(0.0), 1.0); UICtx ctx = { - rd: rd, + rd: InitRenderer(handles, MB(16), MB(8)), items: CreateHashTable!(UIHash, UIItem*)(12), - arena: CreateArena(MB(4)), - atlas: atlas, + arena: arena, root: g_UI_NIL, top_parent: g_UI_NIL_NODE, prev_sibling: g_UI_NIL_NODE, drag_item: g_UI_NIL, text_size: 14.0, 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; InitWidgets(); @@ -351,6 +470,24 @@ SetAdjustment(Vec2 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 SetColor(Vec4 col) { @@ -367,6 +504,22 @@ SetColor(Vec4 col0, Vec4 col1, Vec4 col2, Vec4 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 SetLayoutAxis(Axis2D axis) { @@ -418,28 +571,61 @@ EndBuild() } DrawUI(ctx, ctx.root); - static bool first = false; - if (!first) - { - DrawNodes(ctx.root); - first = true; - } + PrepRendering(ctx); - 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); + 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); + + CompleteRendering(ctx); ctx.frame += 1; + + debug + { + static bool first = false; + if (!first) + { + PrintNodes(ctx.root); + first = true; + } + } } 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)) { 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); + PrintNodes(item.first); + PrintNodes(item.next); } } @@ -516,6 +702,11 @@ DrawUI(UICtx* ctx, UIItem* item) DrawRect(ctx, i); } + if (i.flags & UIF.DrawBorder) + { + DrawBorder(ctx, i); + } + if (i.flags & UIF.DrawText) { DrawLine(i); @@ -557,6 +748,10 @@ BuildItem(UIItem* item, UISize size_x, UISize size_y, UIFlags properties) item.layout_axis = ctx.layout_axis; item.level = ctx.panel_level; 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; if (!Nil(item.parent)) @@ -583,6 +778,7 @@ Signal(UIItem* item) if (item.flags & UIF.Clickable && Clicked(item, n)) { Logf("Clicked"); + item.last_click_pos = Vec2(n.value.x, n.value.y) - item.rect.vec0; item.signal |= UIS.Clicked; taken = true; } @@ -626,7 +822,7 @@ GetCtx() Vec2 RootSize() { - Vec2 size = GetExtent(GetCtx().rd); + Vec2 size = GetExtent(&g_ui_ctx.rd); return size; } @@ -830,44 +1026,59 @@ DrawLine(UIItem* item) } 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') { *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.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); } + v.cols = col; + + v.texture = 1; + + AddUIIndices(ctx); + *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 @@ -886,6 +1097,21 @@ DrawRect(UICtx* ctx, UIItem* item) 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 AddUIIndices(UICtx* ctx) { @@ -905,6 +1131,28 @@ Nil(UIItem* item) 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 Clicked(UIItem* item, DNode!(InputEvent)* n) { diff --git a/src/editor/widgets.d b/src/editor/widgets.d index 5d40a5a..30d233b 100644 --- a/src/editor/widgets.d +++ b/src/editor/widgets.d @@ -1,6 +1,8 @@ import dlib; +import buffer; import ui; +import editor; import std.format : sformat; 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); BuildItem(item, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground); @@ -159,7 +166,7 @@ CheckPanelBounds(f32 pct) void 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 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 -TextLine(u8[] text, f32 text_size, u64 line_no) +EditorView(Editor* ed) { + UICtx* ctx = GetCtx(); 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 EndPanel() {