From 54619b52d306f75e553504b082828ceb243faf45 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sat, 27 Sep 2025 17:34:22 +1000 Subject: [PATCH] add command palette --- assets/gui.frag.spv | Bin 5664 -> 5160 bytes assets/gui.vert.spv | Bin 6244 -> 6676 bytes readme.md | 4 +- src/editor/editor.d | 198 ++++++++++++++++++++++---- src/editor/ui.d | 180 ++++++++++++++++++++--- src/editor/widgets.d | 290 +++++++++++++++++++++++--------------- src/shaders/gui.frag.glsl | 54 +++---- src/shaders/gui.vert.glsl | 15 +- 8 files changed, 549 insertions(+), 192 deletions(-) diff --git a/assets/gui.frag.spv b/assets/gui.frag.spv index 8da6167e938f635834479cb65b578df2ed23e778..642d42651cc396d2f5805f4deb9ae4f677ecf526 100644 GIT binary patch literal 5160 zcmZve`*&Pb6~}Ly%rvy6lxKOk%7iMmf)yw&k6K!2+O!&JB}nl(+-4@}Kr%DTOaevF zVnr%%5ET(CqLylXp!h)WLB${a5Bxh^K9;)td?t5iZbwhnw|npJ-e;eE&e>;f2bZi} zniK|-<;j`JpOS@fcCsWHKwX{`yK-!NVthlpQ`vCsrmKutnGALVjX5h>kqjZlY+9@9 zSPPy5FM?y>E$|2M4p_xpW0!(K{+1yBOrjyy8XGU~yz};QrFvj)y4-Bl>YXY0W%6Tt z_l}M4EKfF@<#y-LOtm~yt5?gFTB|zQsm)iNKb(uJ)hB1>D*1o{5&0SmRIrk@ZzI9P z=&p71Q`c+}Q&(?E3e>e6U$fChFR;TTIhf8&mD{xkSPEu{(@K(7TB*&owF}9m*zLwt zr(SKtEN~;pU9EI_H0`8&IGG}Ga*oNyOrwP?l1q*wNap5yF`i4M-6>l`kw0TRb-BrE zy;Eg!kv+(}FZq0p;Va;4S13K5ZE|d;Tbx(KH`!>_tF3ZuWpORPleHgx#*4ovI zd4{{|p8 zYxVi^bUHhmqFdkN$evwDp2F@_?=Q!lyITdQfS+U_i@bDY>-&O&k$yD>+9WW8lR-=00VBggp`M_cKAduG$=g?=t1 zr{H!p>g`Ti@7TchoSdWCYTWHRYtX~K4d3y;9Z53AUe}_p0RvzRpZoG%=~LI|&~|Sl zU8`qbhP^O%sJl=7d0W53`R*m3&wb?cY4_p>yY>5yb~E=B(&2m9&A(yW=d1cS_vAR2 zUnl2}u&+U+`UPmdn-W+}UgvA;D`ERSr*Q)^_?yr@_prC2yD#h9jM}{!HctCk#$7_MzKm{;INMjy zqb~3KRp2n!38cOfh`L`xxA*nGxu`GHr0tv3@4kMA{D1rMZ3uJx9_cXFdbNGGjtjBh zMwWp6v4+g2kZ%L${t;<^$vId1os8Q|hq;FH9M<%A82ipx=UocyGi~4A`kbxbZ&3KP zx8`i){qBTc+wV@;#{1pL+m$}s?@jof@3$sz`>hGvd=KU9N70Y=*?wmtUccX&ynVdS z_WPj!W=`dDzW1TaJ;HoruLN@bE*#0Y4H@s7S;1IX7bGa~iXc8rho?+XCe5H+6Gw@5+VT+tAHzZMS56tiJ=h z{|-js_ZTq0zR15Dy?=ju;N&CMhtTCj1OiY^~9d(r#X+Xp8fJ@Qd>=gCKp+=d?aso%3U=XT`nz?^%%+IR|4aW({G&bTwm^%-57n7_*@Z29Q>X>>VjKfyY_g@Zue8tz6qtUu4e!)?rKo$XSPa^id1lI`Z$ajV&Lw@?Y!!sdWxc zKJI=VUC#TDH*_Dmyofh+Kf3w#MGX(2TSLB=KZ`BzJsm=7t3QY|N7VK?^!_{hJe<7o zelN76-w&hP@8&d@e&g;%9tM&3Vf6m{`2w7L?D+_~`<0I#{UW-#qTe4u9{|zs&M_w5 z{g=?~LF25$o|N;x`I z(DyYUXK$#R=LO_9!Hb#w67t*N7`TJHFC)JLjFFGK{4ToZ_6ji1Qsno5{C?(@klzQ+ zm3Lp(FXx`44y8FyAb$YN`6@7H3Hd`H@BHJL9ljr7%Z2@8bl33SO30r8W8|HGGUJRd zqW=_l53YX(@@K%9*FePlJmaGmUdNVmZp`}yddzbjZQ~-xFVQ!Fi2oIO#7E4p;pAhT zH_+vr8}ojH9`jsB+c1Q?L0i0M6AH xaTlVm&fquHxQl>wtOL$5#v0x8#X#RJz_?id5_H#(H~KDgW99$DzO_BO{{llP^`rm* literal 5664 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 MN%&X)IBpmI1LrL=YXATM diff --git a/readme.md b/readme.md index a818b00..9401ffc 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -# Editor (Real Name Pending) +# PadScratch A text editor I'm developing for personal use as well as getting used to 2D development with Vulkan. Will be updated as development continues. -![Screenshot](https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/editor-screenshot.jpg) \ No newline at end of file +![Screenshot](https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/editor-screenshot.jpg) diff --git a/src/editor/editor.d b/src/editor/editor.d index 7c51c8c..9e83f71 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -15,22 +15,33 @@ import std.format; import std.stdio; import std.exception; import std.file; +import std.string; f32 g_delta = 0.0; debug bool g_frame_step = false; debug bool g_frame_continue = false; struct EditorCtx +{ + Arena arena; + UIPanel* base_panel; + u64 panel_id; + EditState state; + u8[128] input_buf; + u32 icount; + Timer timer; + CmdPalette cmd; +} + +struct CmdPalette { Arena arena; - UIPanel* base_panel; - u64 panel_id; - EditState state; - u8[128] input_buf; + u8[] buffer; u32 icount; - u8[] cmd_buffer; - u32 cmd_count; - Timer timer; + Command[] commands; + u8[][] opt_strs; + i64 selected; + Command current; } struct Editor @@ -50,6 +61,22 @@ struct Editor u64 line_offset; } +struct Command +{ + u8[] name; + CmdType type; +} + +enum CmdType +{ + None, + OpenFile, + SaveFile, + CreateFile, +} + +alias CT = CmdType; + enum EditState { NormalMode, @@ -62,6 +89,11 @@ alias ES = EditState; bool g_input_mode = false; +const Command NO_CMD = { + name: [], + type: CT.None, +}; + void Cycle(EditorCtx* ctx, Inputs* inputs) { @@ -88,7 +120,12 @@ Cycle(EditorCtx* ctx, Inputs* inputs) if(ctx.state == ES.CmdOpen) { - CommandPalette(ctx.cmd_buffer[0 .. ctx.cmd_count]); + if(ctx.cmd.commands.length == 0 && ctx.cmd.icount == 0) + { + GetCommands(&ctx.cmd); + } + + CommandPalette(&ctx.cmd); } DrawPanels(ctx.base_panel); @@ -106,12 +143,15 @@ InitEditorCtx(PlatformWindow* window) EditorCtx ctx = { arena: CreateArena(MB(2)), + cmd: { + arena: CreateArena(KB(512)), + buffer: MAllocArray!(u8)(1024), + }, }; ctx.base_panel = CreatePanel(&ctx); ctx.base_panel.ed = CreateEditor(&ctx); ctx.timer = CreateTimer(); - ctx.cmd_buffer = MAllocArray!(u8)(1024); SetFocusedPanel(ctx.base_panel); return ctx; @@ -265,6 +305,8 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs) if(key == Input.Escape) { ctx.state = ES.NormalMode; + ctx.cmd.icount = 0; + ctx.cmd.commands = []; InsertInputToBuf(ctx); taken = true; } @@ -289,7 +331,6 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs) { if(node.value.md & (MD.LeftShift | MD.RightShift)) { - Logf("cmd open"); ctx.state = ES.CmdOpen; taken = true; } @@ -415,7 +456,7 @@ TextLineCharCases() foreach(input; EnumMembers!Input) { u8 ch = InputToChar(input); - if(ch > 0 && ch != '\n') + if(ch > 0 && ch != '\n' && ch != '\t' && ch != ' ') { if(ch == '\'' || ch == '\\') { @@ -431,39 +472,148 @@ TextLineCharCases() return result; } +void +GetCommands(CmdPalette* cmd) +{ + const Command[] cmd_list = [ + { + name: CastStr!(u8)("open"), + type: CT.OpenFile, + }, + { + name: CastStr!(u8)("save"), + type: CT.SaveFile, + }, + { + name: CastStr!(u8)("create"), + type: CT.CreateFile, + }, + ]; + + Reset(&cmd.arena); + cmd.commands = AllocArray!(Command)(&cmd.arena, cmd_list.length); + + u8[] str = cmd.buffer[0 .. cmd.icount]; + + u64 count = 0; + if(str.length > 0) + { + for(u64 i = 0; i < cmd_list.length; i += 1) + { + u64 match = 0; + for(u64 j = 0; j < str.length && j < cmd_list[i].name.length; j += 1) + { + if(str[j] == cmd_list[i].name[j] || (str[j]-32) == cmd_list[i].name[j]) + { + match += 1; + } + } + + if(match == str.length) + { + cmd.commands[count] = cast(Command)cmd_list[i]; + count += 1; + } + } + } + + if(count == 0 && cmd.icount == 0) + { + cmd.commands[] = cast(Command[])cmd_list[]; + } + else + { + cmd.commands = cmd.commands[0 .. count]; + } + + cmd.opt_strs = AllocArray!(u8[])(&cmd.arena, cmd.commands.length); + for(u64 i = 0; i < cmd.commands.length; i += 1) + { + cmd.opt_strs[i] = cmd.commands[i].name; + } +} + bool HandleCmdMode(EditorCtx* ctx, InputEvent ev) { - u8 result = 0; bool taken = false; + CmdPalette* cmd = &ctx.cmd; + + u64 prev_count = cmd.icount; switch(ev.key) with(Input) { mixin(TextLineCharCases()); case Backspace: - { - if(ctx.cmd_count > 0) { - ctx.cmd_count -= 1; - } - } break; - case Escape: - { - ctx.cmd_count = 0; - ctx.state = ES.NormalMode; - } break; + if(cmd.icount > 0) + { + cmd.icount -= 1; + + if(cmd.buffer[cmd.icount] == ' ') + { + cmd.current = cast(Command)NO_CMD; + } + } + } break; + case Space: + { + Check(cmd, 1); + cmd.buffer[cmd.icount++] = ' '; + } goto case Tab; + case Tab: + { + if(cmd.commands.length > 0) + { + cmd.current = cmd.commands[cmd.selected]; + cmd.buffer[0 .. cmd.current.name.length] = cmd.current.name[0 .. $]; + cmd.icount = cast(u32)cmd.current.name.length; + cmd.buffer[cmd.icount++] = ' '; + } + } break; + case Up: + { + if(cmd.selected > 0) + { + cmd.selected -= 1; + } + } break; + case Down: + { + if(cmd.selected < cmd.commands.length) + { + cmd.selected += 1; + } + } break; default: break; } if(result != 0) { - Logf("test"); - ctx.cmd_buffer[ctx.cmd_count++] = result; + Check(cmd, 1); + cmd.buffer[cmd.icount++] = result; + } + + if(cmd.commands.length == 0 || prev_count != cmd.icount) + { + GetCommands(cmd); + cmd.selected = 0; + + Logf("%s", cmd.current.type); } return taken; } +pragma(inline) void +Check(CmdPalette* cmd, u64 length) +{ + if(cmd.icount+length >= cmd.buffer.length) + { + cmd.buffer = ReallocArray!(u8)(cmd.buffer, cmd.buffer.length*2); + } +} + /* void DrawBuffer(Editor* ed, f32 x, f32 y, f32 px, FlatBuffer* fb) diff --git a/src/editor/ui.d b/src/editor/ui.d index af87a79..a3e0ca2 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -37,23 +37,38 @@ alias A2D = Axis2D; enum UIFlags { - None = 0x00, - DrawBackground = 0x01, - DrawText = 0x02, - DrawBorder = 0x04, - Clickable = 0x08, - Draggable = 0x10, - TextInput = 0x20, - Window = 0x40, + None = 0x0000, + DrawBackground = 0x0001, + DrawText = 0x0002, + DrawBorder = 0x0004, + Clickable = 0x0008, + Draggable = 0x0010, + DragBorder = 0x0020, + ClickBorder = 0x0040, + BorderX0 = 0x0080, + BorderX1 = 0x0100, + BorderY0 = 0x0200, + BorderY1 = 0x0400, + TextInput = 0x0800, + Window = 0x1000, + DeferredBorder = 0x2000, } alias UIF = UIFlags; enum UISignal { - None = 0x00, - Clicked = 0x01, - Dragged = 0x02, + None = 0x00, + Clicked = 0x01, + Dragged = 0x02, + ClickedBorder = 0x04, + DraggedBorder = 0x08, + + + BorderX0 = 0x080, + BorderX1 = 0x100, + BorderY0 = 0x200, + BorderY1 = 0x400, } alias UIS = UISignal; @@ -429,6 +444,15 @@ Push(string id, T)(T value) } } +void +Pop(stacks...)() +{ + static foreach(stack; stacks) + { + Pop!(stack); + } +} + auto Pop(string stack)() { @@ -771,7 +795,7 @@ CalcFixedSizes(UIItem* item) { if(i.size_info[axis].type == ST.Pixels) { - i.size.v[axis] = i.size_info[axis].value + i.adjustment.v[axis] + (i.padding.v[axis] * 2.0); + i.size.v[axis] = i.size_info[axis].value + i.adjustment.v[axis]; assert(!isNaN(i.size.v[axis])); } } @@ -870,7 +894,35 @@ DrawDebugUI(UICtx* ctx, UIItem* item) 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) { @@ -995,39 +1047,46 @@ Signal(UIItem* item) { UICtx* ctx = GetCtx(); - item.signal = UIS.None; + 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(item.flags & UIF.Clickable && Clicked(item, n)) + if(BorderClicked(item, n)) + { + taken = true; + if(item.signal & UIS.DraggedBorder) + { + ctx.drag_item = item; + } + } + + else 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; } - if(item.flags & UIF.Draggable && Clicked(item, n) && Nil(ctx.drag_item)) + else if(item.flags & UIF.Draggable && Clicked(item, n) && Nil(ctx.drag_item)) { - Logf("Dragged"); item.signal |= UIS.Dragged; ctx.drag_item = item; taken = true; } - if(ctx.drag_item == item && n.value.key == Input.LeftClick && !n.value.pressed) + else if(ctx.drag_item == item && n.value.key == Input.LeftClick && !n.value.pressed) { - Logf("Released"); ctx.drag_item = g_UI_NIL; + item.signal = UIS.None; taken = true; } - if(ctx.drag_item == item && n.value.key == Input.MouseMotion) + else if(ctx.drag_item == item && n.value.key == Input.MouseMotion) { - item.signal |= UIS.Dragged; + 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; @@ -1263,8 +1322,8 @@ DrawLine(UIItem* item) { UICtx* ctx = GetCtx(); f32 text_size = Get!("text_size")(); - f32 y = item.rect.y0 + text_size + item.padding.v[A2D.Y]; - f32 x = item.rect.x0 + item.padding.v[A2D.X]; + 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(); @@ -1449,6 +1508,14 @@ DrawRect(UICtx* ctx, UIItem* item) 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); } @@ -1529,6 +1596,73 @@ Clicked(UIItem* item, DNode!(InputEvent)* n) 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; + + if(taken) + { + Logf("%s Clicked or Dragged", cast(char[])item.key.hash_text); + } + } + + return taken; +} + +u8[] +ScratchName(u64 num_len, u8[] base, u64 iteration) +{ + import core.stdc.stdio : sprintf; + + char[64] ch_buf; + char[] s = ch_buf.sformat("%%0%sllu%%s", num_len); + + u8[] id = ScratchAlloc!(u8)(base.length+num_len); + sprintf(cast(char*)id.ptr, s.ptr, iteration, cast(char*)base.ptr); + + return id; +} + +u8[] +ScratchName(string fmt, u64 len, u64 iteration) +{ + u8[] id = ScratchAlloc!(u8)(len); + (cast(char[])id).sformat(fmt, iteration); + return id; +} + u8[] ScratchName(u8[] base, u8[] append) { diff --git a/src/editor/widgets.d b/src/editor/widgets.d index 19e6108..e377de9 100644 --- a/src/editor/widgets.d +++ b/src/editor/widgets.d @@ -12,12 +12,77 @@ import std.conv; import core.stdc.stdio : sprintf; const Vec4[4] DEFAULT_COL = [ - Vec4(0.3, 0.65, 0.86, 1.0), - Vec4(0.28, 0.63, 0.83, 1.0), - Vec4(0.26, 0.62, 0.82, 1.0), - Vec4(0.25, 0.61, 0.80, 1.0), + Vec4(0.13, 0.13, 0.13, 1.0), + Vec4(0.13, 0.13, 0.13, 1.0), + Vec4(0.13, 0.13, 0.13, 1.0), + Vec4(0.13, 0.13, 0.13, 1.0), ]; +const Vec4[4] CMD_PALETTE_COL = [ + Vec4(0.21, 0.21, 0.21, 1.0), + Vec4(0.21, 0.21, 0.21, 1.0), + Vec4(0.21, 0.21, 0.21, 1.0), + Vec4(0.21, 0.21, 0.21, 1.0), +]; + +const Vec4[4] DEFAULT_BORDER_COL = [ + Vec4(0.254, 0.254, 0.266, 1.0), + Vec4(0.254, 0.254, 0.266, 1.0), + Vec4(0.254, 0.254, 0.266, 1.0), + Vec4(0.254, 0.254, 0.266, 1.0), +]; + +const Vec4[4] HL_BORDER_COL = [ + Vec4(0.035, 0.549, 0.824, 1.0), + Vec4(0.035, 0.549, 0.824, 1.0), + Vec4(0.035, 0.549, 0.824, 1.0), + Vec4(0.035, 0.549, 0.824, 1.0), +]; + +const Vec4[4] LC_COLOR = [ + Vec4(0.12, 0.12, 0.12, 1.0), + Vec4(0.12, 0.12, 0.12, 1.0), + Vec4(0.12, 0.12, 0.12, 1.0), + Vec4(0.12, 0.12, 0.12, 1.0), +]; + +const Vec4[4] LC_HL_COLOR = [ + Vec4(0.012, 0.176, 0.29, 1.0), + Vec4(0.012, 0.176, 0.29, 1.0), + Vec4(0.012, 0.176, 0.29, 1.0), + Vec4(0.012, 0.176, 0.29, 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), +]; + +const Vec4[4] CMD_PALETTE_INPUT_HL = [ + Vec4(0.24, 0.45, 0.81, 1.0), + Vec4(0.24, 0.45, 0.81, 1.0), + Vec4(0.24, 0.45, 0.81, 1.0), + 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), + Vec4(0.003, 0.48, 0.68, 1.0), + Vec4(0.003, 0.48, 0.68, 1.0), + 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), + Vec4(0.05, 0.56, 0.76, 1.0), + Vec4(0.05, 0.56, 0.76, 1.0), + Vec4(0.05, 0.56, 0.76, 1.0), +]; + +// 9, 141, 211 + const UIPanel g_ui_nil_panel; UIPanel* g_UI_NIL_PANEL; @@ -97,13 +162,14 @@ 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; + UIItem* item = Get(panel.id); + UIItem* separator = g_UI_NIL; UIPanel* parent_pn = panel.parent; - UIItem* prev_panel = !Nil(panel.prev) ? Get(panel.prev.id) : g_UI_NIL; - UIItem* parent = !Nil(parent_pn) ? Get(parent_pn.id) : PeekParent(); + 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)) @@ -118,50 +184,84 @@ Panel(UIPanel* panel, UIFlags flags = UIF.None) } f32 adj_x = 0.0, adj_y = 0.0; - if(!Nil(prev_panel)) + if(!Nil(prev) || !Nil(next)) { - Separator(panel, parent, &adj_x, &adj_y); + 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|flags); + BuildItem(item, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground|UIF.DragBorder|flags); PushParent(item); - Pop!("color")(); - Pop!("layout_axis")(); + Pop!("color", "layout_axis"); return item; } UIItem* -LineCounter(u8[] parent_id, FlatBuffer* buf, f32 offset, i64 start_row, i64 end_row) +LineCounter(u8[] parent_id, FlatBuffer* buf, f32 offset, i64 start_row, i64 end_row, bool focused) { - Push!("padding")(Vec2(4.0, 0.0)); - Push!("color")(Vec4(0.2, 0.5, 0.65, 1.0)); + f32 padding = 4.0; + + Push!("padding")(Vec2(padding, 0.0)); + Push!("color")(focused ? LC_HL_COLOR : LC_COLOR); u8[] id = ScratchName(parent_id, "linec"); - - auto end_chars = end_row.toChars(); - u64 width = cast(u64)(end_chars.length); - - char[20] ch_buf; - ch_buf.sformat("%%0%sllu%%s", width); - 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] = ScratchAlloc!(u8)(width+parent_id.length); - sprintf(cast(char*)line_counts[i].ptr, ch_buf.ptr, start_row+i, cast(char*)parent_id.ptr); + 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)); + 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); @@ -169,18 +269,19 @@ LineCounter(u8[] parent_id, FlatBuffer* buf, f32 offset, i64 start_row, i64 end_ UIItem* inner = Container(ScratchName(parent_id, "lcinner"), s_x, s_y, A2D.Y, UIF.None); { Push!("offset")(Vec2(0.0, offset)); + for(u64 i = 0; i < line_counts.length; i += 1) { UIItem* line = Text(line_counts[i]); } + Pop!("offset")(); } EndContainer(); } EndContainer(); - Pop!("padding")(); - Pop!("color")(); + Pop!("padding", "color"); return item; } @@ -194,7 +295,7 @@ Container(u8[] text, UISize size_x, UISize size_y, Axis2D axis, UIF flags = UIF. BuildItem(item, size_x, size_y, flags); PushParent(item); - Pop!("layout_axis")(); + Pop!("layout_axis"); return item; } @@ -214,61 +315,6 @@ Text(u8[] text) return item; } -void -Separator(UIPanel* panel, UIItem* parent, f32* adj_x, f32* adj_y) -{ - Push!("color")(Vec4(0.1, 0.2, 0.6, 1.0)); - - Axis2D axis = parent.layout_axis; - - f32 sep_y = 1.0, sep_x = 1.0, parent_start = 0.0, parent_end = 0.0; - if(axis == A2D.X) - { - sep_x = 5.0; - *adj_x = -5.0; - parent_start = parent.rect.vec0.x; - parent_end = parent.rect.vec1.x; - } - else - { - sep_y = 5.0; - *adj_y = -5.0; - parent_start = parent.rect.vec0.y; - parent_end = parent.rect.vec1.y; - } - - u8[] buf = ScratchAlloc!(u8)(panel.id.length + 5); - (cast(char[])buf).sformat("%s_sep", cast(char[])panel.id); - - SizeType x_t = axis == A2D.X ? ST.Pixels : ST.Percentage; - SizeType y_t = axis == A2D.Y ? ST.Pixels : ST.Percentage; - - UIItem* item = Get(buf); - BuildItem(item, UISize(x_t, sep_x), UISize(y_t, sep_y), UIF.DrawBackground|UIF.Draggable); - Signal(item); - - f32 pos = item.dragged.v[parent.layout_axis]; - if(item.signal & UIS.Dragged && pos != 0.0) - { - f32 pct = Remap(pos, 0.0, parent_start-parent_end, 0.0, 1.0); - if(CheckPanelBounds(panel.pct - pct) && CheckPanelBounds(panel.prev.pct + pct)) - { - panel.pct -= pct; - panel.prev.pct += pct; - } - } - - Pop!("color"); -} - -void -TextLine(u8[] text) -{ - UIItem* parent = PeekParent(); - - UIItem* item = Get(text); -} - void SetPanelScroll(UIPanel* panel, i64 rows, f32 text_size) { @@ -316,8 +362,13 @@ EditorView(UIPanel* panel) UICtx* ctx = GetCtx(); Editor* ed = panel.ed; - UIItem* item = Panel(panel, UIF.Clickable); + 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; @@ -362,11 +413,12 @@ EditorView(UIPanel* panel) GetLines(&ed.buf, &ed.linebufs, rows); } - LineCounter(panel.id, &panel.ed.buf, offset, line_offset, line_offset+line_rows); + 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); { Push!("offset")(Vec2(0.0, offset)); + U64Vec2 pos = VecPos(&ed.buf); u64 i = 0; for(LineBuffer* buf = ed.linebufs.first; buf != null; buf = buf.next, i += 1) @@ -396,8 +448,6 @@ EditorView(UIPanel* panel) } EndPanel(); - Signal(item); - if(item.signal & UIS.Clicked) { SetFocusedPanel(panel); @@ -461,20 +511,19 @@ WrappedTextLine(u8[] text, u8[] parent_id, f32 text_size, u64 line_no) } UIItem* -TextInput(u8[] text, u8[] hash, f32 x, f32 y, f32 w_pct, f32 text_size, Vec4 bg_col) +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)); - //Logf("%s %s %s", input.rect.vec0.v, input.rect.vec1.v, cast(char[])input.key.text); + Push!("padding")(Vec2(6.0, (h-(text_size))*0.5)-2.0); Push!("offset")(Vec2(x, y)); - Push!("padding")(Vec2(2.0)); - Push!("color")(bg_col); + Push!("border_color")(border_col); + Push!("color")(bg_cols); - Container(ScratchName(hash, "_cntr"), UISize(ST.Percentage, w_pct), UISize(ST.Pixels, text_size), A2D.Y, UIF.DrawBackground); + Container(ScratchName(hash, "_cntr"), UISize(ST.Percentage, w_pct), UISize(ST.Pixels, h), A2D.Y, UIF.DrawBackground|UIF.DrawBorder); - Pop!("color"); - Pop!("offset"); + Pop!("color", "offset", "border_color"); Push!("color")(Vec4(1.0)); Push!("text_size")(text_size); @@ -483,39 +532,52 @@ TextInput(u8[] text, u8[] hash, f32 x, f32 y, f32 w_pct, f32 text_size, Vec4 bg_ EndContainer(); - Pop!("color"); - Pop!("padding"); + Pop!("color", "padding"); return input; } void -CommandPalette(u8[] text) +CommandPalette(CmdPalette* cmd) { + u8[] text = cmd.buffer[0 .. cmd.icount]; + u8[][] options = cmd.opt_strs; + Vec2 size = RootSize(); - f32 x = size.x*0.3; - f32 y = size.y*0.2; - f32 w = size.x*0.4; - f32 h = size.y*0.3; + 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); - Push!("color")(Vec4(0.2, 0.5, 0.65, 1.0)); - Push!("border_color")(Vec4(0.08, 0.3, 0.40, 1.0)); - Push!("corner_radius")(4.0); - Push!("edge_softness")(0.08); - UIItem* window = Window(CastStr!(u8)("##cmd_palette"), x, y, w, h); + 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); - UIItem* text_box = TextInput(text, CastStr!(u8)("###cmd_palette_input"), w*0.1, y*0.2, 0.8, 16.0, Vec4(Vec3(0.0), 1.0)); + 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"); - Pop!("border_color"); - Pop!("corner_radius"); - Pop!("edge_softness"); - Pop!("color"); + Pop!("border_thickness", "corner_radius", "edge_softness", "color"); } UIItem* @@ -525,7 +587,7 @@ Window(u8[] id, f32 x, f32 y, f32 w, f32 h) Push!("offset")(Vec2(x, y)); - BuildItem(item, UISize(ST.Pixels, x), UISize(ST.Pixels, y), UIF.Window|UIF.DrawBackground|UIF.DrawBorder); + BuildItem(item, UISize(ST.Pixels, w), UISize(ST.Pixels, h), UIF.Window|UIF.DrawBackground|UIF.DrawBorder); Pop!("offset"); diff --git a/src/shaders/gui.frag.glsl b/src/shaders/gui.frag.glsl index 03adc64..5ae2b2a 100644 --- a/src/shaders/gui.frag.glsl +++ b/src/shaders/gui.frag.glsl @@ -8,11 +8,12 @@ layout (location = 0) flat in uint in_has_texture; layout (location = 1) in struct FragDataIn { - vec4 color; - vec2 uv; - vec2 dst_pos; - vec2 dst_center; - vec2 dst_half_size; + vec4 color; + vec2 uv; + vec2 dst_pos; + vec2 dst_center; + vec2 dst_half_size; + vec2 sdf_sample_pos; float corner_radius; float softness; float raised; @@ -22,10 +23,9 @@ layout (location = 1) in struct FragDataIn { layout (location = 0) out vec4 FragColor; -float RoundedRectSDF(vec2 pos, vec2 center, vec2 half_size, float radius) +float RectSDF(vec2 pos, vec2 half_size, float radius) { - vec2 dist = (abs(center - pos) - half_size + vec2(radius)); - return min(max(dist.x, dist.y), 0.0) + length(max(dist, 0.0)) - radius; + return length(max(abs(pos) - half_size + radius, 0.0)) - radius; } float ToSRGB(float col) @@ -48,24 +48,27 @@ void main() float softness = FD.softness; vec2 softness_padding = vec2(max(0, softness*2-1), max(0, softness*2-1)); - float dist = RoundedRectSDF(FD.dst_pos, FD.dst_center, FD.dst_half_size-softness_padding, FD.corner_radius); - //float dist = RoundedRectSDF2(FD.dst_pos, FD.dst_center, vec2(1.0), FD.corner_radius); - - float sdf_factor = 1.0 - smoothstep(0, 2*softness, dist); - float border_factor = 1.0; - if(FD.border_thickness != 0.0) + if(FD.border_thickness > 0.0) { - vec2 interior_half_size = FD.dst_half_size - vec2(FD.border_thickness); + float border_sdf = RectSDF(FD.sdf_sample_pos, + FD.dst_half_size - vec2(FD.softness*2.0f) - FD.border_thickness, + max(FD.corner_radius - FD.border_thickness, 0.0f)); + border_factor = smoothstep(0.0f, 2.0f * FD.softness, border_sdf); + } - float interior_radius_reduce_f = min(interior_half_size.x/FD.dst_half_size.x, interior_half_size.y/FD.dst_half_size.y); - - float interior_corner_radius = FD.corner_radius * interior_radius_reduce_f * interior_radius_reduce_f; + if(border_factor < 0.001f) + { + discard; + } - float inside_d = RoundedRectSDF(FD.dst_pos, FD.dst_center, interior_half_size-softness_padding, interior_corner_radius); - - float inside_f = smoothstep(0, 2*softness, inside_d); - border_factor = inside_f; + float corner_factor = 1.0; + if(FD.corner_radius > 0.0 || FD.softness > 0.0) + { + float corner_sdf = RectSDF(FD.sdf_sample_pos, + FD.dst_half_size - vec2(FD.softness * 2.0f), + FD.corner_radius); + corner_factor = 1.0f - smoothstep(0.0, 2.0f * FD.softness, corner_sdf); } vec4 inv_gamma = vec4(1.4); @@ -77,8 +80,11 @@ void main() tex_color = texture(sampler2D(SpriteAtlas, SamplerNearest), FD.uv); } - vec4 color = ToLinear(FD.color); - vec4 out_color = color * tex_color * sdf_factor * border_factor; + vec4 color = FD.color; // ToLinear(FD.color); + vec4 out_color = color * tex_color; + + out_color.a *= corner_factor; + out_color.a *= border_factor; FragColor = out_color; } diff --git a/src/shaders/gui.vert.glsl b/src/shaders/gui.vert.glsl index db63396..155307e 100644 --- a/src/shaders/gui.vert.glsl +++ b/src/shaders/gui.vert.glsl @@ -22,11 +22,12 @@ layout (location = 13) in uint in_has_texture; layout (location = 0) flat out uint out_has_texture; layout (location = 1) out struct FragDataOut { - vec4 color; - vec2 uv; - vec2 dst_pos; - vec2 dst_center; - vec2 dst_half_size; + vec4 color; + vec2 uv; + vec2 dst_pos; + vec2 dst_center; + vec2 dst_half_size; + vec2 sdf_sample_pos; float corner_radius; float softness; float raised; @@ -75,6 +76,9 @@ void main() in_col_4 ); + vec2 dst_verts_pct = vec2(bool(gl_VertexIndex >> 1) ? 1.0f : 0.0f, + bool(gl_VertexIndex & 1) ? 0.0f : 1.0f); + FragData.color = cols[gl_VertexIndex]; FragData.uv = uvs[gl_VertexIndex] / tex_size; FragData.dst_pos = pos; @@ -84,6 +88,7 @@ void main() FragData.softness = edge_softness; FragData.raised = raised; FragData.border_thickness = border_thickness; + FragData.sdf_sample_pos = (2.0f * dst_verts_pct - 1.0f) * half_size; out_has_texture = in_has_texture; vec4 v_pos = PC.projection * vec4(pos.x, pos.y, z_index, 1);