From c9617b701bbbe517a73c68cc0bde89c61667a011 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sat, 20 Sep 2025 15:48:00 +1000 Subject: [PATCH] more fixes for text buffer, add scrolling --- src/editor/buffer.d | 272 +++++++++++++++++++++++-------------------- src/editor/editor.d | 6 + src/editor/ui.d | 35 ++++-- src/editor/widgets.d | 35 ++++-- 4 files changed, 204 insertions(+), 144 deletions(-) diff --git a/src/editor/buffer.d b/src/editor/buffer.d index cb7f8dd..e05c4ab 100644 --- a/src/editor/buffer.d +++ b/src/editor/buffer.d @@ -13,12 +13,22 @@ struct FlatBuffer u64 length; u64 lf_count; i64 buf_pos; + i64 offset; + i64 rows; } struct LineBuffers { - Arena arena; - u8[][] lines; + Arena arena; + LineBuffer* first; + LineBuffer* last; + u64 count; +} + +struct LineBuffer +{ + u8[] text; + LineBuffer* next; } enum WalkDir @@ -102,6 +112,8 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos) buffer.length += length; Reset(&buffer.arena); + + AdjustOffset(buffer); } void @@ -118,59 +130,96 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, I64Vec2 pos) Insert(buffer, insert, length, VecToPos(buffer, pos)); } +void +GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 length) +{ + GetLines(buffer, linebufs, buffer.offset, length); +} + void GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length) { assert(linebufs != null, "GetLines failure: linebufs is null"); - u64 line_count = buffer.lf_count + 1; - length = length > line_count ? line_count : length; Reset(&linebufs.arena); - linebufs.lines = AllocArray!(u8[])(&linebufs.arena, length); + linebufs.first = Alloc!(LineBuffer)(&linebufs.arena); + linebufs.count = 0; i64 start = -1; u64 line = 0; - u64 current_line = 0; - for(u64 i = 0; i < buffer.length; i += 1) + if(buffer.length == 0) { - if(current_line == length) + linebufs.first.text = AllocArray!(u8)(&linebufs.arena, 1); + linebufs.first.text[0] = 0; + } + else with(linebufs) + { + LineBuffer* current = first; + for(u64 i = 0; i < buffer.length; i += 1) { - break; + if(count == length) + { + break; + } + + bool new_line = (buffer.data.ptr[i] == '\n'); + if(line < start_line && new_line) + { + line += 1; + continue; + } + + if(line < start_line) + { + continue; + } + + if(start < 0 && new_line) + { + current.text = AllocArray!(u8)(&arena, 1); + current.text[0] = '\n'; + count += 1; + + current.next = Alloc!(LineBuffer)(&arena); + current = current.next; + continue; + } + + if(start < 0) + { + start = cast(i64)i; + } + + if(new_line) + { + current.text = AllocArray!(u8)(&arena, i-start); + current.text[0 .. i-start] = buffer.data[start .. i]; + count += 1; + start = -1; + + current.next = Alloc!(LineBuffer)(&arena); + current = current.next; + + continue; + } + + if(i == buffer.length-1) + { + current.text = AllocArray!(u8)(&arena, (buffer.length-start)); + current.text[0 .. buffer.length-start] = buffer.data[start .. buffer.length]; + + current.next = Alloc!(LineBuffer)(&arena); + current = current.next; + + count += 1; + } } - bool new_line = (buffer.data.ptr[i] == '\n'); - if(line < start_line && new_line) + if(buffer.length > 0 && buffer.data[buffer.length-1] == '\n') { - line += 1; - continue; - } - - if(start < 0 && new_line) - { - linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, 1); - linebufs.lines[current_line][0] = '\n'; - current_line += 1; - continue; - } - - if(start < 0) - { - start = cast(i64)i; - } - - if(new_line) - { - linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (i-start)); - linebufs.lines[current_line][0 .. i-start] = buffer.data[start .. i]; - current_line += 1; - start = -1; - continue; - } - - if(i == buffer.length-1) - { - linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (buffer.length-start)); - linebufs.lines[current_line][0 .. buffer.length-start] = buffer.data[start .. buffer.length]; + current.text = AllocArray!(u8)(&linebufs.arena, 1); + current.text[0] = 0; + count += 1; } } } @@ -178,6 +227,7 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length) void Move(FlatBuffer* buffer, Input key, Modifier md) { + Logf("key: %s", key); if(md & (MD.LeftShift | MD.RightShift)) { switch(key) @@ -254,6 +304,11 @@ Move(FlatBuffer* buffer, Input key, Modifier md) break; } + if(buffer.buf_pos == buffer.length) + { + break; + } + Walk!(WalkDir.Forward)(buffer); } } @@ -275,11 +330,9 @@ Move(FlatBuffer* buffer, Input key, Modifier md) if(buffer.data[buffer.buf_pos] == '\n') { - Logf("1"); new_l += 1; if(new_l == 2) { - Logf("2"); Walk!(WalkDir.Forward)(buffer); break; } @@ -300,6 +353,11 @@ Move(FlatBuffer* buffer, Input key, Modifier md) break; } + if(buffer.buf_pos == buffer.length) + { + break; + } + Walk!(WalkDir.Forward)(buffer); } } @@ -322,10 +380,33 @@ Move(FlatBuffer* buffer, Input key, Modifier md) } } - Logf("%s %s", buffer.buf_pos, buffer.pos.v); + AdjustOffset(buffer); + Logf("buf_pos %s pos %s", buffer.buf_pos, buffer.pos.v); + //VerifyPosition(buffer, key, md); } +void +AdjustOffset(FlatBuffer* buffer) +{ + with(buffer) + { + i64 screen_pos = pos.y - offset; + i64 start = 1; + i64 end = rows-1; + if(offset > 0 && screen_pos < start) + { + Logf("rows %s offset %s adjusting by %s - %s", rows, offset, screen_pos, start); + offset += screen_pos - start; + } + else if(screen_pos > end) + { + Logf("rows %s offset %s adjusting by %s - %s", rows, offset, screen_pos, end); + offset += screen_pos - end; + } + } +} + u64 VecToPos(FlatBuffer* buffer, I64Vec2 vec) { @@ -428,79 +509,9 @@ AdjustLinePos(FlatBuffer* buffer, i64 adj) } } -pragma(inline) i64 -MoveToPrevLine(FlatBuffer* buffer) -{ - i64 adj = 0; - - with(buffer) - { - if(pos.y == 0) - { - adj = -pos.x; - pos.x = 0; - buf_pos = 0; - } - else - { - i64 p = buf_pos; - if(buffer.data[p] == '\n') - { - p -= 1; - } - - for(; p > 0 && data[p] != '\n'; p -= 1) {} - - buf_pos = p; - pos.y -= 1; - Logf("pos in line %s", PosInLine(buffer, p)); - pos.x = PosInLine(buffer, p); - } - } - - return adj; -} - -pragma(inline) i64 -MoveToNextLine(FlatBuffer* buffer) -{ - i64 adj = 0; - - with(buffer) - { - if(pos.y == lf_count) - { - Logf("upper"); - adj = length - buf_pos; - pos.x += adj; - buf_pos += adj; - } - else - { - Logf("lower"); - i64 p = buf_pos; - u8 prev = 0; - for(; p < length; p += 1) - { - if(p > 0 && data[p-1] == '\n') - { - break; - } - } - - pos.x = 0; - pos.y += 1; - buf_pos = p; - } - } - - return adj; -} - pragma(inline) void Walk(alias dir)(FlatBuffer* buffer) { - Logf("%s", buffer.pos.v); static if(dir == WalkDir.Backward) { buffer.buf_pos -= 1; @@ -514,8 +525,11 @@ Walk(alias dir)(FlatBuffer* buffer) buffer.pos.x -= 1; } - assert(buffer.pos.y >= 0); - assert(buffer.pos.x >= 0); + if(buffer.pos.x < 0 || buffer.pos.y < 0) + { + Logf("walked %s buf_pos %s pos %s", dir, buffer.buf_pos, buffer.pos.v); + assert(pos.x >= 0 && pos.y >= 0); + } } else static if(dir == WalkDir.Forward) { @@ -530,7 +544,6 @@ Walk(alias dir)(FlatBuffer* buffer) buffer.pos.x += 1; } } - Logf("%s", buffer.pos.v); } pragma(inline) void @@ -554,8 +567,6 @@ VerifyPosition(FlatBuffer* buffer, Input input, Modifier md) p.x += 1; } - Logf("%s %s", p.v, pos.v); - assert(p.y <= pos.y); } @@ -576,16 +587,25 @@ PosInLine(FlatBuffer* buffer, i64 pos) i64 p = pos; i64 line_pos = 0; - if(buffer.data[p] == '\n') + i64 skip_lf = buffer.data[pos] == '\n'; + + i64 lf_count = 0; + for(; p > 0; p -= 1, line_pos += 1) { - p -= 1; - line_pos += 1; + if(buffer.data[p] == '\n') + { + if(skip_lf) + { + skip_lf = false; + } + else + { + break; + } + } } - for(; p > 0 && buffer.data[p] != '\n'; p -= 1, line_pos += 1) - { - - } + Logf("line_pos %s", line_pos); return line_pos; } diff --git a/src/editor/editor.d b/src/editor/editor.d index e881f72..13ca52a 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -35,6 +35,7 @@ struct Editor FlatBuffer buf; Tokenizer tk; LineBuffers linebufs; + i64 offset; Vec2 cursor_pos; Vec2 select_start; @@ -276,6 +277,11 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs) case Input.Left: case Input.Right: { + UIPanel* p = GetFocusedPanel(); + if(!Nil(p)) + { + Move(&p.ed.buf, key, node.value.md); + } } break; default: break; } diff --git a/src/editor/ui.d b/src/editor/ui.d index 8b0f51d..e05d39b 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -1131,27 +1131,42 @@ DrawLine(UIItem* item) f32 x = item.rect.x0; FontAtlas* atlas = &ctx.atlas_buf.atlas; + UIPanel* panel = GetFocusedPanel(); + bool active = panel.id == item.parent.key.text; + bool edit_mode = EditModeActive(); + + Vertex* v; + if(item.highlighted_char >= 0 && active) + { + v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count; + AddUIIndices(ctx); + } + for(u64 i = 0; i < item.key.text.length && item.key.text[i] != '\0'; i += 1) { u8 ch = item.key.text.ptr[i]; if(ch < 128) { - DrawGlyph(item, &atlas.glyphs[ch], atlas.size/ctx.text_size, &x, y, i == item.highlighted_char); + DrawGlyph(item, &atlas.glyphs[ch], atlas.size/ctx.text_size, &x, y, !edit_mode && i == item.highlighted_char); } } - if(item.highlighted_char >= 0) + if(v) { y = item.rect.y0 + 2; x = item.rect.x0; - UIPanel* panel = GetFocusedPanel(); - bool text_in_focus = panel.id == item.parent.key.text; f32 adv = atlas.glyphs[' '].advance; - Vertex* v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count; for(u64 i = 0; i < item.highlighted_char; i += 1) { - x += adv; + if(item.key.text[i] == '\t') + { + x += adv*2; + } + else + { + x += adv; + } } v.dst_start.x = x; @@ -1160,7 +1175,7 @@ DrawLine(UIItem* item) v.dst_end.y = y + ctx.text_size - 2; v.cols = Vec4(1.0); - if(text_in_focus) + if(edit_mode) { v.dst_end.x = v.dst_start.x + 2; } @@ -1175,7 +1190,6 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool highlig UICtx* ctx = GetCtx(); Vertex* bg_v = null, v = null; f32 h; - bool text_in_focus = false; if(glyph.ch == '\t') { @@ -1203,6 +1217,11 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool highlig v.src_end.y = glyph.atlas_bottom; } + if(highlight) + { + col = Vec4(Vec3(1.0)-col.xyz, 1.0); + } + v.cols = col; v.texture = 1; diff --git a/src/editor/widgets.d b/src/editor/widgets.d index 5587558..beac213 100644 --- a/src/editor/widgets.d +++ b/src/editor/widgets.d @@ -5,7 +5,7 @@ import ui : Nil; import ui; import editor; import std.format : sformat; -import std.math.rounding : ceil; +import std.math.rounding : ceil, floor; const UIPanel g_ui_nil_panel; UIPanel* g_UI_NIL_PANEL; @@ -42,6 +42,7 @@ struct TextPart { UIItem* item; TextPart* next; + u32 count; } UIItem* @@ -175,26 +176,38 @@ EditorView(UIPanel* panel) { UICtx* ctx = GetCtx(); UIItem* item = Panel(panel, UIF.Clickable); + Editor* ed = panel.ed; bool focused = panel == GetFocusedPanel(); - 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) + + f32 text_size = 16.0; + f32 height = 0.0; + + i64 rows = cast(i64)ceil(item.size.y/text_size); + if(ed.buf.rows != rows) { - u8[] line = panel.ed.linebufs.lines[i]; - if(line.length > 0) + Logf("editor height %s text_size %s rows %s", item.size.y, text_size, rows); + ed.buf.rows = rows; + } + + GetLines(&ed.buf, &ed.linebufs, rows); + u64 i = 0; + for(LineBuffer* buf = ed.linebufs.first; buf != null; buf = buf.next, i += 1) + { + if(buf.text.length > 0) { - if(panel.ed.buf.pos.y == i && focused) + i64 line_no = i+ed.buf.offset; + if(ed.buf.pos.y == line_no && focused) { - SetHighlightedChar(panel.ed.buf.pos.x); + SetHighlightedChar(ed.buf.pos.x); } - TextPart* tp = WrappedTextLine(line, panel.id, 16.0, i); + TextPart* tp = WrappedTextLine(buf.text, panel.id, text_size, line_no); + height += (text_size * tp.count); if(TextClicked(tp)) { SetFocusedPanel(panel); } - } SetHighlightedChar(-1); @@ -242,6 +255,8 @@ WrappedTextLine(u8[] text, u8[] parent_id, f32 text_size, u64 line_no) Node!(u8[])* lines = MakeMultiline(text, parent.size.x, parent_id, line_no); for(Node!(u8[])* line = lines; line != null; line = line.next, tp = tp.next) { + part.count += 1; + tp.item = Get(line.value); tp.next = ScratchAlloc!(TextPart)(); tp.next.item = g_UI_NIL;