From d233c653043c50531502e09304348b0da593c7ce Mon Sep 17 00:00:00 2001 From: Matthew Date: Wed, 17 Sep 2025 06:28:01 +1000 Subject: [PATCH] start work on text input --- src/dlib | 2 +- src/editor/buffer.d | 177 ++++++++++++++++++++++++++++++-------- src/editor/editor.d | 196 +++++++++++++++++++++++-------------------- src/editor/parsing.d | 2 + src/editor/widgets.d | 23 +++-- 5 files changed, 266 insertions(+), 134 deletions(-) diff --git a/src/dlib b/src/dlib index 6757559..ff94c5c 160000 --- a/src/dlib +++ b/src/dlib @@ -1 +1 @@ -Subproject commit 6757559089314da06956460c32ff632db7d10f88 +Subproject commit ff94c5cb5e4b3c581521452a2a50b363a09cd8d6 diff --git a/src/editor/buffer.d b/src/editor/buffer.d index de9df6a..b88adae 100644 --- a/src/editor/buffer.d +++ b/src/editor/buffer.d @@ -1,25 +1,18 @@ -import dlib.aliases; -import dlib.math; -import dlib.alloc; -import dlib.util; +import dlib; import core.stdc.stdio : EOF; import parsing; import std.format : sformat; +import std.math.algebraic : abs; struct FlatBuffer { Tokenizer tk; - Arena arena; - u8[] data; - u64 length; - u64 line_count; - Range pos; -} - -struct Range -{ - u32 x; - u32 y; + Arena arena; + U64Vec2 pos; + u8[] data; + u64 length; + u64 line_count; + u64 buf_pos; } struct LineBuffers @@ -31,7 +24,7 @@ struct LineBuffers FlatBuffer CreateFlatBuffer(u8[] data) { - u64 capacity = RoundUp(cast(u64)(data.length) * 2, KB(4)); + u64 capacity = data.length > 0 ? RoundUp(cast(u64)(data.length) * 2, KB(4)) : KB(4); u8[] buf = MAllocArray!(u8)(capacity); buf[0 .. data.length] = data[0 .. data.length]; @@ -70,6 +63,7 @@ CreateLineBuffers(u64 arena_size) void Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos) { + Logf("%s %s %s", buffer.length, length, buffer.data.length); if(buffer.length + length > buffer.data.length) { FlatBuffer new_buf = CreateFlatBuffer(buffer.data); @@ -99,11 +93,11 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos) } void -Insert(FlatBuffer* buffer, u8[] insert, u64 length, Range pos) +Insert(FlatBuffer* buffer, u8[] insert, u64 length, UVec2 pos) { assert(pos.y <= buffer.line_count, "Insert failure: y provided is greater than line_count"); - Insert(buffer, insert, length, RangeToPos(buffer, pos)); + Insert(buffer, insert, length, VecToPos(buffer, pos)); } // TODO: handle case for when lines are longer than line buffer @@ -164,37 +158,150 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length) } } -u64 -RangeToPos(FlatBuffer* buffer, Range range) +void +Move(FlatBuffer* buffer, Input key, Modifier md) { - u64 buffer_pos; - u32 line, col; - for(u64 i = 0; i < buffer.length; i += 1) + if(md & (MD.LeftShift | MD.RightShift)) { - if(range.y == line) + switch(key) { - buffer_pos = i; - break; - } + case Input.Up: + { + } break; + case Input.Down: + { - if(buffer.data.ptr[i] == '\n') + } break; + case Input.Left: + { + + } break; + case Input.Right: + { + + } break; + default: break; + } + } + else if(md & (MD.LeftCtrl | MD.RightCtrl)) + { + switch(key) { - line += 1; + case Input.Up: + { + } break; + case Input.Down: + { + + } break; + case Input.Left: + { + + } break; + case Input.Right: + { + + } break; + default: break; + } + } + else + { + switch(key) + { + case Input.Down: + case Input.Up: + { + if((Input.Up && buffer.pos.y > 0) || (Input.Down && buffer.pos.y < buffer.line_count)) + { + u32 x = buffer.pos.x; + buffer.pos.x = 0; + buffer.pos.y += key == Input.Down ? 1 : -1; + u64 pos = VecToPos(buffer, buffer.pos); + u64 i = pos; + for(; i < pos+x && buffer.data[i] != '\n'; i += 1) {} + buffer.pos.x = cast(u32)(i - pos); + buffer.buf_pos = i; + } + } break; + case Input.Left: + case Input.Right: + { + if((Input.Left && buffer.buf_pos > 0) || (Input.Right && buffer.buf_pos < buffer.length)) + { + u64 move = key == Input.Left ? -1 : +1; + buffer.buf_pos += move; + if(buffer.data[buffer.buf_pos] == '\n') + { + u64 x = buffer.pos.x; + buffer.pos.y += move; + buffer.pos.x = 0; + if(key == Input.Left) + { + u64 pos = VecToPos(buffer, buffer.pos); + u64 i = pos; + for(; i < pos+x && buffer.data[i] != '\n'; i += 1) {} + buffer.pos.x = cast(u32)(i - pos); + buffer.buf_pos = i; + } + } + else + { + buffer.pos.x += move; + } + } + } break; + default: break; } } - for(u64 i = buffer_pos; i < buffer.length; i += 1) + Logf("%s %s", buffer.buf_pos, buffer.pos.v); +} + +u64 +RelVecToPos(FlatBuffer* buffer, U64Vec2 vec) +{ + u64 pos = 0; + bool closer_to_start = abs(buffer.pos.y - vec.x) > vec.x; + if(closer_to_start) { - if(col == range.x) + u32 line, col; + for(; i < buffer.length; pos += 1) { - buffer_pos = i; - break; + if(range.y == line) + { + break; + } + + if(buffer.data.ptr[pos] == '\n') + { + line += 1; + } } - col += 1; + for(; buffer_pos < buffer.length; pos += 1) + { + if(col == range.x) + { + break; + } - assert(buffer.data.ptr[i] != '\n', "Insert FlatBuffer Range failure: x position not in range of line"); + col += 1; + + assert(buffer.data.ptr[i] != '\n', "Insert FlatBuffer Range failure: x position not in range of line"); + } } + else + { + u64 move = buffer.pos.y < vec.y || (buffer.pos.y == vec.y && buffer.pos.x < vec.pos.x) ? -1 : +1; + } + + return pos; +} + +u64 +VecToPos(FlatBuffer* buffer, U64Vec2 vec) +{ return buffer_pos; } diff --git a/src/editor/editor.d b/src/editor/editor.d index 2e9630f..fd7bb5e 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -11,6 +11,7 @@ import ui; import widgets : Nil; import widgets; +import std.format; import std.stdio; import std.exception; @@ -22,6 +23,8 @@ struct EditorCtx UIPanel* base_panel; u64 panel_id; EditState state; + u8[128] input_buf; + u32 icount; } struct Editor @@ -108,6 +111,8 @@ CreateEditor(EditorCtx* ctx) Editor* ed = Alloc!(Editor)(&ctx.arena); ed.arena = CreateArena(MB(4)); + ed.buf = CreateFlatBuffer([]); + ed.linebufs = CreateLineBuffers(MB(1)); return ed; } @@ -190,6 +195,20 @@ AddEditor(EditorCtx* ctx, UIPanel* target, Axis2D axis) } } +pragma(inline) void +InsertInputToBuf(EditorCtx* ctx) +{ + if(ctx.icount > 0) + { + UIPanel* p = GetFocusedPanel(); + if(!Nil(p)) + { + Insert(&p.ed.buf, ctx.input_buf, ctx.icount, 0); + ctx.icount = 0; + } + } +} + void HandleInputs(EditorCtx* ctx, Inputs* inputs) { @@ -202,111 +221,54 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs) if (pressed) { - switch(key) + if(key == Input.Escape) { - case Input.i: - { - if(ctx.state == ES.NormalMode) + ctx.state = ES.NormalMode; + InsertInputToBuf(ctx); + taken = true; + } + else if(ctx.state == ES.InputMode) + { + taken = HandleInputMode(ctx, node.value); + } + else + { + switch(key) + { + case Input.i: { ctx.state = ES.InputMode; taken = true; - } - } break; - case Input.Esc: - { - if(ctx.state == ES.InputMode || ctx.state == ES.CmdOpen) + } break; + case Input.Semicolon: { - ctx.state = ES.NormalMode; - taken = true; - } - } break; - case Input.Semicolon: - { - if(node.value.md & (MD.LeftShift | MD.RightShift)) - { - if(ctx.state != ES.NormalMode) + if(node.value.md & (MD.LeftShift | MD.RightShift)) { ctx.state = ES.CmdOpen; taken = true; } - } - } break; - case Input.w: - { - if(ctx.state == ES.NormalMode && node.value.md & (MD.LeftCtrl | MD.RightCtrl)) - { - ctx.state = ES.SetPanelFocus; - taken = true; - } - } break; - case Input.v: - { - if(ctx.state == ES.NormalMode) + } break; + case Input.v: { AddEditor(ctx, GetFocusedPanel(), A2D.X); - } - } break; - case Input.c: - { - if(ctx.state == ES.NormalMode) + taken = true; + } break; + case Input.c: { AddEditor(ctx, GetFocusedPanel(), A2D.Y); - } - } break; - case Input.d: - { - if(node.value.md & (MD.LeftShift | MD.RightShift)) + taken = true; + } break; + case Input.d: { - static dbg = false; - dbg = !dbg; - SetDebug(dbg); - } - } break; - case Input.Up: - case Input.Down: - case Input.Left: - case Input.Right: - { - if(false) + } break; + case Input.Up: + case Input.Down: + case Input.Left: + case Input.Right: { - UIPanel* panel = GetFocusedPanel(); - UIPanel* focused = g_UI_NIL_PANEL; - for(UIPanel* p = panel; !Nil(p); p = p.parent) - { - if(p.parent.axis == A2D.X) - { - if(key == Input.Left && !Nil(p.prev) && p.prev.ed != null) - { - focused = p.prev; - break; - } - - if(key == Input.Right && !Nil(p.next) && p.next.ed != null) - { - focused = p.next; - break; - } - } - else - { - if(key == Input.Up && !Nil(p.prev) && p.prev.ed != null) - { - focused = p.prev; - break; - } - - if(key == Input.Down && !Nil(p.next) && p.next.ed != null) - { - focused = p.next; - break; - } - } - } - - SetFocusedPanel(focused); - } - } break; - default: break; + } break; + default: break; + } } } @@ -315,6 +277,60 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs) DLLRemove(&inputs.list, node, null); } } + + InsertInputToBuf(ctx); +} + +pragma(inline) void +InsertChar(EditorCtx* ctx, Input input, bool modified) +{ + ctx.input_buf[ctx.icount++] = InputToChar(input, modified); +} + +static string +CharCases() +{ + import std.traits; + + string result = ""; + foreach(input; EnumMembers!Input) + { + u8 ch = InputToChar(input); + if(ch > 0) + { + result ~= format("case Input.%s: InsertChar(ctx, Input.%s, cast(bool)(ev.md & (MD.LeftShift | MD.RightShift))); taken = true; break;\n", input, input); + } + } + return result; +} + +bool +HandleInputMode(EditorCtx* ctx, InputEvent ev) +{ + bool taken = false; + + switch(ev.key) + { + mixin(CharCases()); + case Input.Escape: + { + ctx.state = ES.NormalMode; + } break; + case Input.Up: + case Input.Down: + case Input.Left: + case Input.Right: + { + UIPanel* p = GetFocusedPanel(); + if(!Nil(p)) + { + Move(&p.ed.buf, ev.key, ev.md); + } + } break; + default: break; + } + + return taken; } /* diff --git a/src/editor/parsing.d b/src/editor/parsing.d index eb0cd8c..b983e8a 100644 --- a/src/editor/parsing.d +++ b/src/editor/parsing.d @@ -149,6 +149,8 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [ void Tokenize(FlatBuffer* fb) { + if(fb.data.length == 0) return; + Tokenizer* tk = &fb.tk; u64 semi_count; diff --git a/src/editor/widgets.d b/src/editor/widgets.d index a00cdce..f1b619e 100644 --- a/src/editor/widgets.d +++ b/src/editor/widgets.d @@ -5,6 +5,7 @@ import ui : Nil; import ui; import editor; import std.format : sformat; +import std.math.rounding : ceil; const UIPanel g_ui_nil_panel; UIPanel* g_UI_NIL_PANEL; @@ -175,14 +176,20 @@ EditorView(UIPanel* panel) UICtx* ctx = GetCtx(); UIItem* item = Panel(panel, UIF.Clickable); - TextPart* tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 16.0, 0); - if(TextClicked(tp)) SetFocusedPanel(panel); - tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 16.0, 1); - if(TextClicked(tp)) SetFocusedPanel(panel); - tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 16.0, 2); - if(TextClicked(tp)) SetFocusedPanel(panel); - tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 16.0, 3); - if(TextClicked(tp)) SetFocusedPanel(panel); + u64 rows = cast(u64)ceil(item.size.y/16.0); + GetLines(&panel.ed.buf, &panel.ed.linebufs, 0, rows); + for(u64 i = 0; i < panel.ed.linebufs.lines.length; i += 1) + { + u8[] line = panel.ed.linebufs.lines[i]; + if(line.length > 0) + { + TextPart* tp = WrappedTextLine(line, panel.id, 16.0, i); + if(TextClicked(tp)) + { + SetFocusedPanel(panel); + } + } + } Signal(item);