From 342e342a505857d051aaf13eff47c49534f1eb9f Mon Sep 17 00:00:00 2001 From: Matthew Date: Sun, 5 Oct 2025 20:02:44 +1100 Subject: [PATCH] add copy, start on paste and text selections --- src/dlib | 2 +- src/editor/buffer.d | 74 ++++++++++++++++++++++++++++++++++-- src/editor/editor.d | 90 +++++++++++++++++++++++++++++++++----------- src/editor/ui.d | 2 + src/editor/widgets.d | 17 ++++++++- 5 files changed, 156 insertions(+), 29 deletions(-) diff --git a/src/dlib b/src/dlib index 2af4a29..92c4f8e 160000 --- a/src/dlib +++ b/src/dlib @@ -1 +1 @@ -Subproject commit 2af4a293148aadac84d9fec3505d2adcdb747298 +Subproject commit 92c4f8eb665e9fc7a58dc9631bb6f26a0828a638 diff --git a/src/editor/buffer.d b/src/editor/buffer.d index 3d5a026..e2be6e2 100644 --- a/src/editor/buffer.d +++ b/src/editor/buffer.d @@ -15,12 +15,26 @@ struct FlatBuffer LineStart[] line_starts; u64 length; u64 lf_count; + i64 buf_pos; i64 offset; i64 rows; + + I64Vec2 sel; + SelectMode sel_mode; + bool dirty; } +enum SelectMode +{ + None, + Normal, + Line, +} + +alias SM = SelectMode; + struct LineStart { u64 pos; @@ -98,6 +112,7 @@ CreateFlatBuffer(u8[] data) data: buf, length: cast(u64)data.length, lf_count: CountLF(data), + sel: -1, }; fb.tk = CreateTokenizer(&fb); @@ -277,7 +292,42 @@ Insert(FlatBuffer* fb, u8[] insert, u64 length, u64 pos) Reset(&fb.arena); - AdjustOffset(fb); + UpdateOffset(fb); +} + +void +ToggleSelection(FlatBuffer* fb, SelectMode mode) +{ + if(fb.sel_mode == SM.None) + { + StartSelection(fb, mode); + } + else + { + EndSelection(fb); + } +} + +void +StartSelection(FlatBuffer* fb, SelectMode mode) +{ + fb.sel_mode = mode; + + if(mode == SM.Normal) + { + fb.sel = fb.buf_pos; + } + else if(mode == SM.Line) + { + fb.sel = CurrentLine(fb); + } +} + +void +EndSelection(FlatBuffer* fb) +{ + fb.sel = -1; + fb.sel_mode = SM.None; } void @@ -790,14 +840,30 @@ Move(FlatBuffer* fb, Input key, Modifier md) } } - AdjustOffset(fb); - U64Vec2 p = VecPos(fb); + UpdateOffset(fb); + UpdateSelection(fb); + + Logf("selection %s", fb.sel.v); return taken; } void -AdjustOffset(FlatBuffer* fb) +UpdateSelection(FlatBuffer* fb) +{ + if(fb.sel_mode == SM.Normal) + { + fb.sel.y = fb.buf_pos; + } + + if(fb.sel_mode == SM.Line) + { + fb.sel.y = CurrentLine(fb); + } +} + +void +UpdateOffset(FlatBuffer* fb) { if(fb.rows > 0) with(fb) { diff --git a/src/editor/editor.d b/src/editor/editor.d index 2224ab4..70b7291 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -22,6 +22,7 @@ debug bool g_frame_continue = false; struct EditorCtx { Arena arena; + PlatformWindow* window; UIPanel* base_panel; u64 panel_id; EditState state; @@ -61,6 +62,23 @@ struct Editor u64 line_offset; } +struct ChangeStacks +{ + Arena arena; + u64 current_pos; + u8[] current_str; + u64 current_len; + EditorChange* undos; + EditorChange* redos; +} + +struct EditorChange +{ + u8[] str; + u64 pos; + EditorChange* next; +} + struct Command { u8[] name; @@ -79,6 +97,8 @@ enum CmdType OpenFile, SaveFile, CreateFile, + VSplit, + HSplit, } alias CT = CmdType; @@ -148,6 +168,7 @@ InitEditorCtx(PlatformWindow* window) InitUICtx(window); EditorCtx ctx = { + window: window, arena: CreateArena(MB(2)), cmd: { arena: CreateArena(MB(1)), @@ -473,6 +494,9 @@ Shift(Modifier md) void HandleInputs(EditorCtx* ctx, Inputs* inputs) { + u8[] cb_text; + FlatBuffer* fb = !Nil(GetFocusedPanel()) ? &(GetFocusedPanel()).ed.buf : null; + for(auto node = inputs.list.first; node != null; node = node.next) { bool taken = false; @@ -480,7 +504,6 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs) Input key = node.value.key; Modifier md = node.value.md; bool pressed = node.value.pressed; - FlatBuffer* fb = !Nil(GetFocusedPanel()) ? &(GetFocusedPanel()).ed.buf : null; if (pressed) { @@ -529,8 +552,18 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs) } break; case v: { - AddEditor(ctx, GetFocusedPanel(), A2D.X); - taken = true; + if(Ctrl(md)) + { + cb_text = ClipboardText(ctx.window); + } + if(Shift(md)) + { + ToggleSelection(fb, SM.Line); + } + else + { + ToggleSelection(fb, SM.Normal); + } } break; case c: { @@ -566,6 +599,11 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs) } InsertInputToBuf(ctx); + + if(cb_text.length > 0) + { + Insert(fb, cb_text, cb_text.length); + } } void @@ -721,6 +759,14 @@ GetCommands(CmdPalette* cmd) name: CastStr!(u8)("create"), type: CT.CreateFile, }, + { + name: CastStr!(u8)("vsplit"), + type: CT.VSplit, + }, + { + name: CastStr!(u8)("hsplit"), + type: CT.HSplit, + }, ]; Reset(&cmd.arena); @@ -770,16 +816,24 @@ HandleCmdMode(EditorCtx* ctx, InputEvent ev) { case Enter: { - if(cmd.current.type == CT.None) + if(cmd.current.type == CT.None && cmd.commands.length > 0) { - goto case Tab; + if(cmd.commands[cmd.selected].type == CT.OpenFile) + { + goto case Tab; + } + else + { + cmd.current = cmd.commands[cmd.selected]; + } } + UIPanel* p = GetFocusedPanel(); + switch(cmd.current.type) { case CT.OpenFile: { - UIPanel* p = GetFocusedPanel(); if(!Nil(p) && cmd.selected >= 0 && cmd.selected < cmd.opt_strs.length) { OpenFile(p.ed, cmd.opt_strs[cmd.selected]); @@ -787,12 +841,15 @@ HandleCmdMode(EditorCtx* ctx, InputEvent ev) } break; case CT.SaveFile: { - UIPanel* p = GetFocusedPanel(); if(!Nil(p)) { SaveFile(p.ed, GetParam(cmd)); } } break; + case CT.VSplit, CT.HSplit: + { + AddEditor(ctx, p, cmd.current.type == CT.VSplit ? A2D.X : A2D.Y); + } break; default: break; } @@ -800,14 +857,9 @@ HandleCmdMode(EditorCtx* ctx, InputEvent ev) } goto CmdInputEnd; case Backspace: { - if(cmd.icount > 0) + if(CondIncr!(-1)(cmd.icount > 0, &cmd.icount) && cmd.buffer[cmd.icount] == ' ') { - cmd.icount -= 1; - - if(cmd.buffer[cmd.icount] == ' ') - { - cmd.current = cast(Command)NO_CMD; - } + cmd.current = cast(Command)NO_CMD; } } break; case Space: @@ -827,17 +879,11 @@ HandleCmdMode(EditorCtx* ctx, InputEvent ev) } break; case Up: { - if(cmd.selected > 0) - { - cmd.selected -= 1; - } + CondIncr!(-1)(cmd.selected > 0, &cmd.selected); } break; case Down: { - if(cmd.selected < cmd.opt_strs.length-1) - { - cmd.selected += 1; - } + CondIncr!(+1)(cmd.selected < cmd.opt_strs.length-1, &cmd.selected); } break; mixin(TextLineCharCases()); default: break; diff --git a/src/editor/ui.d b/src/editor/ui.d index f3814a7..54fd1bb 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -129,6 +129,7 @@ struct UICtx UIStack!(f32)* edge_softness; i64 highlighted_char; + I64Vec2 selected; u32 tab_width; f32 text_size; @@ -176,6 +177,7 @@ struct UIItem UISize[2] size_info; Vec2 adjustment; i64 highlighted_char; + I64Vec2 selected; // Calculated Parameters Vec4[4] color; diff --git a/src/editor/widgets.d b/src/editor/widgets.d index c4bffc5..dd4f954 100644 --- a/src/editor/widgets.d +++ b/src/editor/widgets.d @@ -378,8 +378,17 @@ EditorView(UIPanel* panel) Container(ScratchName(panel.id, "lines"), UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0), A2D.Y); { - U64Vec2 pos = VecPos(&ed.buf); - u64 i = 0; + 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) @@ -395,6 +404,10 @@ EditorView(UIPanel* panel) Push!("highlighted_char")(pos.x); } + 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); height += (text_size * tp.count); if(TextClicked(tp))