diff --git a/assets/gui.frag.spv b/assets/gui.frag.spv index 8da6167..642d426 100644 Binary files a/assets/gui.frag.spv and b/assets/gui.frag.spv differ diff --git a/assets/gui.vert.spv b/assets/gui.vert.spv index 01eb875..7565cdd 100644 Binary files a/assets/gui.vert.spv and b/assets/gui.vert.spv differ 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);