start work on text input

This commit is contained in:
Matthew 2025-09-17 06:28:01 +10:00
parent 13aedaacff
commit d233c65304
5 changed files with 266 additions and 134 deletions

@ -1 +1 @@
Subproject commit 6757559089314da06956460c32ff632db7d10f88 Subproject commit ff94c5cb5e4b3c581521452a2a50b363a09cd8d6

View File

@ -1,25 +1,18 @@
import dlib.aliases; import dlib;
import dlib.math;
import dlib.alloc;
import dlib.util;
import core.stdc.stdio : EOF; import core.stdc.stdio : EOF;
import parsing; import parsing;
import std.format : sformat; import std.format : sformat;
import std.math.algebraic : abs;
struct FlatBuffer struct FlatBuffer
{ {
Tokenizer tk; Tokenizer tk;
Arena arena; Arena arena;
u8[] data; U64Vec2 pos;
u64 length; u8[] data;
u64 line_count; u64 length;
Range pos; u64 line_count;
} u64 buf_pos;
struct Range
{
u32 x;
u32 y;
} }
struct LineBuffers struct LineBuffers
@ -31,7 +24,7 @@ struct LineBuffers
FlatBuffer FlatBuffer
CreateFlatBuffer(u8[] data) 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); u8[] buf = MAllocArray!(u8)(capacity);
buf[0 .. data.length] = data[0 .. data.length]; buf[0 .. data.length] = data[0 .. data.length];
@ -70,6 +63,7 @@ CreateLineBuffers(u64 arena_size)
void void
Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos) 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) if(buffer.length + length > buffer.data.length)
{ {
FlatBuffer new_buf = CreateFlatBuffer(buffer.data); FlatBuffer new_buf = CreateFlatBuffer(buffer.data);
@ -99,11 +93,11 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos)
} }
void 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"); 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 // 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 void
RangeToPos(FlatBuffer* buffer, Range range) Move(FlatBuffer* buffer, Input key, Modifier md)
{ {
u64 buffer_pos; if(md & (MD.LeftShift | MD.RightShift))
u32 line, col;
for(u64 i = 0; i < buffer.length; i += 1)
{ {
if(range.y == line) switch(key)
{ {
buffer_pos = i; case Input.Up:
break; {
} } 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; if(range.y == line)
break; {
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; return buffer_pos;
} }

View File

@ -11,6 +11,7 @@ import ui;
import widgets : Nil; import widgets : Nil;
import widgets; import widgets;
import std.format;
import std.stdio; import std.stdio;
import std.exception; import std.exception;
@ -22,6 +23,8 @@ struct EditorCtx
UIPanel* base_panel; UIPanel* base_panel;
u64 panel_id; u64 panel_id;
EditState state; EditState state;
u8[128] input_buf;
u32 icount;
} }
struct Editor struct Editor
@ -108,6 +111,8 @@ CreateEditor(EditorCtx* ctx)
Editor* ed = Alloc!(Editor)(&ctx.arena); Editor* ed = Alloc!(Editor)(&ctx.arena);
ed.arena = CreateArena(MB(4)); ed.arena = CreateArena(MB(4));
ed.buf = CreateFlatBuffer([]);
ed.linebufs = CreateLineBuffers(MB(1));
return ed; 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 void
HandleInputs(EditorCtx* ctx, Inputs* inputs) HandleInputs(EditorCtx* ctx, Inputs* inputs)
{ {
@ -202,111 +221,54 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
if (pressed) if (pressed)
{ {
switch(key) if(key == Input.Escape)
{ {
case Input.i: ctx.state = ES.NormalMode;
{ InsertInputToBuf(ctx);
if(ctx.state == ES.NormalMode) taken = true;
}
else if(ctx.state == ES.InputMode)
{
taken = HandleInputMode(ctx, node.value);
}
else
{
switch(key)
{
case Input.i:
{ {
ctx.state = ES.InputMode; ctx.state = ES.InputMode;
taken = true; taken = true;
} } break;
} break; case Input.Semicolon:
case Input.Esc:
{
if(ctx.state == ES.InputMode || ctx.state == ES.CmdOpen)
{ {
ctx.state = ES.NormalMode; if(node.value.md & (MD.LeftShift | MD.RightShift))
taken = true;
}
} break;
case Input.Semicolon:
{
if(node.value.md & (MD.LeftShift | MD.RightShift))
{
if(ctx.state != ES.NormalMode)
{ {
ctx.state = ES.CmdOpen; ctx.state = ES.CmdOpen;
taken = true; taken = true;
} }
} } break;
} break; case Input.v:
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)
{ {
AddEditor(ctx, GetFocusedPanel(), A2D.X); AddEditor(ctx, GetFocusedPanel(), A2D.X);
} taken = true;
} break; } break;
case Input.c: case Input.c:
{
if(ctx.state == ES.NormalMode)
{ {
AddEditor(ctx, GetFocusedPanel(), A2D.Y); AddEditor(ctx, GetFocusedPanel(), A2D.Y);
} taken = true;
} break; } break;
case Input.d: case Input.d:
{
if(node.value.md & (MD.LeftShift | MD.RightShift))
{ {
static dbg = false; } break;
dbg = !dbg; case Input.Up:
SetDebug(dbg); case Input.Down:
} case Input.Left:
} break; case Input.Right:
case Input.Up:
case Input.Down:
case Input.Left:
case Input.Right:
{
if(false)
{ {
UIPanel* panel = GetFocusedPanel(); } break;
UIPanel* focused = g_UI_NIL_PANEL; default: break;
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;
} }
} }
@ -315,6 +277,60 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
DLLRemove(&inputs.list, node, null); 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;
} }
/* /*

View File

@ -149,6 +149,8 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [
void void
Tokenize(FlatBuffer* fb) Tokenize(FlatBuffer* fb)
{ {
if(fb.data.length == 0) return;
Tokenizer* tk = &fb.tk; Tokenizer* tk = &fb.tk;
u64 semi_count; u64 semi_count;

View File

@ -5,6 +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;
const UIPanel g_ui_nil_panel; const UIPanel g_ui_nil_panel;
UIPanel* g_UI_NIL_PANEL; UIPanel* g_UI_NIL_PANEL;
@ -175,14 +176,20 @@ EditorView(UIPanel* panel)
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
UIItem* item = Panel(panel, UIF.Clickable); UIItem* item = Panel(panel, UIF.Clickable);
TextPart* tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 16.0, 0); u64 rows = cast(u64)ceil(item.size.y/16.0);
if(TextClicked(tp)) SetFocusedPanel(panel); GetLines(&panel.ed.buf, &panel.ed.linebufs, 0, rows);
tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 16.0, 1); for(u64 i = 0; i < panel.ed.linebufs.lines.length; i += 1)
if(TextClicked(tp)) SetFocusedPanel(panel); {
tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 16.0, 2); u8[] line = panel.ed.linebufs.lines[i];
if(TextClicked(tp)) SetFocusedPanel(panel); if(line.length > 0)
tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 16.0, 3); {
if(TextClicked(tp)) SetFocusedPanel(panel); TextPart* tp = WrappedTextLine(line, panel.id, 16.0, i);
if(TextClicked(tp))
{
SetFocusedPanel(panel);
}
}
}
Signal(item); Signal(item);