more fixes for text buffer, add scrolling

This commit is contained in:
Matthew 2025-09-20 15:48:00 +10:00
parent 6503dae769
commit c9617b701b
4 changed files with 204 additions and 144 deletions

View File

@ -13,12 +13,22 @@ struct FlatBuffer
u64 length; u64 length;
u64 lf_count; u64 lf_count;
i64 buf_pos; i64 buf_pos;
i64 offset;
i64 rows;
} }
struct LineBuffers struct LineBuffers
{ {
Arena arena; Arena arena;
u8[][] lines; LineBuffer* first;
LineBuffer* last;
u64 count;
}
struct LineBuffer
{
u8[] text;
LineBuffer* next;
} }
enum WalkDir enum WalkDir
@ -102,6 +112,8 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos)
buffer.length += length; buffer.length += length;
Reset(&buffer.arena); Reset(&buffer.arena);
AdjustOffset(buffer);
} }
void void
@ -118,22 +130,34 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, I64Vec2 pos)
Insert(buffer, insert, length, VecToPos(buffer, pos)); Insert(buffer, insert, length, VecToPos(buffer, pos));
} }
void
GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 length)
{
GetLines(buffer, linebufs, buffer.offset, length);
}
void void
GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length) GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length)
{ {
assert(linebufs != null, "GetLines failure: linebufs is null"); 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); Reset(&linebufs.arena);
linebufs.lines = AllocArray!(u8[])(&linebufs.arena, length); linebufs.first = Alloc!(LineBuffer)(&linebufs.arena);
linebufs.count = 0;
i64 start = -1; i64 start = -1;
u64 line = 0; u64 line = 0;
u64 current_line = 0; if(buffer.length == 0)
{
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) for(u64 i = 0; i < buffer.length; i += 1)
{ {
if(current_line == length) if(count == length)
{ {
break; break;
} }
@ -145,11 +169,19 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length)
continue; continue;
} }
if(line < start_line)
{
continue;
}
if(start < 0 && new_line) if(start < 0 && new_line)
{ {
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, 1); current.text = AllocArray!(u8)(&arena, 1);
linebufs.lines[current_line][0] = '\n'; current.text[0] = '\n';
current_line += 1; count += 1;
current.next = Alloc!(LineBuffer)(&arena);
current = current.next;
continue; continue;
} }
@ -160,17 +192,34 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length)
if(new_line) if(new_line)
{ {
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (i-start)); current.text = AllocArray!(u8)(&arena, i-start);
linebufs.lines[current_line][0 .. i-start] = buffer.data[start .. i]; current.text[0 .. i-start] = buffer.data[start .. i];
current_line += 1; count += 1;
start = -1; start = -1;
current.next = Alloc!(LineBuffer)(&arena);
current = current.next;
continue; continue;
} }
if(i == buffer.length-1) if(i == buffer.length-1)
{ {
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (buffer.length-start)); current.text = AllocArray!(u8)(&arena, (buffer.length-start));
linebufs.lines[current_line][0 .. buffer.length-start] = buffer.data[start .. buffer.length]; current.text[0 .. buffer.length-start] = buffer.data[start .. buffer.length];
current.next = Alloc!(LineBuffer)(&arena);
current = current.next;
count += 1;
}
}
if(buffer.length > 0 && buffer.data[buffer.length-1] == '\n')
{
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 void
Move(FlatBuffer* buffer, Input key, Modifier md) Move(FlatBuffer* buffer, Input key, Modifier md)
{ {
Logf("key: %s", key);
if(md & (MD.LeftShift | MD.RightShift)) if(md & (MD.LeftShift | MD.RightShift))
{ {
switch(key) switch(key)
@ -254,6 +304,11 @@ Move(FlatBuffer* buffer, Input key, Modifier md)
break; break;
} }
if(buffer.buf_pos == buffer.length)
{
break;
}
Walk!(WalkDir.Forward)(buffer); Walk!(WalkDir.Forward)(buffer);
} }
} }
@ -275,11 +330,9 @@ Move(FlatBuffer* buffer, Input key, Modifier md)
if(buffer.data[buffer.buf_pos] == '\n') if(buffer.data[buffer.buf_pos] == '\n')
{ {
Logf("1");
new_l += 1; new_l += 1;
if(new_l == 2) if(new_l == 2)
{ {
Logf("2");
Walk!(WalkDir.Forward)(buffer); Walk!(WalkDir.Forward)(buffer);
break; break;
} }
@ -300,6 +353,11 @@ Move(FlatBuffer* buffer, Input key, Modifier md)
break; break;
} }
if(buffer.buf_pos == buffer.length)
{
break;
}
Walk!(WalkDir.Forward)(buffer); 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); //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 u64
VecToPos(FlatBuffer* buffer, I64Vec2 vec) 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 pragma(inline) void
Walk(alias dir)(FlatBuffer* buffer) Walk(alias dir)(FlatBuffer* buffer)
{ {
Logf("%s", buffer.pos.v);
static if(dir == WalkDir.Backward) static if(dir == WalkDir.Backward)
{ {
buffer.buf_pos -= 1; buffer.buf_pos -= 1;
@ -514,8 +525,11 @@ Walk(alias dir)(FlatBuffer* buffer)
buffer.pos.x -= 1; buffer.pos.x -= 1;
} }
assert(buffer.pos.y >= 0); if(buffer.pos.x < 0 || buffer.pos.y < 0)
assert(buffer.pos.x >= 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) else static if(dir == WalkDir.Forward)
{ {
@ -530,7 +544,6 @@ Walk(alias dir)(FlatBuffer* buffer)
buffer.pos.x += 1; buffer.pos.x += 1;
} }
} }
Logf("%s", buffer.pos.v);
} }
pragma(inline) void pragma(inline) void
@ -554,8 +567,6 @@ VerifyPosition(FlatBuffer* buffer, Input input, Modifier md)
p.x += 1; p.x += 1;
} }
Logf("%s %s", p.v, pos.v);
assert(p.y <= pos.y); assert(p.y <= pos.y);
} }
@ -576,16 +587,25 @@ PosInLine(FlatBuffer* buffer, i64 pos)
i64 p = pos; i64 p = pos;
i64 line_pos = 0; i64 line_pos = 0;
i64 skip_lf = buffer.data[pos] == '\n';
i64 lf_count = 0;
for(; p > 0; p -= 1, line_pos += 1)
{
if(buffer.data[p] == '\n') if(buffer.data[p] == '\n')
{ {
p -= 1; if(skip_lf)
line_pos += 1;
}
for(; p > 0 && buffer.data[p] != '\n'; p -= 1, line_pos += 1)
{ {
skip_lf = false;
} }
else
{
break;
}
}
}
Logf("line_pos %s", line_pos);
return line_pos; return line_pos;
} }

View File

@ -35,6 +35,7 @@ struct Editor
FlatBuffer buf; FlatBuffer buf;
Tokenizer tk; Tokenizer tk;
LineBuffers linebufs; LineBuffers linebufs;
i64 offset;
Vec2 cursor_pos; Vec2 cursor_pos;
Vec2 select_start; Vec2 select_start;
@ -276,6 +277,11 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
case Input.Left: case Input.Left:
case Input.Right: case Input.Right:
{ {
UIPanel* p = GetFocusedPanel();
if(!Nil(p))
{
Move(&p.ed.buf, key, node.value.md);
}
} break; } break;
default: break; default: break;
} }

View File

@ -1131,28 +1131,43 @@ DrawLine(UIItem* item)
f32 x = item.rect.x0; f32 x = item.rect.x0;
FontAtlas* atlas = &ctx.atlas_buf.atlas; 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) for(u64 i = 0; i < item.key.text.length && item.key.text[i] != '\0'; i += 1)
{ {
u8 ch = item.key.text.ptr[i]; u8 ch = item.key.text.ptr[i];
if(ch < 128) 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; y = item.rect.y0 + 2;
x = item.rect.x0; x = item.rect.x0;
UIPanel* panel = GetFocusedPanel();
bool text_in_focus = panel.id == item.parent.key.text;
f32 adv = atlas.glyphs[' '].advance; 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) for(u64 i = 0; i < item.highlighted_char; i += 1)
{
if(item.key.text[i] == '\t')
{
x += adv*2;
}
else
{ {
x += adv; x += adv;
} }
}
v.dst_start.x = x; v.dst_start.x = x;
v.dst_start.y = y; v.dst_start.y = y;
@ -1160,7 +1175,7 @@ DrawLine(UIItem* item)
v.dst_end.y = y + ctx.text_size - 2; v.dst_end.y = y + ctx.text_size - 2;
v.cols = Vec4(1.0); v.cols = Vec4(1.0);
if(text_in_focus) if(edit_mode)
{ {
v.dst_end.x = v.dst_start.x + 2; 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(); UICtx* ctx = GetCtx();
Vertex* bg_v = null, v = null; Vertex* bg_v = null, v = null;
f32 h; f32 h;
bool text_in_focus = false;
if(glyph.ch == '\t') 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; v.src_end.y = glyph.atlas_bottom;
} }
if(highlight)
{
col = Vec4(Vec3(1.0)-col.xyz, 1.0);
}
v.cols = col; v.cols = col;
v.texture = 1; v.texture = 1;

View File

@ -5,7 +5,7 @@ import ui : Nil;
import ui; import ui;
import editor; import editor;
import std.format : sformat; import std.format : sformat;
import std.math.rounding : ceil; import std.math.rounding : ceil, floor;
const UIPanel g_ui_nil_panel; const UIPanel g_ui_nil_panel;
UIPanel* g_UI_NIL_PANEL; UIPanel* g_UI_NIL_PANEL;
@ -42,6 +42,7 @@ struct TextPart
{ {
UIItem* item; UIItem* item;
TextPart* next; TextPart* next;
u32 count;
} }
UIItem* UIItem*
@ -175,26 +176,38 @@ EditorView(UIPanel* panel)
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
UIItem* item = Panel(panel, UIF.Clickable); UIItem* item = Panel(panel, UIF.Clickable);
Editor* ed = panel.ed;
bool focused = panel == GetFocusedPanel(); bool focused = panel == GetFocusedPanel();
u64 rows = cast(u64)ceil(item.size.y/16.0);
GetLines(&panel.ed.buf, &panel.ed.linebufs, 0, rows); f32 text_size = 16.0;
for(u64 i = 0; i < panel.ed.linebufs.lines.length; i += 1) 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]; Logf("editor height %s text_size %s rows %s", item.size.y, text_size, rows);
if(line.length > 0) ed.buf.rows = rows;
{
if(panel.ed.buf.pos.y == i && focused)
{
SetHighlightedChar(panel.ed.buf.pos.x);
} }
TextPart* tp = WrappedTextLine(line, panel.id, 16.0, i); 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)
{
i64 line_no = i+ed.buf.offset;
if(ed.buf.pos.y == line_no && focused)
{
SetHighlightedChar(ed.buf.pos.x);
}
TextPart* tp = WrappedTextLine(buf.text, panel.id, text_size, line_no);
height += (text_size * tp.count);
if(TextClicked(tp)) if(TextClicked(tp))
{ {
SetFocusedPanel(panel); SetFocusedPanel(panel);
} }
} }
SetHighlightedChar(-1); 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); 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) for(Node!(u8[])* line = lines; line != null; line = line.next, tp = tp.next)
{ {
part.count += 1;
tp.item = Get(line.value); tp.item = Get(line.value);
tp.next = ScratchAlloc!(TextPart)(); tp.next = ScratchAlloc!(TextPart)();
tp.next.item = g_UI_NIL; tp.next.item = g_UI_NIL;