text buffer working

This commit is contained in:
Matthew 2025-09-20 09:05:04 +10:00
parent d233c65304
commit 6503dae769
4 changed files with 412 additions and 81 deletions

View File

@ -8,11 +8,11 @@ struct FlatBuffer
{
Tokenizer tk;
Arena arena;
U64Vec2 pos;
I64Vec2 pos;
u8[] data;
u64 length;
u64 line_count;
u64 buf_pos;
u64 lf_count;
i64 buf_pos;
}
struct LineBuffers
@ -21,6 +21,13 @@ struct LineBuffers
u8[][] lines;
}
enum WalkDir
{
None = 0,
Backward = -1,
Forward = +1,
}
FlatBuffer
CreateFlatBuffer(u8[] data)
{
@ -29,12 +36,12 @@ CreateFlatBuffer(u8[] data)
u8[] buf = MAllocArray!(u8)(capacity);
buf[0 .. data.length] = data[0 .. data.length];
u64 line_count = 1;
u64 lf_count = 0;
for(u64 i = 0; i < cast(u64)data.length; i += 1)
{
if(data.ptr[i] == '\n')
{
line_count += 1;
lf_count += 1;
}
}
@ -42,7 +49,7 @@ CreateFlatBuffer(u8[] data)
arena: CreateArena(MB(1)),
data: buf,
length: cast(u64)data.length,
line_count: line_count,
lf_count: lf_count,
};
fb.tk = CreateTokenizer(&fb);
@ -73,9 +80,14 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos)
for(u64 i = 0; i < length; i += 1)
{
buffer.buf_pos += 1;
buffer.pos.x += 1;
if(insert.ptr[i] == '\n')
{
buffer.line_count += 1;
buffer.lf_count += 1;
buffer.pos.y += 1;
buffer.pos.x = 0;
}
}
@ -93,19 +105,25 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos)
}
void
Insert(FlatBuffer* buffer, u8[] insert, u64 length, UVec2 pos)
Insert(FlatBuffer* buffer, u8[] insert, u64 length)
{
assert(pos.y <= buffer.line_count, "Insert failure: y provided is greater than line_count");
Insert(buffer, insert, length, buffer.buf_pos);
}
void
Insert(FlatBuffer* buffer, u8[] insert, u64 length, I64Vec2 pos)
{
assert(pos.y <= buffer.lf_count, "Insert failure: y provided is greater than lf_count");
Insert(buffer, insert, length, VecToPos(buffer, pos));
}
// TODO: handle case for when lines are longer than line buffer
void
GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length)
{
assert(linebufs != null, "GetLines failure: linebufs is null");
length = length > buffer.line_count ? buffer.line_count : length;
u64 line_count = buffer.lf_count + 1;
length = length > line_count ? line_count : length;
Reset(&linebufs.arena);
linebufs.lines = AllocArray!(u8[])(&linebufs.arena, length);
@ -138,7 +156,6 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length)
if(start < 0)
{
start = cast(i64)i;
continue;
}
if(new_line)
@ -210,45 +227,95 @@ Move(FlatBuffer* buffer, Input key, Modifier md)
switch(key)
{
case Input.Down:
{
if(buffer.pos.y < buffer.lf_count)
{
i64 x = buffer.pos.x;
for(;;)
{
if(buffer.data[buffer.buf_pos] == '\n')
{
Walk!(WalkDir.Forward)(buffer);
break;
}
Walk!(WalkDir.Forward)(buffer);
}
for(;;)
{
if(buffer.data[buffer.buf_pos] == '\n')
{
break;
}
if(buffer.pos.x == x)
{
break;
}
Walk!(WalkDir.Forward)(buffer);
}
}
} break;
case Input.Up:
{
if((Input.Up && buffer.pos.y > 0) || (Input.Down && buffer.pos.y < buffer.line_count))
if(buffer.pos.y > 0)
{
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;
i64 new_l = 0;
i64 x = buffer.pos.x;
for(;;)
{
if(buffer.buf_pos == 0)
{
break;
}
Walk!(WalkDir.Backward)(buffer);
if(buffer.data[buffer.buf_pos] == '\n')
{
Logf("1");
new_l += 1;
if(new_l == 2)
{
Logf("2");
Walk!(WalkDir.Forward)(buffer);
break;
}
}
}
Logf("sol reached %s", buffer.pos.v);
for(;;)
{
if(buffer.data[buffer.buf_pos] == '\n')
{
break;
}
if(buffer.pos.x >= x)
{
break;
}
Walk!(WalkDir.Forward)(buffer);
}
}
} break;
case Input.Left:
{
if(buffer.pos.x != 0)
{
Walk!(WalkDir.Backward)(buffer);
}
} break;
case Input.Right:
{
if((Input.Left && buffer.buf_pos > 0) || (Input.Right && buffer.buf_pos < buffer.length))
if(buffer.buf_pos < buffer.length && buffer.data[buffer.buf_pos] != '\n')
{
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;
}
Walk!(WalkDir.Forward)(buffer);
}
} break;
default: break;
@ -256,19 +323,19 @@ Move(FlatBuffer* buffer, Input key, Modifier md)
}
Logf("%s %s", buffer.buf_pos, buffer.pos.v);
//VerifyPosition(buffer, key, md);
}
u64
RelVecToPos(FlatBuffer* buffer, U64Vec2 vec)
VecToPos(FlatBuffer* buffer, I64Vec2 vec)
{
u64 pos = 0;
u64 pos, line, col;
bool closer_to_start = abs(buffer.pos.y - vec.x) > vec.x;
if(closer_to_start)
{
u32 line, col;
for(; i < buffer.length; pos += 1)
for(; pos < buffer.length; pos += 1)
{
if(range.y == line)
if(vec.y == line)
{
break;
}
@ -279,31 +346,248 @@ RelVecToPos(FlatBuffer* buffer, U64Vec2 vec)
}
}
for(; buffer_pos < buffer.length; pos += 1)
for(; pos < buffer.length; pos += 1)
{
if(col == range.x)
if(buffer.data.ptr[pos] == '\n')
{
break;
}
if(col == vec.x)
{
break;
}
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;
u64 move = buffer.pos.y < vec.y || (buffer.pos.y == vec.y && buffer.pos.x < vec.x) ? -1 : +1;
for(pos = buffer.buf_pos; pos < buffer.length && pos > 0; pos += move)
{
if(vec.y == line)
{
break;
}
if(buffer.data.ptr[pos] == '\n')
{
line += 1;
}
}
for(; pos < buffer.length; pos += move)
{
if(col == vec.x)
{
break;
}
if(buffer.data.ptr[pos] == '\n')
{
break;
}
col += move;
}
}
return pos;
}
u64
VecToPos(FlatBuffer* buffer, U64Vec2 vec)
void
Backspace(FlatBuffer* buffer, u64 length)
{
if(buffer.buf_pos-length > 0)
{
u8[] del = buffer.data[buffer.buf_pos-length .. buffer.buf_pos];
u8 ch = buffer.data[buffer.buf_pos];
buffer.buf_pos -= 1;
Delete(buffer, length, buffer.buf_pos);
if(ch == '\n')
{
return buffer_pos;
}
}
}
void
AdjustLinePos(FlatBuffer* buffer, i64 adj)
{
if(buffer.buf_pos+adj <= 0)
{
buffer.buf_pos = 0;
buffer.pos = 0;
}
else if(buffer.buf_pos+adj >= buffer.length)
{
buffer.buf_pos = buffer.length;
buffer.pos.y = buffer.lf_count;
buffer.pos.x = PosInLine(buffer, buffer.buf_pos);
}
}
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;
if(buffer.data[buffer.buf_pos] == '\n')
{
buffer.pos.y -= 1;
buffer.pos.x = PosInLine(buffer, buffer.buf_pos);
}
else
{
buffer.pos.x -= 1;
}
assert(buffer.pos.y >= 0);
assert(buffer.pos.x >= 0);
}
else static if(dir == WalkDir.Forward)
{
buffer.buf_pos += 1;
if(buffer.data[buffer.buf_pos-1] == '\n')
{
buffer.pos.y += 1;
buffer.pos.x = 0;
}
else
{
buffer.pos.x += 1;
}
}
Logf("%s", buffer.pos.v);
}
pragma(inline) void
VerifyPosition(FlatBuffer* buffer, Input input, Modifier md)
{
debug
{
with(buffer)
{
I64Vec2 p = 0;
i64 i = 0;
for(; i < length && p != pos; i += 1)
{
if(data[i] == '\n')
{
p.x = 0;
p.y += 1;
}
else
{
p.x += 1;
}
Logf("%s %s", p.v, pos.v);
assert(p.y <= pos.y);
}
if(i != buf_pos || p != pos)
{
Logf("Buffer positions not in sync: expected: %s %s %s result: %s %s %s", i, p.v, cast(char)data[i], buf_pos, pos.v, cast(char)data[buf_pos]);
Logf("Input: %s Shift: %s Ctrl: %s", input, md & (MD.LeftShift|MD.RightShift), md & (MD.LeftCtrl|MD.RightCtrl));
assert(false);
}
}
}
}
pragma(inline) i64
PosInLine(FlatBuffer* buffer, i64 pos)
{
assert(pos >= 0 && pos <= buffer.length);
i64 p = pos;
i64 line_pos = 0;
if(buffer.data[p] == '\n')
{
p -= 1;
line_pos += 1;
}
for(; p > 0 && buffer.data[p] != '\n'; p -= 1, line_pos += 1)
{
}
return line_pos;
}
void
@ -786,7 +1070,7 @@ unittest
u8[] data = StringToU8("This is a line.\nThis is a second line,\nalong with a third line.");
FlatBuffer buf = CreateFlatBuffer(data);
assert(buf.line_count == 3, "FlatBuffer line count mismatch");
assert(buf.lf_count == 3, "FlatBuffer line count mismatch");
u8[] insert = StringToU8("Maybe, ");
Insert(&buf, insert, insert.length, Range(x: 0, y: 1));

View File

@ -49,11 +49,13 @@ enum EditState
NormalMode,
InputMode,
CmdOpen,
SetPanelFocus,
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;
bool g_input_mode = false;
void
Cycle(EditorCtx* ctx, Inputs* inputs)
{
@ -63,6 +65,8 @@ Cycle(EditorCtx* ctx, Inputs* inputs)
HandleInputs(ctx, inputs);
g_input_mode = ctx.state == ES.InputMode;
BeginBuild(inputs);
DrawPanels(ctx.base_panel);
@ -103,6 +107,12 @@ CreatePanel(EditorCtx* ctx)
return p;
}
bool
EditModeActive()
{
return g_input_mode;
}
// Load all files then move things into editor after being created when selected
Editor*
@ -203,7 +213,7 @@ InsertInputToBuf(EditorCtx* ctx)
UIPanel* p = GetFocusedPanel();
if(!Nil(p))
{
Insert(&p.ed.buf, ctx.input_buf, ctx.icount, 0);
Insert(&p.ed.buf, ctx.input_buf, ctx.icount);
ctx.icount = 0;
}
}
@ -312,6 +322,10 @@ HandleInputMode(EditorCtx* ctx, InputEvent ev)
switch(ev.key)
{
mixin(CharCases());
case Input.Backspace:
{
} break;
case Input.Escape:
{
ctx.state = ES.NormalMode;

View File

@ -111,6 +111,7 @@ struct UICtx
f32 border_thickness;
f32 corner_radius;
f32 edge_softness;
i64 highlighted_char;
debug u32 item_count;
debug u32 final_count;
@ -149,6 +150,7 @@ struct UIItem
Axis2D layout_axis;
UISize[2] size_info;
Vec2 adjustment;
i64 highlighted_char;
// Calculated Parameters
Vec2 size;
@ -481,10 +483,15 @@ SetAdjustment(Vec2 adj)
g_ui_ctx.adjustment = adj;
}
void
SetHighlightedChar(i64 pos)
{
g_ui_ctx.highlighted_char = pos;
}
void
SetDebug(bool dbg)
{
Logf("Set");
debug
{
g_ui_ctx.dbg = dbg;
@ -578,10 +585,6 @@ BeginBuild(Inputs* inputs)
ctx.inputs = inputs;
memset(ctx.buffers[ctx.f_idx].vtx.ptr, 0, Vertex.sizeof * ctx.buffers[ctx.f_idx].count);
memset(ctx.buffers[ctx.f_idx].idx.ptr, 0, u32.sizeof * ctx.buffers[ctx.f_idx].count);
ctx.buffers[ctx.f_idx].count = 0;
ctx.panel_level = 0;
ctx.root = Root();
@ -651,6 +654,10 @@ PrepRendering(UICtx* ctx)
PushConstants(&ctx.rd, ctx.pipeline, &ctx.pc);
Bind(&ctx.rd, ctx.pipeline, ctx.desc_set);
memset(ctx.buffers[ctx.f_idx].vtx.ptr, 0, Vertex.sizeof * ctx.buffers[ctx.f_idx].count);
memset(ctx.buffers[ctx.f_idx].idx.ptr, 0, u32.sizeof * ctx.buffers[ctx.f_idx].count);
ctx.buffers[ctx.f_idx].count = 0;
}
void
@ -825,6 +832,7 @@ BuildItem(UIItem* item, UISize size_x, UISize size_y, UIFlags properties)
item.corner_radius = ctx.corner_radius;
item.edge_softness = ctx.edge_softness;
item.last_frame = ctx.frame;
item.highlighted_char = ctx.highlighted_char;
item.parent = ctx.top_parent == g_UI_NIL_NODE ? g_UI_NIL : ctx.top_parent.item;
if(!Nil(item.parent))
@ -1100,7 +1108,7 @@ DrawPanelDebug(UICtx* ctx, UIItem* item)
f32 x0 = x;
for(u64 i = 0; i < text.length; i += 1)
{
DrawGlyph(&ctx.atlas_buf.atlas.glyphs[text[i]], ctx.atlas_buf.atlas.size/ctx.text_size, &x0, y);
DrawGlyph(item, &ctx.atlas_buf.atlas.glyphs[text[i]], ctx.atlas_buf.atlas.size/ctx.text_size, &x0, y);
}
f32 pct = item.parent.layout_axis == A2D.X ? item.size_info[A2D.X].value : item.size_info[A2D.Y].value;
@ -1110,7 +1118,7 @@ DrawPanelDebug(UICtx* ctx, UIItem* item)
y += 16.0;
for(u64 i = 0; i < buf.length && buf[i] != 0; i += 1)
{
DrawGlyph(&ctx.atlas_buf.atlas.glyphs[buf[i]], ctx.atlas_buf.atlas.size/ctx.text_size, &x, y);;
DrawGlyph(item, &ctx.atlas_buf.atlas.glyphs[buf[i]], ctx.atlas_buf.atlas.size/ctx.text_size, &x, y);;
}
}
}
@ -1128,22 +1136,46 @@ DrawLine(UIItem* item)
u8 ch = item.key.text.ptr[i];
if(ch < 128)
{
DrawGlyph(&atlas.glyphs[ch], atlas.size/ctx.text_size, &x, y);
DrawGlyph(item, &atlas.glyphs[ch], atlas.size/ctx.text_size, &x, y, i == item.highlighted_char);
}
}
if(item.highlighted_char >= 0)
{
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;
}
v.dst_start.x = x;
v.dst_start.y = y;
v.dst_end.x = x + adv;
v.dst_end.y = y + ctx.text_size - 2;
v.cols = Vec4(1.0);
if(text_in_focus)
{
v.dst_end.x = v.dst_start.x + 2;
}
AddUIIndices(ctx);
}
}
pragma(inline) void
DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool draw_bg = false, Vec4 col = Vec4(1.0))
DrawGlyph(UIItem* item, Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool highlight = false, Vec4 col = Vec4(1.0))
{
UICtx* ctx = GetCtx();
Vertex* bg_v = null, v = null;
f32 h;
if(draw_bg)
{
bg_v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count;
AddUIIndices(ctx);
}
bool text_in_focus = false;
if(glyph.ch == '\t')
{
@ -1178,15 +1210,6 @@ DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool draw_bg = false, Vec4
AddUIIndices(ctx);
*x_pos += glyph.advance * scale;
if(draw_bg)
{
Vec4 white = Vec4(1.0);
bg_v.dst_start = v.dst_start;
bg_v.dst_end = v.dst_end;
bg_v.cols = white;
}
}
pragma(inline) void

View File

@ -176,6 +176,7 @@ EditorView(UIPanel* panel)
UICtx* ctx = GetCtx();
UIItem* item = Panel(panel, UIF.Clickable);
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)
@ -183,14 +184,23 @@ EditorView(UIPanel* panel)
u8[] line = panel.ed.linebufs.lines[i];
if(line.length > 0)
{
if(panel.ed.buf.pos.y == i && focused)
{
SetHighlightedChar(panel.ed.buf.pos.x);
}
TextPart* tp = WrappedTextLine(line, panel.id, 16.0, i);
if(TextClicked(tp))
{
SetFocusedPanel(panel);
}
}
SetHighlightedChar(-1);
}
Signal(item);
if(item.signal & UIS.Clicked)