diff --git a/src/editor/buffer.d b/src/editor/buffer.d index b88adae..cb7f8dd 100644 --- a/src/editor/buffer.d +++ b/src/editor/buffer.d @@ -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)); diff --git a/src/editor/editor.d b/src/editor/editor.d index fd7bb5e..e881f72 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -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; diff --git a/src/editor/ui.d b/src/editor/ui.d index 10c13fb..8b0f51d 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -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 diff --git a/src/editor/widgets.d b/src/editor/widgets.d index f1b619e..5587558 100644 --- a/src/editor/widgets.d +++ b/src/editor/widgets.d @@ -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)