diff --git a/src/editor/editor.d b/src/editor/editor.d index f6f514a..20045c7 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -3,9 +3,8 @@ import dlib; import vulkan; import std.format : sformat; import buffer; +import ui : Nil; import ui; -import widgets : Nil; -import widgets; import parsing; import std.format; @@ -148,7 +147,17 @@ Cycle(EditorCtx* ctx, Inputs* inputs) for(auto p = ctx.base_panel; !Nil(p); p = Recurse(p)) { - Panel2(p); + Panel(p); + } + + if(ctx.state == ES.CmdOpen) + { + if(ctx.cmd.commands.length == 0 && ctx.cmd.icount == 0) + { + GetCommands(&ctx.cmd); + } + + CommandPalette(&ctx.cmd); } EndUI(); diff --git a/src/editor/ui.d b/src/editor/ui.d index cb615b8..41b366c 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -2,16 +2,51 @@ import dlib; import dlib.util : HTPush = Push; import vulkan; -import widgets; +import buffer; +import parsing; +import editor; + import std.stdio; import std.math.traits : isNaN; import std.math.rounding : ceil; import std.format : sformat; import core.stdc.string : memset; import core.stdc.math : fabsf; +import std.conv; +import core.stdc.stdio : sprintf; -import parsing; -import editor; +const Vec4[4] DEFAULT_COL = Vec4(0.13, 0.13, 0.13, 1.0); + +const Vec4[4] CMD_PALETTE_COL = Vec4(0.21, 0.21, 0.21, 1.0); + +const Vec4[4] DEFAULT_BORDER_COL = Vec4(0.254, 0.254, 0.266, 1.0); + +const Vec4[4] HL_BORDER_COL = Vec4(0.035, 0.549, 0.824, 1.0); + +const Vec4[4] LC_COLOR = Vec4(0.12, 0.12, 0.12, 1.0); + +const Vec4[4] LC_HL_COLOR = Vec4(0.012, 0.176, 0.29, 1.0); + +const Vec4[4] CMD_PALETTE_INPUT_HL = Vec4(0.24, 0.45, 0.81, 1.0); + +const Vec4[4] CMD_INPUT_BORDER_COL = Vec4(0.003, 0.48, 0.68, 1.0); + +const Vec4[4] CMD_INPUT_BORDER_HL = Vec4(0.05, 0.56, 0.76, 1.0); + +const Vec4[4] CMD_PALETTE_INPUT_COL = [ + Vec4(0.14, 0.14, 0.14, 1.0), + Vec4(0.14, 0.14, 0.14, 1.0), + Vec4(0.17, 0.17, 0.17, 1.0), + Vec4(0.17, 0.17, 0.17, 1.0), +]; + +// TODO: add setting +const f32 TEXT_SIZE = 16.0; + +const UIPanel g_ui_nil_panel; +UIPanel* g_UI_NIL_PANEL; + +UIPanel g_root_panel; const u8[] FONT_BYTES = import("pc-9800.ttf"); const u8[] VERTEX_BYTES = import("gui.vert.spv"); @@ -108,36 +143,14 @@ struct UICtx UIBuffer[FRAME_OVERLAP] buffers; - UIItem* root; - UIItemNode* top_parent; - UIItemNode* prev_sibling; - u32 panel_level; - UIItem* drag_item; - UIItem* focused; - - UIItemStackList stack_free_list; - - UIStack!(Vec4[4])* color; - UIStack!(Vec4[4])* border_color; - UIStack!(Vec4)* text_color; - UIStack!(Axis2D)* layout_axis; - UIStack!(Vec2)* adjustment; - UIStack!(Vec2)* offset; - UIStack!(Vec2)* padding; - UIStack!(f32)* border_thickness; - UIStack!(f32)* corner_radius; - UIStack!(f32)* edge_softness; - - i64 highlighted_char; - I64Vec2 selected; u32 tab_width; f32 text_size; - debug u32 item_count; - debug u32 final_count; - debug bool dbg; + UIItem* drag_item; + UIPanel* parent; + UIPanel* focused_panel; - + debug bool dbg; } struct UIStack(T) @@ -160,40 +173,9 @@ struct UIItemNode struct UIItem { - UIFlags flags; UIKey key; - UISignal signal; - - UIItem* next; - UIItem* prev; - UIItem* first; // first child - UIItem* last; // last child - UIItem* parent; - u64 last_frame; - u32 level; Vec2 dragged; - - // Build Parameters - Axis2D layout_axis; - UISize[2] size_info; - Vec2 adjustment; - i64 highlighted_char; - I64Vec2 selected; - - // Calculated Parameters - Vec4[4] color; - Vec4[4] border_color; - TokenStyle[] token_styles; - Rect rect; - Vec2 size; - Vec2 last_click_pos; - Vec2 offset; - Vec2 padding; - f32 border_thickness; - f32 corner_radius; - f32 edge_softness; - Rect culling; } struct UISize @@ -273,6 +255,365 @@ struct UIKey u64 hash; } +struct UIPanel +{ + AnimState anim; + u8[] id; + Editor* ed; + + UIPanel* parent; + UIPanel* next; + UIPanel* prev; + UIPanel* first; + UIPanel* last; + + UIPanel* list_next; + + Axis2D axis; + f32 pct; + Vec4 color; + + Rect rect; + Vec2 size; +} + +struct AnimState +{ + f32 start_value; + f32 end_value; + f32 start_time; + f32 remaining_time; +} + +struct TextPart +{ + UIItem* item; + TextPart* next; + u32 count; +} + +void +BeginUI(EditorCtx* edctx, Inputs* inputs) +{ + UICtx* ctx = GetCtx(); + + Reset(&ctx.temp_arena); + + ctx.f_idx = ctx.frame%FRAME_OVERLAP; + ctx.inputs = inputs; + + PrepRendering(ctx); + + SetPanelSizes(edctx.base_panel); +} + +void +EndUI() +{ + UICtx* ctx = GetCtx(); + + with(ctx) + { + BindBuffers(&rd, &buffers[f_idx].m_idx, &buffers[f_idx].m_vtx); + DrawIndexed(&rd, 6, buffers[f_idx].count, 0); + } + + CompleteRendering(ctx); + + ctx.frame += 1; +} + +void +SetPanelSizes(UIPanel* panel) +{ + UICtx* ctx = GetCtx(); + + panel.size.x = ctx.res.x; + panel.size.y = ctx.res.y; + panel.rect.vec0 = 0.0; + panel.rect.vec1 = 0.0; + + static foreach(axis; A2D.min .. A2D.max) + { + for(UIPanel* p = panel; !Nil(p); p = Recurse(p)) + { + f32 pos = p.rect.vec0.v[axis]; + for(UIPanel* c = p.first; !Nil(c); c = c.next) + { + c.size.v[axis] = p.axis == axis ? c.pct * p.size.v[axis] : p.size.v[axis]; + c.rect.vec0.v[axis] = pos; + c.rect.vec1.v[axis] = pos + c.size.v[axis]; + + if(axis == p.axis) + { + pos += c.size.v[axis]; + } + } + } + } +} + +pragma(inline) void +DrawChar(FontAtlas* atlas, u8 ch, f32* x_pos, f32 y, Vec4 col = Vec4(1.0)) +{ + if(ch < atlas.glyphs.length) + { + Glyph* g = atlas.glyphs.ptr + ch; + DrawGlyph(g, 1.0, x_pos, y, col); + } +} + +void +Panel(UIPanel* panel) +{ + UICtx* ctx = GetCtx(); + UIItem* item = Get(panel.id); + Editor* ed = panel.ed; + bool focused = GetFocusedPanel() == panel; + + UIPanel* parent = panel.parent; + UIPanel* prev = panel.prev; + + Axis2D pax = parent.axis; + + Vec2 adj = Vec2( + parent.axis == A2D.X ? 10 : 0, + parent.axis == A2D.Y ? 10 : 0 + ); + + Vec2 p0 = panel.rect.vec0-adj; + Vec2 p1 = panel.rect.vec1+adj; + + if(!Nil(prev)) with(panel) + { + if(Dragged(item, p0, p1) && item.dragged.v[pax] != 0.0) + { + f32 mov_pct = Remap(item.dragged.v[pax], 0.0, size.v[pax], 0.0, 1.0); + if(CheckPanelBounds(pct + mov_pct) && CheckPanelBounds(panel.prev.pct - mov_pct)) + { + pct += mov_pct; + panel.prev.pct -= mov_pct; + } + } + } + + if(panel.ed != null) + { + if(Clicked(item, p0, p1)) + { + SetFocusedPanel(panel); + } + + DrawBorderedRect(panel.rect.vec0, panel.size, 2.0, 2.0, 0.1, DEFAULT_COL, focused ? HL_BORDER_COL : DEFAULT_BORDER_COL); + + i64 rows = cast(i64)ceil(panel.size.y/TEXT_SIZE); + + GetLines(&ed.buf, &ed.linebufs, rows); + + char[64] ch_buf = '\0'; + char[] fmt = ch_buf.sformat("%%0%ss", u64(ed.linebufs.end.toChars().length)); + + u8[] end_line_text = ScratchAlloc!(u8)(ed.linebufs.end.toChars().length, '0'); + + f32 lcw = CalcTextWidth(end_line_text); + f32 padding = 4.0; + + DrawRect(panel.rect.vec0, panel.rect.vec1+Vec2(lcw+padding*2, panel.size.y), 0.0, 0.0, focused ? LC_HL_COLOR : LC_COLOR); + + f32 x = panel.rect.x0; + f32 y = panel.rect.y0 + TEXT_SIZE; + + FontAtlas* atlas = &ctx.atlas_buf.atlas; + bool edit = EditModeActive(); + U64Vec2 pos = VecPos(&ed.buf); + u64 i; + for(auto buf = ed.linebufs.first; buf != null; buf = buf.next, i += 1) + { + f32 x_pos = x + padding; + + DrawLineCount(atlas, fmt, &x_pos, y, ed.linebufs.start+i); + + x_pos += padding; + + u64 ch_offset; + auto parts = MakeMultiline(buf.text, panel.size.x-lcw, buf.style); + for(auto n = parts; n != null; n = n.next) + { + auto l = &n.value; + + if(pos.y == i) + { + DrawCursor(atlas, l.text, pos.x, x+lcw+padding*2.0, y, ch_offset, edit); + } + + foreach(j; 0 .. l.text.length) + { + bool hl = pos.y == i && !edit && j == pos.x-ch_offset; + DrawChar(atlas, l.text[j], &x_pos, y, hl ? Vec4(Vec3(0.0), 1.0) : SYNTAX_COLORS[l.style[j]]); + } + + y += TEXT_SIZE; + x_pos = x + lcw + padding*2.0; + ch_offset += l.text.length; + } + } + } +} + +pragma(inline) void +DrawCursor(FontAtlas* atlas, u8[] text, u64 ch_x, f32 x, f32 y, u64 offset, bool edit) +{ + Glyph* g = GetGlyph(' '); + foreach(j; 0 .. text.length) + { + bool hl = j == ch_x-offset; + if(hl) + { + break; + } + + g = j == text.length-1 ? GetGlyph(' ') : GetGlyph(text[j]); + x += GlyphWidth(g); + } + + DrawRect(x, y, atlas, cast(u8)g.ch, Vec4(1.0), edit); +} + +pragma(inline) void +DrawLineCount(FontAtlas* atlas, char[] fmt, f32* x_pos, f32 y, u64 line) +{ + char[32] line_buf = '\0'; + char[] line_str = sformat(line_buf, fmt, line); + + foreach(j; 0 .. line_str.length) + { + DrawChar(atlas, line_str[j], x_pos, y, Vec4(1.0)); + } +} + +void +CommandPalette(CmdPalette* cmd) +{ + UICtx* ctx = GetCtx(); + + u8[] text = cmd.buffer[0 .. cmd.icount]; + u8[][] options = cmd.opt_strs; + + Vec2 size = RootSize(); + + f32 x = size.x*0.15; + f32 y = size.y*0.1; + f32 w = size.x*0.7; + f32 h = 40.0; + + f32 corner_r = 2.0, edge_softness = 0.1, border_thickness = 2.0; + + DrawBorderedRect(Vec2(x, y), Vec2(w, h), border_thickness, corner_r, edge_softness, CMD_PALETTE_INPUT_COL, CMD_INPUT_BORDER_COL); + + f32 y_off = h*0.5 + 6; + f32 ch_x = x + 6.0; + f32 ch_y = y + y_off; + + foreach(i; 0 .. text.length) + { + DrawChar(&ctx.atlas_buf.atlas, text[i], &ch_x, ch_y, Vec4(1.0)); + } + + for(u64 i = 0; i < options.length; i += 1) + { + y += h; + ch_x = x + 6.0; + ch_y = y + y_off; + + DrawBorderedRect( + Vec2(x, y), + Vec2(w, h), + border_thickness, + corner_r, + edge_softness, + cmd.selected == i ? CMD_PALETTE_INPUT_HL : CMD_PALETTE_INPUT_COL, + cmd.selected == i ? CMD_INPUT_BORDER_HL : CMD_INPUT_BORDER_COL + ); + + foreach(j; 0 .. options[i].length) + { + DrawChar(&ctx.atlas_buf.atlas, options[i][j], &ch_x, ch_y, Vec4(1.0)); + } + } +} + +UIPanel* +Recurse(UIPanel* panel) +{ + UIPanel* result = g_UI_NIL_PANEL; + if(!Nil(panel.first)) + { + result = panel.first; + } + else if(!Nil(panel.next)) + { + result = panel.next; + } + else for(UIPanel* p = panel.parent; !Nil(p); p = p.parent) + { + if(!Nil(p.next)) + { + result = p.next; + break; + } + } + + return result; +} + +bool +CheckPanelBounds(f32 pct) +{ + return pct >= 0.0 && pct <= 1.0; +} + +void +PushPanel(UIPanel* parent, UIPanel* panel) +{ + DLLPush(parent, panel, g_UI_NIL_PANEL); + panel.parent = parent; +} + +void +InsertPanel(UIPanel* parent, UIPanel* prev, UIPanel* panel) +{ + DLLInsert(parent, panel, prev, g_UI_NIL_PANEL); + panel.parent = prev.parent; +} + +void +InitWidgets() +{ + g_UI_NIL_PANEL = cast(UIPanel*)&g_ui_nil_panel; + g_ui_ctx.parent = g_UI_NIL_PANEL; +} + +void +SetFocusedPanel(UIPanel* panel) +{ + if(!CheckNil(g_UI_NIL_PANEL, panel)) + { + g_ui_ctx.focused_panel = panel; + } +} + +UIPanel* +GetFocusedPanel() +{ + return Nil(g_ui_ctx.focused_panel) ? g_UI_NIL_PANEL : g_ui_ctx.focused_panel; +} + +bool +Nil(UIPanel* panel) +{ + return panel == null || panel == g_UI_NIL_PANEL; +} + void InitUICtx(PlatformWindow* window) { @@ -306,17 +647,12 @@ InitUICtx(PlatformWindow* window) items: CreateHashTable!(UIHash, UIItem*)(12), arena: arena, temp_arena: CreateArena(MB(1)), - root: g_UI_NIL, - top_parent: g_UI_NIL_NODE, - prev_sibling: g_UI_NIL_NODE, drag_item: g_UI_NIL, - focused: g_UI_NIL, atlas_buf: atlas_buf, font: font, font_data: cast(u8[])FONT_BYTES, text_size: 16.0, tab_width: 2, - highlighted_char: -1, }; u64 vertex_size = 10000; @@ -382,260 +718,6 @@ InitUICtx(PlatformWindow* window) InitWidgets(); } -void -ClearStacks(UICtx* ctx) -{ - import std.traits; - - static foreach(i; 0 .. ctx.tupleof.length) - { - { - alias T = typeof(ctx.tupleof[i]); - - static if( - isPointer!(T) && - hasMember!(T, "value") && - is(typeof(*ctx.tupleof[i]): UIStack!(typeof(ctx.tupleof[i].value))) - ) - { - ctx.tupleof[i] = null; - ctx.tupleof[i] = Alloc!(UIStack!(typeof(ctx.tupleof[i].value)))(&ctx.temp_arena); - } - } - } -} - -void -Push(string id, T)(T value) -{ - import std.string; - import std.traits; - - enum string s = "g_ui_ctx.@".replace("@", id); - alias Y = typeof(mixin(s)); - - enum is_ptr = isPointer!(Y); - enum has_mem = is_ptr && hasMember!(typeof(*mixin(s)), "next"); - - static if(is_ptr && has_mem) - { - enum stack = true; - alias U = typeof(mixin(s~".value")); - } - else - { - enum stack = false; - alias U = typeof(mixin(s)); - } - - static if(!stack) - { - mixin(s) = value; - } - else static if(stack) - { - static if(is(U: Vec4[4]) && is(T: Vec4)) - { - UIStack!(U)* node = Alloc!(UIStack!(U))(&g_ui_ctx.temp_arena); - node.value[] = value; - } - else - { - UIStack!(U)* node = Alloc!(UIStack!(U))(&g_ui_ctx.temp_arena); - node.value = value; - } - - node.next = mixin(s); - mixin(s) = node; - } -} - -void -Pop(stacks...)() -{ - static foreach(stack; stacks) - { - Pop!(stack); - } -} - -auto -Pop(string stack)() -{ - import std.string; - import std.traits; - - enum string s = "g_ui_ctx.@".replace("@", stack); - enum string v = s~".value"; - enum string n = s~".next"; - - static assert(isPointer!(typeof(mixin(s))) && hasMember!(typeof(*mixin(s)), "next")); - - assert(mixin(s) != null); - auto value = mixin(v); - mixin(s) = mixin(n); - return value; -} - -auto -Get(string id)() -{ - import std.string; - import std.traits; - - enum string s = "g_ui_ctx.@".replace("@", id); - - static if(isPointer!(typeof(mixin(s))) && hasMember!(typeof(*mixin(s)), "next")) - { - assert(mixin(s) != null); - return mixin(s~".value"); - } - else - { - return mixin(s); - } -} - -UIItemNode* -PopItemNode() -{ - UICtx* ctx = GetCtx(); - - UIItemNode* node; - if(!CheckNil(g_UI_NIL_NODE, ctx.stack_free_list.first)) - { - node = SLLPop(&ctx.stack_free_list, g_UI_NIL_NODE); - } - else - { - node = Alloc!(UIItemNode)(&ctx.arena); - } - - node.item = g_UI_NIL; - node.next = g_UI_NIL_NODE; - - return node; -} - -void -PushItemNode(UIItemNode* node) -{ - UICtx* ctx = GetCtx(); - SLLPushFront(&ctx.stack_free_list, node, g_UI_NIL_NODE); -} - -void -PushSiblingPanel(UIItem* panel) -{ - UICtx* ctx = GetCtx(); - UIItemNode* node = PopItemNode(); - - node.item = panel; - node.next = ctx.prev_sibling; - ctx.prev_sibling = node; -} - -UIItem* -PopSiblingPanel() -{ - UICtx* ctx = GetCtx(); - UIItem* panel = g_UI_NIL; - UIItemNode* node = ctx.prev_sibling; - - if(node != g_UI_NIL_NODE) - { - panel = node.item; - ctx.prev_sibling = node.next; - PushItemNode(node); - } - - return panel; -} - -void -PopSiblingPanels() -{ - UIItem* sibling = PeekSiblingPanel(); - while(!Nil(sibling)) - { - sibling = PopSiblingPanel(); - } -} - -UIItem* -PeekSiblingPanel() -{ - return g_ui_ctx.prev_sibling == g_UI_NIL_NODE ? g_UI_NIL : g_ui_ctx.prev_sibling.item; -} - -UIItem* -PrevSiblingPanel(UIItem* panel) -{ - UICtx* ctx = GetCtx(); - UIItem* result = g_UI_NIL; - UIItemNode* prev = ctx.prev_sibling; - for(;;) - { - if(panel == prev.item) - { - prev = prev.next; - } - - if(prev == g_UI_NIL_NODE) - { - break; - } - - if(prev.item.level < panel.level) - { - break; - } - - if(prev.item.level == panel.level) - { - result = prev.item; - break; - } - - prev = prev.next; - } - - return result; -} - -UIItem* -PeekParent() -{ - return g_ui_ctx.top_parent == g_UI_NIL_NODE ? g_UI_NIL : g_ui_ctx.top_parent.item; -} - -UIItem* -PopParent() -{ - UICtx* ctx = GetCtx(); - UIItemNode* node = ctx.top_parent; - UIItem* item = node.item; - - assert(!Nil(item)); - ctx.top_parent = node.next; - PushItemNode(node); - ctx.panel_level -= 1; - - return item; -} - -void -PushParent(UIItem* parent) -{ - UICtx* ctx = GetCtx(); - UIItemNode* node = PopItemNode(); - - node.item = parent; - node.next = ctx.top_parent; - ctx.top_parent = node; - ctx.panel_level += 1; -} - void SetDebug(bool dbg) { @@ -645,82 +727,6 @@ SetDebug(bool dbg) } } -void -BeginBuild(Inputs* inputs) -{ - UICtx* ctx = GetCtx(); - - debug ctx.item_count = 0; - debug ctx.final_count = 0; - - Reset(&ctx.temp_arena); - ClearStacks(ctx); - Push!("text_size")(16.0); - Push!("tab_width")(2); - - KVPair!(u64, UIItem*)*[] items = GetAllNodes(&ctx.temp_arena, &ctx.items); - for(u64 i = 0; i < items.length; i += 1) - { - UIItem* item = items[i].value; - if(item.last_frame != ctx.frame-1) - { - Delete(&ctx.items, items[i].key); - } - } - - ctx.f_idx = ctx.frame%FRAME_OVERLAP; - - ctx.inputs = inputs; - - ctx.panel_level = 0; - - ctx.root = Root(); - PushParent(ctx.root); -} - -void -EndBuild() -{ - UICtx* ctx = GetCtx(); - - PopParent(); - PopSiblingPanels(); - - assert(ctx.prev_sibling == g_UI_NIL_NODE); - assert(ctx.top_parent == g_UI_NIL_NODE, "Parent panel not popped"); - - CalcFixedSizes(ctx.root); - CalcPercentageSizes(ctx.root); - static foreach(axis; A2D.min .. A2D.max) - { - CalcPositions!(axis)(ctx.root); - } - FixViolations(ctx.root); - PrepRendering(ctx); - - DrawUI(ctx, ctx.root); - - debug if(ctx.dbg) - { - DrawDebugUI(ctx, ctx.root); - } - - debug assert(ctx.item_count == ctx.final_count); - - ctx.frame += 1; - - debug - { - static u32 prev_count = 0; - if(prev_count != ctx.item_count) - { - //Logf("New node count, printing node debug info: "); - //PrintNodes(ctx.root); - prev_count = ctx.item_count; - } - } -} - void PrepRendering(UICtx* ctx) { @@ -751,349 +757,6 @@ CompleteRendering(UICtx* ctx) SubmitAndPresent(&ctx.rd); } -void -PrintNodes(UIItem* item) -{ - if(!Nil(item)) - { - Logf("%s:", cast(char[])item.key.text); - Logf("x0 %s x1 %s y0 %s y1 %s", item.rect.x0, item.rect.x1, item.rect.y0, item.rect.y1); - - PrintNodes(item.first); - PrintNodes(item.next); - } -} - -void -FixViolations(UIItem* item) -{ - static foreach(axis; A2D.X .. A2D.Y) - { - for(UIItem* i = item; !Nil(i); i = Recurse(i)) - { - f32 total = 0.0; - for(UIItem* c = i.first; !Nil(c); c = c.next) - { - total += c.size.v[axis]; - if(total > i.size.v[axis]) - { - f32 size = c.size.v[axis]; - f32 sub = total - i.size.v[axis]; - c.size.v[axis] = Max(size - sub, 0.0); - i.culling.vec1.v[axis] = Min(size - sub, size); - } - } - } - } -} - -void -CalcFixedSizes(UIItem* item) -{ - for(UIItem* i = item; !Nil(i); i = Recurse(i)) - { - static foreach(axis; A2D.min .. A2D.max) - { - if(i.size_info[axis].type == ST.Pixels) - { - i.size.v[axis] = i.size_info[axis].value + i.adjustment.v[axis]; - assert(!isNaN(i.size.v[axis])); - } - } - } -} - -void -CalcPercentageSizes(UIItem* item) -{ - for(UIItem* i = item; !Nil(i); i = Recurse(i)) - { - static foreach(axis; A2D.min .. A2D.max) - { - if(i.size_info[axis].type == ST.Percentage) - { - i.size.v[axis] = (i.parent.size.v[axis]*i.size_info[axis].value) + i.adjustment.v[axis]; - assert(!isNaN(i.size.v[axis])); - } - } - } -} - -void -CalcPositions(alias axis)(UIItem* item) -{ - f32 pos = 0.0; - for(UIItem* i = item; !Nil(i);) - { - f32 end_pos = 0.0, next_pos = 0.0; - if(i.flags & UIF.Window) - { - end_pos = i.offset.v[axis] + i.size.v[axis]; - i.rect.vec0.v[axis] = i.offset.v[axis]; - i.rect.vec1.v[axis] = end_pos; - - pos = i.rect.vec0.v[axis]; - } - else - { - pos += i.offset.v[axis]; - end_pos = pos + i.size.v[axis]; - i.rect.vec0.v[axis] = pos; - i.rect.vec1.v[axis] = end_pos; - - assert(!isNaN(i.rect.vec0.v[axis])); - assert(!isNaN(i.rect.vec1.v[axis])); - - next_pos = i.parent.layout_axis == axis ? end_pos : pos; - } - - if(!Nil(i.first)) - { - i = i.first; - } - else if(!Nil(i.next)) - { - i = i.next; - pos = next_pos; - } - else for(UIItem* p = i.parent; true; p = p.parent) - { - if(!Nil(p.next)) - { - i = p.next; - pos = i.parent.layout_axis == axis ? i.prev.rect.vec1.v[axis] : i.prev.rect.vec0.v[axis]; - break; - } - - if(Nil(p)) - { - i = g_UI_NIL; - break; - } - } - } -} - -void -DrawDebugUI(UICtx* ctx, UIItem* item) -{ - for(UIItem* i = item; !Nil(i); i = Recurse(i)) - { - if(i.flags & UIF.DrawBackground) - { - DrawPanelDebug(ctx, i); - } - } -} - -void -DrawUI(UICtx* ctx, UIItem* item) -{ - for(UIItem* i = item; !Nil(i) && i != ctx.root.next; i = Recurse(i)) - { - if(i.flags & UIF.DrawBackground) - { - DrawRect(ctx, i); - } - - if(i.flags & UIF.DrawBorder) - { - DrawBorder(ctx, i); - } - - if(i.flags & UIF.DrawText) - { - DrawLine(i); - } - - debug ctx.final_count += 1; - } - - for(UIItem* i = item; !Nil(i); i = Recurse(i)) - { - if(i.flags & UIF.DeferredBorder) - { - DrawBorder(ctx, i); - } - } - - for(UIItem* i = item.next; !Nil(i); i = Recurse(i)) - { - if(i.flags & UIF.DrawBackground) - { - DrawRect(ctx, i); - } - - if(i.flags & UIF.DrawBorder) - { - DrawBorder(ctx, i); - } - - if(i.flags & UIF.DrawText) - { - DrawLine(i); - } - - debug ctx.final_count += 1; - } -} - -UIItem* -Recurse(UIItem* item) -{ - UIItem* result = g_UI_NIL; - if(!Nil(item.first)) - { - result = item.first; - } - else if(!Nil(item.next)) - { - result = item.next; - } - else for(UIItem* i = item.parent; !Nil(i); i = i.parent) - { - if(!Nil(i.next)) - { - result = i.next; - break; - } - } - - return result; -} - -UIItem* -RecurseB(UIItem* item) -{ - UIItem* result = g_UI_NIL; - if(!Nil(item.last)) - { - result = item.last; - } - else if(!Nil(item.prev)) - { - result = item.prev; - } - else for(UIItem* i = item.parent; !Nil(i); i = i.parent) - { - if(!Nil(i.prev)) - { - result = i.prev; - break; - } - } - - return result; -} - -void -BuildItem(UIItem* item, UISize size_x, UISize size_y, UIFlags flags) -{ - UICtx* ctx = GetCtx(); - - item.first = item.last = item.next = item.prev = item.parent = g_UI_NIL; - - item.flags = flags; - item.size_info[A2D.X] = size_x; - item.size_info[A2D.Y] = size_y; - item.level = ctx.panel_level; - item.last_frame = ctx.frame; - item.color = Get!("color")(); - item.layout_axis = Get!("layout_axis")(); - item.adjustment = Get!("adjustment")(); - item.border_color = Get!("border_color")(); - item.border_thickness = Get!("border_thickness")(); - item.corner_radius = Get!("corner_radius")(); - item.edge_softness = Get!("edge_softness")(); - item.highlighted_char = Get!("highlighted_char")(); - item.offset = Get!("offset")(); - item.padding = Get!("padding")(); - item.culling.vec0 = Vec2(0.0); - item.culling.vec1 = Vec2(0.0); - - if(flags & UIF.Window) - { - assert(ctx.root != g_UI_NIL); - auto n = ctx.root; - for(;;) - { - if(Nil(n.next)) - { - n.next = item; - item.prev = n; - break; - } - } - } - else - { - item.parent = ctx.top_parent == g_UI_NIL_NODE ? g_UI_NIL : ctx.top_parent.item; - if(!Nil(item.parent)) - { - DLLPush(item.parent, item, g_UI_NIL); - } - } - - debug ctx.item_count += 1; -} - -void -Signal(UIItem* item) -{ - UICtx* ctx = GetCtx(); - - item.signal &= UIS.BorderX0|UIS.BorderX1|UIS.BorderY0|UIS.BorderY1; - item.dragged = 0.0; - - for(DNode!(InputEvent)* n = ctx.inputs.list.first; !CheckNil(null, n); n = n.next) - { - bool taken = false; - - if(BorderClicked(item, n)) - { - taken = true; - if(item.signal & UIS.DraggedBorder) - { - ctx.drag_item = item; - } - } - - else if(item.flags & UIF.Clickable && Clicked(item, n)) - { - item.last_click_pos = Vec2(n.value.x, n.value.y) - item.rect.vec0; - item.signal |= UIS.Clicked; - taken = true; - } - - else if(item.flags & UIF.Draggable && Clicked(item, n) && Nil(ctx.drag_item)) - { - item.signal |= UIS.Dragged; - ctx.drag_item = item; - taken = true; - } - - else if(ctx.drag_item == item && n.value.key == Input.LeftClick && !n.value.pressed) - { - ctx.drag_item = g_UI_NIL; - item.signal = UIS.None; - taken = true; - } - - else if(ctx.drag_item == item && n.value.key == Input.MouseMotion) - { - item.signal |= (item.flags & (UIF.DragBorder)) ? UIS.DraggedBorder : 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); - } - } -} - UICtx* GetCtx() { @@ -1103,8 +766,7 @@ GetCtx() Vec2 RootSize() { - Vec2 size = GetExtent(&g_ui_ctx.rd); - return size; + return cast(Vec2)GetExtent(&g_ui_ctx.rd); } UIKey @@ -1298,95 +960,7 @@ MakeMultiline(u8[] text, f32 width, TS[] style = []) return node; } -void -DrawPanelDebug(UICtx* ctx, UIItem* item) -{ - if(item.first) - { - u8[] text = item.key.text; - Rect rect = item.rect; - - f32 y = rect.y0 + (rect.y1 - rect.y0) / 3; - f32 x = rect.x0 + (rect.x1 - rect.x0) / 3; - f32 x0 = x; - for(u64 i = 0; i < text.length; i += 1) - { - DrawGlyph(item, &ctx.atlas_buf.atlas.glyphs[text[i]], ctx.atlas_buf.atlas.size/ctx.text_size, &x0, y); - } - - f32 pct = item.parent.layout_axis == A2D.X ? item.size_info[A2D.X].value : item.size_info[A2D.Y].value; - - u8[512] buf = 0; - (cast(char[])buf).sformat("%f%%", pct); - y += 16.0; - for(u64 i = 0; i < buf.length && buf[i] != 0; i += 1) - { - DrawGlyph(item, &ctx.atlas_buf.atlas.glyphs[buf[i]], ctx.atlas_buf.atlas.size/ctx.text_size, &x, y);; - } - } -} - -void -DrawLine(UIItem* item) -{ - UICtx* ctx = GetCtx(); - f32 text_size = Get!("text_size")(); - f32 y = item.rect.y0 + text_size + item.padding.y; - f32 x = item.rect.x0 + item.padding.x; - FontAtlas* atlas = &ctx.atlas_buf.atlas; - - bool edit_mode = EditModeActive(); - - Vertex* v; - if(item.highlighted_char >= 0) - { - v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count; - AddUIIndices(ctx); - } - - for(u64 i = 0; i < item.key.text.length && item.key.text[i] != '\0'; i += 1) - { - u8 ch = item.key.text.ptr[i]; - TS ts = i < item.token_styles.length ? item.token_styles[i] : TS.None; - if(ch < 128) - { - DrawGlyph(item, &atlas.glyphs[ch], atlas.size/ctx.text_size, &x, y, !edit_mode && i == item.highlighted_char, ts); - } - } - - if(v) - { - y = item.rect.y0 + 2; - x = item.rect.x0; - - f32 adv = atlas.glyphs[' '].advance; - for(u64 i = 0; i < item.highlighted_char; i += 1) - { - if(item.key.text[i] == '\t') - { - x += adv*2; - } - else - { - x += adv; - } - } - - v.dst_start.x = x; - v.dst_start.y = y; - v.dst_end.x = x + adv; - v.dst_end.y = y + ctx.text_size - 2; - v.cols = Vec4(1.0); - - if(edit_mode) - { - v.dst_end.x = v.dst_start.x + 2; - } - - AddUIIndices(ctx); - } -} - +/* pragma(inline) bool CullText(UIItem* item, GlyphBounds* gb) { @@ -1438,6 +1012,7 @@ CullText(UIItem* item, GlyphBounds* gb) return skip; } +*/ void DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, Vec4 col) @@ -1517,76 +1092,6 @@ DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y) return v; } -pragma(inline) void -DrawGlyph(UIItem* item, Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool highlight = false, TokenStyle ts = TS.None, Vec4 col = Vec4(1.0)) -{ - UICtx* ctx = GetCtx(); - Vertex* v = null; - - if(glyph.ch == '\t') - { - *x_pos += glyph.advance * (GetCtx().tab_width - 1); - } - - f32 r = glyph.plane_right * scale; - f32 l = glyph.plane_left * scale; - f32 t = glyph.plane_top * scale; - f32 b = glyph.plane_bottom * scale; - - GlyphBounds gb = { - r: r, - l: l, - t: t, - b: b, - w: r - l, - h: b - t, - atlas_r: glyph.atlas_right, - atlas_l: glyph.atlas_left, - atlas_t: glyph.atlas_top, - atlas_b: glyph.atlas_bottom, - }; - - //bool skip = false; - bool skip = CullText(item, &gb); - - if(!skip) - { - f32 y_pos = t; - - v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count; - - v.dst_start.x = *x_pos + gb.l; - v.dst_start.y = y - y_pos; - v.dst_end.x = *x_pos + gb.w + gb.l; - v.dst_end.y = y + gb.h - y_pos; - - if(glyph.ch != '\t' && glyph.ch != '\n') - { - v.src_start.x = gb.atlas_l; - v.src_start.y = gb.atlas_t; - v.src_end.x = gb.atlas_r; - v.src_end.y = gb.atlas_b; - } - - if(highlight) - { - col = Vec4(Vec3(1.0)-col.xyz, 1.0); - } - else if(ts != TS.None) - { - col = SYNTAX_COLORS[ts]; - } - - v.cols = col; - - v.texture = 1; - - AddUIIndices(ctx); - } - - *x_pos += glyph.advance * scale; -} - pragma(inline) Vertex* GetVertex(UICtx* ctx) { @@ -1607,22 +1112,30 @@ GetGlyph(u8 ch) return ch < a.glyphs.length ? a.glyphs.ptr + ch : a.glyphs.ptr; } -void +pragma(inline) void DrawRect(f32 x, f32 y, FontAtlas* atlas, u8 ch, Vec4 col, bool edit_mode) { - Glyph* g = ch < atlas.glyphs.length ? atlas.glyphs.ptr + ch : atlas.glyphs.ptr; + Glyph* g = GetGlyph(ch); Vec4[4] cols = col; DrawRect(Vec2(x, y-TEXT_SIZE*0.8), Vec2(edit_mode ? 1.0 : g.advance, TEXT_SIZE), 0.0, 0.0, cols); } -void +pragma(inline) void DrawRect(Vec2 pos, Vec2 size, f32 corner_radius, f32 border, Vec4[4] cols) +{ + DrawRect(pos.x, pos.y, size.x, size.y, corner_radius, border, cols); +} + +void +DrawRect(f32 x, f32 y, f32 w, f32 h, f32 corner_radius, f32 border, Vec4[4] cols) { UICtx* ctx = GetCtx(); Vertex* v = GetVertex(ctx); - v.dst_start = pos; - v.dst_end = pos + size; + v.dst_start.x = x; + v.dst_start.y = y; + v.dst_end.x = x+w; + v.dst_end.y = y+h; v.cols = cols; v.corner_radius = corner_radius; @@ -1654,45 +1167,6 @@ DrawBorder(Vec2 pos, Vec2 size, f32 border, f32 radius, f32 softness, Vec4[4] co AddUIIndices(ctx); } -pragma(inline) void -DrawRect(UICtx* ctx, UIItem* item) -{ - // Y reversed - 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.color; - v.border_thickness = 0.0; - v.corner_radius = item.corner_radius; - v.edge_softness = 0.0; - v.raised = 0.0; - - if(item.border_thickness > 0.0) - { - v.dst_start.x += item.border_thickness; - v.dst_start.y += item.border_thickness; - v.dst_end.x -= item.border_thickness; - v.dst_end.y -= item.border_thickness; - } - - 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) { @@ -1712,27 +1186,6 @@ 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_buf.atlas.glyphs.length) - { - f32 ch_w = GlyphWidth(ctx.atlas_buf.atlas.glyphs.ptr + ch); - if(ch_w + w > item.last_click_pos.x) - { - //item.clicked_char_idx = i; - break; - } - w += ch_w; - } - } -} - pragma(inline) bool InBounds(InputEvent* ev, Vec2 p0, Vec2 p1) { @@ -1800,67 +1253,6 @@ Dragged(UIItem* item, Vec2 p0, Vec2 p1) return result; } -bool -Clicked(UIItem* item, DNode!(InputEvent)* n) -{ - bool result = false; - InputEvent* ev = &n.value; - - if( - ev.key == Input.LeftClick && - ev.x >= item.rect.x0 && - ev.x <= item.rect.x1 && - ev.y >= item.rect.y0 && - ev.y <= item.rect.y1 && - ev.pressed - ) - { - result = true; - } - - return result; -} - -bool -BorderClicked(UIItem* item, DNode!(InputEvent)* n) -{ - InputEvent* ev = &n.value; - bool taken = false; - - if(item.flags & (UIF.ClickBorder | UIF.DragBorder) && ev.key == Input.LeftClick && ev.pressed) - { - taken = true; - - UISignal signal = (item.flags & UIF.DragBorder) ? UIS.DraggedBorder : UIS.ClickedBorder; - - f32 x0 = item.rect.x0; - f32 x1 = item.rect.x1; - f32 y0 = item.rect.y0; - f32 y1 = item.rect.y1; - f32 b = item.border_thickness*2.0; - - if(item.flags & UIF.BorderX0 && ev.x >= x0 && ev.x <= x0+b) - { - item.signal |= (signal | UIS.BorderX0); - } - else if(item.flags & UIF.BorderX1 && ev.x <= x1 && ev.x >= x1-b) - { - item.signal |= (signal | UIS.BorderX1); - } - else if(item.flags & UIF.BorderY0 && ev.y >= y0 && ev.y <= y0+b) - { - item.signal |= (signal | UIS.BorderY0); - } - else if(item.flags & UIF.BorderY1 && ev.y <= y1 && ev.y >= y1-b) - { - item.signal |= (signal | UIS.BorderY1); - } - else taken = false; - } - - return taken; -} - u8[] ScratchName(u64 num_len, u8[] base, u64 iteration) { @@ -1899,12 +1291,6 @@ ScratchName(u8[] base, string append) return ScratchName(base, u8_append); } -void -SetFocus(UIItem* item) -{ - g_ui_ctx.focused = item; -} - unittest { { // UI Key diff --git a/src/editor/widgets.d b/src/editor/widgets.d deleted file mode 100644 index 7be2221..0000000 --- a/src/editor/widgets.d +++ /dev/null @@ -1,986 +0,0 @@ -import dlib; - -import vulkan; -import buffer; -import ui : Nil; -import ui; -import parsing; -import editor; -import std.format : sformat; -import std.math.rounding : ceil, floor; -import std.math.exponential : pow; -import core.stdc.math : fabsf; -import std.conv; -import core.stdc.stdio : sprintf; - -const Vec4[4] DEFAULT_COL = Vec4(0.13, 0.13, 0.13, 1.0); - -const Vec4[4] CMD_PALETTE_COL = Vec4(0.21, 0.21, 0.21, 1.0); - -const Vec4[4] DEFAULT_BORDER_COL = Vec4(0.254, 0.254, 0.266, 1.0); - -const Vec4[4] HL_BORDER_COL = Vec4(0.035, 0.549, 0.824, 1.0); - -const Vec4[4] LC_COLOR = Vec4(0.12, 0.12, 0.12, 1.0); - -const Vec4[4] LC_HL_COLOR = Vec4(0.012, 0.176, 0.29, 1.0); - -const Vec4[4] CMD_PALETTE_INPUT_HL = Vec4(0.24, 0.45, 0.81, 1.0); - -const Vec4[4] CMD_INPUT_BORDER_COL = Vec4(0.003, 0.48, 0.68, 1.0); - -const Vec4[4] CMD_INPUT_BORDER_HL = Vec4(0.05, 0.56, 0.76, 1.0); - -const Vec4[4] CMD_PALETTE_INPUT_COL = [ - Vec4(0.14, 0.14, 0.14, 1.0), - Vec4(0.14, 0.14, 0.14, 1.0), - Vec4(0.17, 0.17, 0.17, 1.0), - Vec4(0.17, 0.17, 0.17, 1.0), -]; - -// TODO: add setting -const f32 TEXT_SIZE = 16.0; - -const UIPanel g_ui_nil_panel; -UIPanel* g_UI_NIL_PANEL; - -UIPanel g_root_panel; - -WidgetCtx g_widget_ctx; - -struct WidgetCtx -{ - UIPanel* parent; - UIPanel* focused_panel; -} - -struct UIPanel -{ - AnimState anim; - u8[] id; - Editor* ed; - - UIPanel* parent; - UIPanel* next; - UIPanel* prev; - UIPanel* first; - UIPanel* last; - - UIPanel* list_next; - - Axis2D axis; - SizeType size_type; - f32 pct; - Vec4 color; - - i64 prev_offset; - i64 start_row; - i64 end_row; - - Rect rect; - Vec2 size; -} - -struct AnimState -{ - f32 start_value; - f32 end_value; - f32 start_time; - f32 remaining_time; -} - -struct TextPart -{ - UIItem* item; - TextPart* next; - u32 count; -} - -UIItem* -Root() -{ - Vec2 d = RootSize(); - - Push!("color")(DEFAULT_COL); - Push!("layout_axis")(A2D.Y); - - 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; -} - -void -BeginUI(EditorCtx* edctx, Inputs* inputs) -{ - UICtx* ctx = GetCtx(); - - Reset(&ctx.temp_arena); - - ctx.f_idx = ctx.frame%FRAME_OVERLAP; - ctx.inputs = inputs; - - PrepRendering(ctx); - - SetPanelSizes(edctx.base_panel); -} - -void -EndUI() -{ - UICtx* ctx = GetCtx(); - - with(ctx) - { - BindBuffers(&rd, &buffers[f_idx].m_idx, &buffers[f_idx].m_vtx); - DrawIndexed(&rd, 6, buffers[f_idx].count, 0); - } - - CompleteRendering(ctx); - - ctx.frame += 1; -} - -void -SetPanelSizes(UIPanel* panel) -{ - UICtx* ctx = GetCtx(); - - panel.size.x = ctx.res.x; - panel.size.y = ctx.res.y; - panel.rect.vec0 = 0.0; - panel.rect.vec1 = 0.0; - - static foreach(axis; A2D.min .. A2D.max) - { - for(UIPanel* p = panel; !Nil(p); p = Recurse(p)) - { - f32 pos = p.rect.vec0.v[axis]; - for(UIPanel* c = p.first; !Nil(c); c = c.next) - { - c.size.v[axis] = p.axis == axis ? c.pct * p.size.v[axis] : p.size.v[axis]; - c.rect.vec0.v[axis] = pos; - c.rect.vec1.v[axis] = pos + c.size.v[axis]; - - if(axis == p.axis) - { - pos += c.size.v[axis]; - } - } - } - } -} - -pragma(inline) void -DrawChar(FontAtlas* atlas, u8 ch, f32* x_pos, f32 y, Vec4 col = Vec4(1.0)) -{ - if(ch < atlas.glyphs.length) - { - Glyph* g = atlas.glyphs.ptr + ch; - DrawGlyph(g, 1.0, x_pos, y, col); - } -} - -void -Panel2(UIPanel* panel) -{ - UICtx* ctx = GetCtx(); - UIItem* item = Get(panel.id); - Editor* ed = panel.ed; - bool focused = GetFocusedPanel() == panel; - - UIPanel* parent = panel.parent; - UIPanel* prev = panel.prev; - - Axis2D pax = parent.axis; - - Vec2 adj = Vec2( - parent.axis == A2D.X ? 10 : 0, - parent.axis == A2D.Y ? 10 : 0 - ); - - Vec2 p0 = panel.rect.vec0-adj; - Vec2 p1 = panel.rect.vec1+adj; - - if(!Nil(prev)) with(panel) - { - if(Dragged(item, p0, p1) && item.dragged.v[pax] != 0.0) - { - f32 mov_pct = Remap(item.dragged.v[pax], 0.0, size.v[pax], 0.0, 1.0); - if(CheckPanelBounds(pct + mov_pct) && CheckPanelBounds(panel.prev.pct - mov_pct)) - { - pct += mov_pct; - panel.prev.pct -= mov_pct; - } - } - } - - if(panel.ed != null) - { - if(Clicked(item, p0, p1)) - { - SetFocusedPanel(panel); - } - - DrawBorderedRect(panel.rect.vec0, panel.size, 2.0, 2.0, 0.1, DEFAULT_COL, focused ? HL_BORDER_COL : DEFAULT_BORDER_COL); - - i64 rows = cast(i64)ceil(panel.size.y/TEXT_SIZE); - - GetLines(&ed.buf, &ed.linebufs, rows); - - char[64] ch_buf = '\0'; - char[] fmt = ch_buf.sformat("%%0%ss", u64(ed.linebufs.end.toChars().length)); - - u8[] end_line_text = ScratchAlloc!(u8)(ed.linebufs.end.toChars().length, '0'); - - f32 lcw = CalcTextWidth(end_line_text); - f32 padding = 4.0; - - DrawRect(panel.rect.vec0, panel.rect.vec1+Vec2(lcw+padding*2, panel.size.y), 0.0, 0.0, focused ? LC_HL_COLOR : LC_COLOR); - - f32 x = panel.rect.x0; - f32 y = panel.rect.y0 + TEXT_SIZE; - - FontAtlas* atlas = &ctx.atlas_buf.atlas; - bool edit = EditModeActive(); - U64Vec2 pos = VecPos(&ed.buf); - u64 i; - for(auto buf = ed.linebufs.first; buf != null; buf = buf.next, i += 1) - { - f32 x_pos = x + padding; - - DrawLineCount(atlas, fmt, &x_pos, y, ed.linebufs.start+i); - - x_pos += padding; - - u64 ch_offset; - auto parts = MakeMultiline(buf.text, panel.size.x-lcw, buf.style); - for(auto n = parts; n != null; n = n.next) - { - auto l = &n.value; - - if(pos.y == i) - { - DrawCursor(atlas, l.text, pos.x, x+lcw+padding*2.0, y, ch_offset, edit); - } - - foreach(j; 0 .. l.text.length) - { - bool hl = pos.y == i && !edit && j == pos.x-ch_offset; - DrawChar(atlas, l.text[j], &x_pos, y, hl ? Vec4(Vec3(0.0), 1.0) : SYNTAX_COLORS[l.style[j]]); - } - - y += TEXT_SIZE; - x_pos = x + lcw + padding*2.0; - ch_offset += l.text.length; - } - } - } -} - -pragma(inline) void -DrawCursor(FontAtlas* atlas, u8[] text, u64 ch_x, f32 x, f32 y, u64 offset, bool edit) -{ - Glyph* g = GetGlyph(' '); - foreach(j; 0 .. text.length) - { - bool hl = j == ch_x-offset; - if(hl) - { - break; - } - - g = j == text.length-1 ? GetGlyph(' ') : GetGlyph(text[j]); - x += GlyphWidth(g); - } - - DrawRect(x, y, atlas, cast(u8)g.ch, Vec4(1.0), edit); -} - -pragma(inline) void -DrawLineCount(FontAtlas* atlas, char[] fmt, f32* x_pos, f32 y, u64 line) -{ - char[32] line_buf = '\0'; - char[] line_str = sformat(line_buf, fmt, line); - - foreach(j; 0 .. line_str.length) - { - DrawChar(atlas, line_str[j], x_pos, y, Vec4(1.0)); - } -} - -f32 -LineCounter2(UIPanel* panel, i64 start_row, i64 end_row) -{ - UICtx* ctx = GetCtx(); - - f32 lc_width = 0.0; - f32 padding = 4.0; - - UIItem* item = Get(ScratchName(panel.id, "linec")); - - u8[] max_line_text; - u8[][] line_counts = ScratchAlloc!(u8[])(end_row-start_row); - - u64 width = u64(end_row.toChars().length); - - char[32] buf = '\0'; - char[] fmt = buf.sformat("%%0%sllu", width); - - if(line_counts.length > 0) - { - for(u64 i = 0; start_row+i < end_row; i += 1) - { - line_counts[i] = ScratchAlloc!(u8)(width); - sprintf(cast(char*)line_counts[i].ptr, fmt.ptr, start_row+i); - } - - max_line_text = ScratchAlloc!(u8)(width, '0'); - } - - lc_width = CalcTextWidth(max_line_text) + padding*2.0; - - f32 x = panel.rect.x0 + padding; - f32 y = panel.rect.y0 + TEXT_SIZE; - - foreach(i; 0 .. line_counts.length) - { - f32 x_pos = x; - - foreach(j; 0 .. line_counts[i].length) - { - DrawChar(&ctx.atlas_buf.atlas, line_counts[i][j], &x_pos, y); - } - - y += TEXT_SIZE; - } - - return lc_width; -} - -UIItem* -Panel(UIPanel* panel, UIFlags flags = UIF.None) -{ - Push!("color")(DEFAULT_COL); - Push!("layout_axis")(panel.axis); - - UIItem* item = Get(panel.id); - UIItem* separator = g_UI_NIL; - - UIPanel* parent_pn = panel.parent; - - UIItem* next = !Nil(panel.next) ? Get(panel.next.id) : g_UI_NIL; - UIItem* prev = !Nil(panel.prev) ? Get(panel.prev.id) : g_UI_NIL; - UIItem* parent = !Nil(parent_pn) ? Get(parent_pn.id) : PeekParent(); - - f32 x_pct = 1.0, y_pct = 1.0; - if(!Nil(parent_pn)) - { - x_pct = parent_pn.axis == A2D.X ? panel.pct : 1.0; - y_pct = parent_pn.axis == A2D.Y ? panel.pct : 1.0; - } - - if(item.signal & UIS.Clicked) - { - SetFocusedPanel(panel); - } - - f32 adj_x = 0.0, adj_y = 0.0; - if(!Nil(prev) || !Nil(next)) - { - A2D p_axis = parent_pn.axis; - UIS prev_signal = p_axis == A2D.X ? UIS.BorderX0 : UIS.BorderY0; - UIS next_signal = p_axis == A2D.X ? UIS.BorderX1 : UIS.BorderY1; - - if(p_axis == A2D.X) - { - flags |= !Nil(prev) ? UIF.BorderX0 : UIF.None; - flags |= !Nil(next) ? UIF.BorderX1 : UIF.None; - } - else - { - flags |= !Nil(prev) ? UIF.BorderY0 : UIF.None; - flags |= !Nil(next) ? UIF.BorderY1 : UIF.None; - } - - Signal(item); - - f32 p_start = parent.rect.vec0.v[p_axis]; - f32 p_end = parent.rect.vec1.v[p_axis]; - f32 pos = item.dragged.v[p_axis]; - if(!Nil(prev) && item.signal & UIS.DraggedBorder && item.signal & prev_signal && pos != 0.0) - { - f32 pct = Remap(pos, 0.0, p_start-p_end, 0.0, 1.0); - if(CheckPanelBounds(panel.pct - pct) && CheckPanelBounds(panel.prev.pct + pct)) - { - panel.pct -= pct; - panel.prev.pct += pct; - } - } - - else if(!Nil(next) && item.signal & UIS.DraggedBorder && item.signal & next_signal && pos != 0.0) - { - f32 pct = Remap(pos, 0.0, p_start-p_end, 0.0, 1.0); - if(CheckPanelBounds(panel.next.pct - pct) && CheckPanelBounds(panel.pct + pct)) - { - panel.next.pct -= pct; - panel.pct += pct; - } - } - } - - BuildItem(item, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground|UIF.DragBorder|flags); - - PushParent(item); - - Pop!("color", "layout_axis"); - - return item; -} - -UIItem* -LineCounter(u8[] parent_id, FlatBuffer* buf, f32 offset, i64 start_row, i64 end_row, bool focused) -{ - f32 padding = 4.0; - - Push!("padding")(Vec2(padding, 0.0)); - Push!("color")(focused ? LC_HL_COLOR : LC_COLOR); - - u8[] id = ScratchName(parent_id, "linec"); - u8[] max_row_text; - u8[][] line_counts = ScratchAlloc!(u8[])(end_row-start_row); - - u64 width = u64(end_row.toChars().length); - - if(line_counts.length > 0) - { - for(u64 i = 0; start_row+i < end_row; i += 1) - { - line_counts[i] = ScratchName(width, parent_id, start_row+i); - } - - max_row_text = ScratchAlloc!(u8)(width); - max_row_text[] = cast(u8)'0'; - } - - UISize s_x = UISize(ST.Pixels, CalcTextWidth(max_row_text) + (padding*2.0)); - UISize s_y = UISize(ST.Percentage, 1.0); - - UIItem* item = Container(ScratchName(parent_id, "linec"), s_x, s_y, A2D.Y, UIF.DrawBackground); - { - UIItem* inner = Container(ScratchName(parent_id, "lcinner"), s_x, s_y, A2D.Y, UIF.None); - { - for(u64 i = 0; i < line_counts.length; i += 1) - { - if(i == 0) - { - Push!("offset")(Vec2(0.0, offset)); - UIItem* line = Text(line_counts[i]); - Pop!("offset"); - } - else - { - UIItem* line = Text(line_counts[i]); - } - } - } - EndContainer(); - } - EndContainer(); - - Pop!("padding", "color"); - - return item; -} - -UIItem* -Container(u8[] text, UISize size_x, UISize size_y, Axis2D axis, UIF flags = UIF.None) -{ - Push!("layout_axis")(axis); - - UIItem* item = Get(text); - BuildItem(item, size_x, size_y, flags); - PushParent(item); - - Pop!("layout_axis"); - - return item; -} - -void -EndContainer() -{ - PopParent(); -} - -UIItem* -Text(u8[] text) -{ - UIItem* item = Get(text); - f32 width = CalcTextWidth(item.key.text); - BuildItem(item, UISize(ST.Pixels, width), UISize(ST.Pixels, 16.0), UIF.DrawText); - return item; -} - -void -SetPanelScroll(UIPanel* panel, i64 rows, f32 text_size) -{ - i64 low = panel.prev_offset < panel.ed.buf.offset ? panel.prev_offset : panel.ed.buf.offset; - i64 high = panel.prev_offset < panel.ed.buf.offset ? panel.ed.buf.offset + rows : panel.prev_offset + rows; - - panel.start_row = low; - panel.end_row = high; - - f32 start = panel.anim.start_value; - f32 end = panel.anim.end_value; - - f32 offset = (panel.prev_offset-panel.ed.buf.offset) * text_size; - if(offset > 0.0) - { - end = -offset; - } - else - { - start = offset; - } - - with(panel.anim) - { - start_value = start; - end_value = end; - - start_time = 0.1; - remaining_time = remaining_time > 0.05 ? 0.04 : 0.1; - - panel.prev_offset = panel.ed.buf.offset; - } -} - -f32 -EaseOutExp(f32 v) -{ - return fabsf(1.0 - v) <= 0.00009 ? 1.0 : 1.0 - pow(2, -10 * v); -} - -void -EditorView(UIPanel* panel) -{ - bool focused = panel == GetFocusedPanel(); - - UICtx* ctx = GetCtx(); - Editor* ed = panel.ed; - - Push!("border_thickness")(2.0); - Push!("border_color")(focused ? HL_BORDER_COL : DEFAULT_BORDER_COL); - - UIItem* item = Panel(panel, UIF.Clickable|UIF.DeferredBorder); - { - Pop!("border_thickness", "border_color"); - - Container(ScratchName(panel.id, "cntr"), UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0), A2D.X); - { - f32 text_size = 16.0; - f32 height = 0.0; - - i64 rows = cast(i64)ceil(item.size.y/text_size); - if(ed.buf.rows != rows) - { - ed.buf.rows = rows; - } - - if(panel.prev_offset != ed.buf.offset) - { - SetPanelScroll(panel, rows, text_size); - } - - f32 offset = 0.0; - i64 line_offset = ed.buf.offset; - i64 line_rows = rows; - if(panel.anim.remaining_time > 0.0) with(panel.anim) - { - remaining_time -= g_delta; - if(remaining_time < 0.0) - { - remaining_time = 0.0; - } - - offset = Remap(remaining_time, 0.0, start_time, 0.0, 1.0); - offset = Remap(EaseOutExp(offset), 0.0, 1.0, start_value, end_value); - - line_offset = panel.start_row; - line_rows = panel.end_row-panel.start_row; - GetLines(&ed.buf, &ed.linebufs, panel.start_row, panel.end_row-panel.start_row); - - if(remaining_time == 0.0) - { - start_value = end_value = start_time = 0.0; - panel.start_row = panel.end_row = -1; - } - } - else - { - GetLines(&ed.buf, &ed.linebufs, rows); - } - - UIItem* lc = LineCounter(panel.id, &panel.ed.buf, offset, line_offset, line_offset+line_rows, focused); - - Container(ScratchName(panel.id, "lines"), UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0), A2D.Y); - { - I64Vec2 sel = ed.buf.sel; - if(sel.x > sel.y) - { - i64 t = sel.y; - sel.y = sel.x; - sel.x = t; - } - - U64Vec2 pos = VecPos(&ed.buf); - SelectMode sm = ed.buf.sel_mode; - u64 i = 0; - for(LineBuffer* buf = ed.linebufs.first; buf != null; buf = buf.next, i += 1) - { - if(buf == ed.linebufs.first) - { - Push!("offset")(Vec2(0.0, offset)); - } - - if(buf.text.length > 0) - { - i64 line_no = i+line_offset; - - if(sm == SM.Line && line_no >= sel.x && line_no <= sel.y) - { - } - - TextPart* tp = WrappedTextLine(buf.text, panel.id, text_size, line_no, buf.style, &pos, focused); - height += (text_size * tp.count); - if(TextClicked(tp)) - { - SetFocusedPanel(panel); - } - } - - if(buf == ed.linebufs.first) - { - Pop!("offset"); - } - } - } - EndContainer(); - } - EndContainer(); - } - EndPanel(); - - if(item.signal & UIS.Clicked) - { - SetFocusedPanel(panel); - } -} - -bool -TextClicked(TextPart* text_part) -{ - bool result = false; - - alias result res; - - for(TextPart* tp = text_part; !Nil(tp.item); tp = tp.next) - { - if(tp.item.signal & UIS.Clicked) - { - res = true; - break; - } - } - - return res; -} - -TextPart* -WrappedTextLine(u8[] text, u8[] parent_id, f32 text_size, u64 line_no, TS[] style = [], U64Vec2* hl_pos, bool focused) -{ - /* - Push!("color")(Vec4(1.0)); - Push!("text_size")(text_size); - - UIItem* parent = PeekParent(); - TextPart* part = ScratchAlloc!(TextPart)(); - part.item = g_UI_NIL; - - i64 hl = -1; - if(hl_pos.y == line_no) - { - hl = hl_pos.x; - } - - f32 parent_width = parent.size.x - (parent.border_thickness * 2); - Logf("w %f", parent_width); - - u64 chars; - TextPart* tp = part; - Node!(TextBuffer)* lines = MakeMultiline(text, parent_width, parent_id, line_no, style); - for(Node!(TextBuffer)* line = lines; line != null; line = line.next, tp = tp.next) - { - part.count += 1; - - tp.item = Get(line.value.text); - tp.next = ScratchAlloc!(TextPart)(); - tp.next.item = g_UI_NIL; - - tp.item.token_styles = line.value.style; - - if(focused && hl >= 0) - { - if(hl <= tp.item.key.text.length) - { - Push!("highlighted_char")(hl); - hl = -1; - } - else - { - hl -= tp.item.key.text.length; - } - } - - Signal(tp.item); - - if(tp.item.signal & UIS.Clicked) - { - ClickedCharIndex(tp.item); - } - - BuildItem(tp.item, UISize(ST.Percentage, 1.0), UISize(ST.Pixels, text_size), UIF.DrawText|UIF.Clickable|UIF.Draggable); - - Push!("highlighted_char")(-1); - } - - Pop!("color"); - - return part; - */ - return null; -} - -UIItem* -TextInput(u8[] text, u8[] hash, f32 x, f32 y, f32 w_pct, f32 h, f32 text_size, Vec4[4] bg_cols, Vec4[4] border_col) -{ - UIItem* input = Get(ScratchName(text, hash)); - - Push!("padding")(Vec2(6.0, (h-(text_size))*0.5)-2.0); - - Push!("offset")(Vec2(x, y)); - Push!("border_color")(border_col); - Push!("color")(bg_cols); - - Container(ScratchName(hash, "_cntr"), UISize(ST.Percentage, w_pct), UISize(ST.Pixels, h), A2D.Y, UIF.DrawBackground|UIF.DrawBorder); - - Pop!("color", "offset", "border_color"); - - Push!("color")(Vec4(1.0)); - Push!("text_size")(text_size); - - BuildItem(input, UISize(ST.Percentage, 1.0), UISize(ST.Pixels, text_size), UIF.DrawText); - - EndContainer(); - - Pop!("color", "padding"); - - return input; -} - -void -CommandPalette(CmdPalette* cmd) -{ - u8[] text = cmd.buffer[0 .. cmd.icount]; - u8[][] options = cmd.opt_strs; - - Vec2 size = RootSize(); - - f32 x = size.x*0.15; - f32 y = size.y*0.1; - f32 w = size.x*0.7; - f32 h = size.y*0.8; - - Push!("color")(CMD_PALETTE_COL); - - UIItem* window = Window(CastStr!(u8)("##cmd_palette"), x, y, w, 40.0*(options.length+1)); - - Push!("corner_radius")(2.0); - Push!("edge_softness")(0.1); - Push!("border_thickness")(2.0); - - UIItem* text_box = TextInput(text, CastStr!(u8)("###cmd_palette_input"), 0.0, 0.0, 1.0, 40.0, 16.0, CMD_PALETTE_INPUT_COL, CMD_INPUT_BORDER_COL); - - for(u64 i = 0; i < options.length; i += 1) - { - TextInput( - options[i], - ScratchName("###cmd%04s", 10, i), - 0.0, - 0.0, - 1.0, - 40.0, - 16.0, - i == cmd.selected ? CMD_PALETTE_INPUT_HL : CMD_PALETTE_INPUT_COL, - i == cmd.selected ? CMD_INPUT_BORDER_HL : CMD_INPUT_BORDER_COL - ); - } - - EndWindow(); - - Pop!("border_thickness", "corner_radius", "edge_softness", "color"); -} - -UIItem* -Window(u8[] id, f32 x, f32 y, f32 w, f32 h) -{ - UIItem* item = Get(id); - - Push!("offset")(Vec2(x, y)); - - BuildItem(item, UISize(ST.Pixels, w), UISize(ST.Pixels, h), UIF.Window|UIF.DrawBackground|UIF.DrawBorder); - - Pop!("offset"); - - PushParent(item); - - return item; -} - -void -EndWindow() -{ - PopParent(); -} - -void -EndPanel() -{ - PopParent(); -} - -void -DrawPanels(UIPanel* panel) -{ - if(!Nil(panel)) - { - debug - { - import core.stdc.math : fabsf; - - if(!Nil(panel.first)) - { - f32 total = 1.0; - for(UIPanel* child = panel.first; !Nil(child); child = child.next) - { - total -= child.pct; - } - - if(total > 0.00009) - { - Logf("%s failed with %f", cast(char[])panel.id, total); - assert(false); - } - } - } - - if(panel.ed) - { - EditorView(panel); - } - else - { - Panel(panel); - } - - DrawPanels(panel.first); - - if(panel.ed == null) - { - EndPanel(); - } - - DrawPanels(panel.next); - } -} - -UIPanel* -Recurse(UIPanel* panel) -{ - UIPanel* result = g_UI_NIL_PANEL; - if(!Nil(panel.first)) - { - result = panel.first; - } - else if(!Nil(panel.next)) - { - result = panel.next; - } - else for(UIPanel* p = panel.parent; !Nil(p); p = p.parent) - { - if(!Nil(p.next)) - { - result = p.next; - break; - } - } - - return result; -} - -bool -CheckPanelBounds(f32 pct) -{ - return pct >= 0.0 && pct <= 1.0; -} - -void -PushPanel(UIPanel* parent, UIPanel* panel) -{ - DLLPush(parent, panel, g_UI_NIL_PANEL); - panel.parent = parent; -} - -void -InsertPanel(UIPanel* parent, UIPanel* prev, UIPanel* panel) -{ - DLLInsert(parent, panel, prev, g_UI_NIL_PANEL); - panel.parent = prev.parent; -} - -void -InitWidgets() -{ - g_UI_NIL_PANEL = cast(UIPanel*)&g_ui_nil_panel; - g_widget_ctx.parent = g_UI_NIL_PANEL; -} - -void -SetFocusedPanel(UIPanel* panel) -{ - if(!CheckNil(g_UI_NIL_PANEL, panel)) - { - g_widget_ctx.focused_panel = panel; - SetFocus(Get(panel.id)); - } -} - -UIPanel* -GetFocusedPanel() -{ - return Nil(g_widget_ctx.focused_panel) ? g_UI_NIL_PANEL : g_widget_ctx.focused_panel; -} - -bool -Nil(UIPanel* panel) -{ - return panel == null || panel == g_UI_NIL_PANEL; -}