diff --git a/src/editor/editor.d b/src/editor/editor.d index 5848aea..9fbac76 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -67,20 +67,26 @@ struct Ctx UIKey hover_key; UIKey focus_key; + UIPanel* base_panel; + UIPanel* focused_panel; + u64 last_hover_frame; + f32 pos_rate; f32 scroll_rate; f32 animation_rate; f32 fade_rate; - EditState state; - u8[128] input_buf; - u32 icount; - u64 editor_id_incr; - Timer timer; - CmdPalette cmd; - string[] file_names; - u64 panel_id; + ConfigValue[CO.max] config_values; + + EditState state; + u8[128] input_buf; + u32 icount; + u64 editor_id_incr; + Timer timer; + CmdPalette cmd; + string[] file_names; + u64 panel_id; alias rd_ctx this; } @@ -111,6 +117,8 @@ struct Editor Vec2 select_end; i64 line_offset; i64 line_height; + + alias buf this; } struct ChangeStacks @@ -130,7 +138,7 @@ struct EditorChange EditorChange* next; } -alias CmdFn = bool function(Ctx* ctx); +alias CmdFn = bool function(Ctx* ctx); // return true when completed struct Command { @@ -140,28 +148,56 @@ struct Command CmdType type; union { - CmdFn fn; - + CmdFn fn; + ConfigOpt opt; } } -struct Parameter +union ConfigValue { - string value; - bool visible; + f32 _f32; + u64 _u64; + Vec4 _vec4; } +enum ValueType +{ + F32, + U64, + Vec4, + Max, +} alias VT = ValueType; + +enum ConfigInputType +{ + Text, + Slider, + Dropdown, + Max, +} alias CIT = ConfigInputType; + +struct ConfigInfo +{ + ValueType value_type; + ConfigInputType input_type; +} + +enum ConfigOpt +{ + CornerRadius, + TabWidth, + ScrollSpeed, + EditorPadding, + Max, +} alias CO = ConfigOpt; + enum CmdType { None, - OpenFile, - SaveFile, - CreateFile, - VSplit, - HSplit, -} - -alias CT = CmdType; + Config, + Callback, + Hotkey, +} alias CT = CmdType; enum EditState { @@ -170,14 +206,12 @@ enum EditState CmdPalette, RunCmd, SetPanelFocus, // if moving left/right move up parent tree until one is found with a2d.x, same thing for up/down a2d.y -} +} alias ES = EditState; -alias ES = EditState; - -bool -CmdModeActive() +struct Parameter { - return g_ctx.state == ES.CmdPalette; + string value; + bool visible; } __gshared bool g_input_mode = false; @@ -193,34 +227,48 @@ const Command[] CMD_LIST = [ name: "Open", cmd: "open", desc: "Open a file in the focused editor view.", - type: CT.OpenFile, + type: CT.Callback, + //fn: &NotImplementedCallback, }, { name: "Save", cmd: "save", desc: "Save the current file in the focused editor view.", - type: CT.SaveFile, + type: CT.Callback, }, { name: "New File", cmd: "new file", desc: "Create a new file with a specified name and location.", - type: CT.CreateFile, + type: CT.Callback, }, { name: "Vertical Split", cmd: "vsplit", desc: "Split the current editor view vertically.", - type: CT.VSplit, + type: CT.Callback, }, { name: "Horizontal Split", cmd: "hsplit", desc: "Split the current editor view horizontally.", - type: CT.HSplit, + type: CT.Callback, }, ]; +bool +NotImplementedCallback(Ctx* ctx) +{ + Logf("Not yet implemented!"); + return true; +} + +bool +CmdModeActive() +{ + return g_ctx.state == ES.CmdPalette; +} + bool Active(EditState state) { @@ -245,71 +293,25 @@ Cycle(Inputs* inputs) { ResetScratch(MB(4)); - g_delta = DeltaTime(&g_ctx.timer); - + g_delta = DeltaTime(&g_ctx.timer); g_input_mode = Active(ES.InputMode); BeginUI(inputs); Ctx* ctx = GetCtx(); - static UIPanel* panel; - if(Nil(panel)) - { - panel = MakePanel(); - panel.pct = 1.0; - - UIPanel* p0 = MakePanel(); - UIPanel* p1 = MakePanel(); - UIPanel* p2 = MakePanel(); - UIPanel* p3 = MakePanel(); - UIPanel* p4 = MakePanel(); - UIPanel* p5 = MakePanel(); - UIPanel* p6 = MakePanel(); - - p0.pct = 0.3; - p1.pct = 0.7; - p0.parent = panel; - p1.parent = panel; - - DLLPush(panel, p0, g_NIL_PANEL); - DLLPush(panel, p1, g_NIL_PANEL); - - p0.axis = A2D.Y; - - p2.pct = 0.25; - p3.pct = 0.35; - p4.pct = 0.40; - p2.parent = p3.parent = p4.parent = p0; - - DLLPush(p0, p2, g_NIL_PANEL); - DLLPush(p0, p3, g_NIL_PANEL); - DLLPush(p0, p4, g_NIL_PANEL); - - p5.pct = 0.2; - p6.pct = 0.8; - p5.parent = p6.parent = p4; - - DLLPush(p4, p5, g_NIL_PANEL); - DLLPush(p4, p6, g_NIL_PANEL); - - p1.ed = CreateEditor(); - p2.ed = CreateEditor(); - p3.ed = CreateEditor(); - p5.ed = CreateEditor(); - p6.ed = CreateEditor(); - - OpenFile(p1.ed, "./src/VulkanRenderer/external/vma/vk_mem_alloc.h"); - OpenFile(p3.ed, "./src/VulkanRenderer/external/vma/vk_mem_alloc.h"); - OpenFile(p5.ed, "./src/VulkanRenderer/external/vma/vk_mem_alloc.h"); - OpenFile(p6.ed, "./src/VulkanRenderer/external/vma/vk_mem_alloc.h"); - } - - Panel(ctx, panel); + Panel(ctx, ctx.base_panel); EndUI(); } +void +Focus(UIPanel* panel) +{ + g_ctx.focused_panel = panel; + g_ctx.focus_key = panel.key; +} + void InitCtx(PlatformWindow* window) { @@ -320,12 +322,12 @@ InitCtx(PlatformWindow* window) ctx.cmd.arena = CreateArena(MB(1)); ctx.cmd.cmd_arena = CreateArena(MB(1)); ctx.cmd.buffer = Alloc!(u8)(1024); - //ctx.base_panel = CreatePanel(CreateEditor()); ctx.timer = CreateTimer(); InitUI(ctx); - //FocusEditor(ctx.base_panel); + ctx.base_panel = CreatePanel(CreateEditor()); + Focus(ctx.base_panel); if(getcwd() != "/") { @@ -664,13 +666,14 @@ MovePanelFocus(A2D axis, bool prev)(UIPanel* panel) return result; } -void +bool HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focused) { Editor* ed = p.ed; Ctx* ctx = &g_ctx; FlatBuffer* fb = &ed.buf; - u8[] cb_text; + u64 initial_len = p.ed.buf.length; + u8[] cb_text; for(auto node = inputs.first; node != null; node = node.next) { @@ -808,6 +811,8 @@ HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focuse { Insert(fb, cb_text, cb_text.length); } + + return initial_len != p.ed.buf.length; } void diff --git a/src/editor/ui.d b/src/editor/ui.d index 96ba7f0..78a0c21 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -168,8 +168,8 @@ struct Style Vec4 border_col; Vec4 border_hl_col; Vec4 corner_radius; - f32 border_thickness; - f32 edge_softness; + f32 border_thickness = 0.0; + f32 edge_softness = 0.0; } Style SEP_STYLE = { @@ -177,6 +177,11 @@ Style SEP_STYLE = { hl_col: Vec4(0.2, 0.2, 0.2, 1.0), }; +Style CURSOR_STYLE = { + col: Vec4(1.0), + hl_col: Vec4(1.0), +}; + Style PANEL_STYLE = { col: BG_COL, hl_col: BG_COL, @@ -200,13 +205,13 @@ Style CMD_STYLE = { enum UIFlags { None, - Ready = 1<<0, - Hot = 1<<1, - Active = 1<<2, - Click = 1<<3, - Drag = 1<<4, - Priority = 1<<5, - TargetLeniency = 1<<6, + Ready = 1<<0, + Hot = 1<<1, + Active = 1<<2, + Click = 1<<3, + Drag = 1<<4, + Priority = 1<<5, + TargetLeniency = 1<<6, } alias UIF = UIFlags; struct UIItem @@ -218,6 +223,7 @@ struct UIItem Vec2 scroll_offset; Vec2 scroll_target; Vec2 scroll_size; + Vec2 target_pos; Axis2D axis; u64 last_frame; f32 ready_t; // Item visible @@ -274,6 +280,7 @@ struct UIPanel f32 pct; u32 id; Editor* ed; + bool move_text_with_cursor; alias item this; } @@ -525,6 +532,25 @@ EndScissor(Ctx* ctx) ResetScissor(&ctx.rd); } +void +AnimatePos(UIItem* item) +{ + if(item.p0 != item.target_pos) + { + item.p0 += g_ctx.pos_rate * (item.target_pos - item.p0); + + static foreach(i; 0 .. 2) + { + if(fabsf(item.p0.v[i] - item.target_pos.v[i]) < 2.0f) + { + item.p0.v[i] = item.target_pos.v[i]; + } + } + } + + item.p1 = item.p0+item.size; +} + void Panel(Ctx* ctx, UIPanel* panel) { @@ -560,6 +586,11 @@ Panel(Ctx* ctx, UIPanel* panel) FontGlyphs* fg = GetFontGlyphs(12); FontAtlasBuf* abuf = &fg.abuf; + // Inputs + bool hovered = ctx.hover_key == panel.key; + bool focused = ctx.focus_key == panel.key && ctx.focused_panel == panel; + panel.move_text_with_cursor |= HandleInputs(panel, &ctx.events, hovered, focused); + f32 lheight = abuf.atlas.line_height; panel.scroll_target.y = cast(f32)(panel.ed.line_offset)*lheight; @@ -590,8 +621,8 @@ Panel(Ctx* ctx, UIPanel* panel) for(u64 i = start_ln; i < max_ln; i += 1) { - string line_num = Scratchf("%s", i); - DrawText(ctx, line_num, fg, Vec4(1.0), text_rect, TA.Right); + string line_num = Scratchf("%s", i+1); + DrawText(ctx, line_num, Vec4(1.0), fg, text_rect, TA.Right); text_rect.p0.y += lheight; } @@ -608,20 +639,78 @@ Panel(Ctx* ctx, UIPanel* panel) text_rect = Pad(rects[1], PAD); text_rect.p0.y += offset; - u64 i = start_ln; - for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); i += 1, lbuf = GetLine(&panel.ed.buf, i)) + I64Vec2 cursor_pos = VecPos(&panel.ed.buf); + UIItem* cursor = MakeItem("###cursor_%s", panel.id); + if(ctx.focused_panel == panel) { - DrawText(ctx, Str(lbuf.text), fg, cast(u8[])lbuf.style, text_rect, TA.Left); - text_rect.p0.y += lheight; + i64 y_pos = cursor_pos.y - panel.ed.line_offset; + + cursor.size.x = Active(ES.InputMode) ? 2.0 : fg.abuf.atlas.max_advance; + cursor.size.y = lheight; + + cursor.target_pos.y = text_rect.p0.y + (y_pos*lheight); + cursor.target_pos.x = text_rect.p0.x + (cursor_pos.x*fg.abuf.atlas.max_advance); + + AnimatePos(cursor); + + if(cursor.p0 == cursor.target_pos) + { + panel.move_text_with_cursor = false; + } + + DrawRect(ctx, cursor.rect, &CURSOR_STYLE); + } + + u64 i = start_ln; + + if(ctx.focused_panel == panel) + { + if(Active(ES.InputMode) && panel.move_text_with_cursor) + { + for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); i += 1, lbuf = GetLine(&panel.ed.buf, i)) + { + if(i == cursor_pos.y) + { + string str = Str(lbuf.text[0 .. cursor_pos.x]); + u8[] tks = cast(u8[])lbuf.style[0 .. cursor_pos.x]; + + DrawText(ctx, str, tks, fg, text_rect, TA.Left); + + Rect split_rect = text_rect; + split_rect.p0.x = cursor.p0.x; + + str = Str(lbuf.text[cursor_pos.x .. $]); + tks = cast(u8[])lbuf.style[cursor_pos.x .. $]; + + DrawText(ctx, str, tks, fg, split_rect, TA.Left); + } + else + { + DrawText(ctx, Str(lbuf.text), cast(u8[])lbuf.style, fg, text_rect, TA.Left); + } + + text_rect.p0.y += lheight; + } + } + else if(!Active(ES.InputMode)) + { + for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); i += 1, lbuf = GetLine(&panel.ed.buf, i)) + { + DrawText(ctx, Str(lbuf.text), cast(u8[])lbuf.style, fg, text_rect, TA.Left, cursor_pos.y == i ? cursor_pos.x : -1); + text_rect.p0.y += lheight; + } + } + else for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); i += 1, lbuf = GetLine(&panel.ed.buf, i)) + { + DrawText(ctx, Str(lbuf.text), cast(u8[])lbuf.style, fg, text_rect, TA.Left); + text_rect.p0.y += lheight; + } } ResetBuffer(&panel.ed.buf); EndScissor(ctx); DrawBorder(ctx, rects[1], &style); - - // Inputs - HandleInputs(panel, &ctx.events, ctx.hover_key == panel.key, ctx.focus_key == panel.key); } else { @@ -773,7 +862,11 @@ CreatePanel(Editor* ed) p.id = panel_id++; p.item = MakeItem!(PANEL_FLAGS)("###panel_%s", p.id); - + p.ed = ed; + p.pct = 1.0; + + p.first = p.last = p.next = p.prev = p.parent = g_NIL_PANEL; + return p; } @@ -1010,6 +1103,7 @@ BeginUI(Inputs* inputs) ctx.animation_rate = 1.0 - pow(2.0, (-30.0f * g_delta)); ctx.fade_rate = 1.0 - pow(2.0, (-50.0f * g_delta)); ctx.scroll_rate = 1.0 - pow(2.0, (-60.0f * g_delta)); + ctx.pos_rate = 1.0 - pow(2.0, (-90.0f * g_delta)); version(ENABLE_RENDERER) { @@ -1252,7 +1346,7 @@ enum TextAlign } alias TA = TextAlign; void -DrawText(T, U)(Ctx* ctx, string text, T size_param, U col_param, Rect rect, TextAlign text_align, f32 ready = 1.0) +DrawText(T, U)(Ctx* ctx, string text, U col_param, T size_param, Rect rect, TextAlign text_align, i64 hl_char = -1, f32 ready = 1.0) if((is(T == FontGlyphs*) || (is(T: u32)) && is(U == Vec4) || is(U == u8[]))) { static if(is(T == FontGlyphs*)) @@ -1293,6 +1387,10 @@ DrawText(T, U)(Ctx* ctx, string text, T size_param, U col_param, Rect rect, Text u8 ch = text[j]; Glyph* g = ch < glyphs.length ? glyphs.ptr + ch : null; Vec4 col = SYNTAX_COLORS[tks[j]]; + if(hl_char == j) + { + col = InvertCol(col); + } AnimateReady(ready, &col); DrawGlyph(rect, g, &x, rect.p0.y, line_height, col); @@ -1307,11 +1405,17 @@ DrawText(T, U)(Ctx* ctx, string text, T size_param, U col_param, Rect rect, Text { u8 ch = text[j]; Glyph* g = ch < glyphs.length ? glyphs.ptr + ch : null; - DrawGlyph(rect, g, &x, rect.p0.y, line_height, col); + DrawGlyph(rect, g, &x, rect.p0.y, line_height, hl_char == j ? InvertCol(col) : col); } } } +pragma(inline) Vec4 +InvertCol(Vec4 col) +{ + return Vec4(1.0-col.r, 1.0-col.g, 1.0-col.b, col.a); +} + void BeginScissor(Ctx* ctx, UIItem* item, bool scissor_x = true, bool scissor_y = true) {