add animated text scrolling

This commit is contained in:
Matthew 2025-09-21 12:04:45 +10:00
parent c9617b701b
commit 7769a861e3
4 changed files with 302 additions and 383 deletions

View File

@ -8,13 +8,15 @@ struct FlatBuffer
{ {
Tokenizer tk; Tokenizer tk;
Arena arena; Arena arena;
I64Vec2 pos;
u8[] data; u8[] data;
Arena ls_arena;
u64[] line_starts;
u64 length; u64 length;
u64 lf_count; u64 lf_count;
i64 buf_pos; i64 buf_pos;
i64 offset; i64 offset;
i64 rows; i64 rows;
bool dirty;
} }
struct LineBuffers struct LineBuffers
@ -57,6 +59,7 @@ CreateFlatBuffer(u8[] data)
FlatBuffer fb = { FlatBuffer fb = {
arena: CreateArena(MB(1)), arena: CreateArena(MB(1)),
ls_arena: CreateArena(KB(512)),
data: buf, data: buf,
length: cast(u64)data.length, length: cast(u64)data.length,
lf_count: lf_count, lf_count: lf_count,
@ -64,11 +67,37 @@ CreateFlatBuffer(u8[] data)
fb.tk = CreateTokenizer(&fb); fb.tk = CreateTokenizer(&fb);
Tokenize(&fb); Fix(&fb);
return fb; return fb;
} }
void
Fix(FlatBuffer* buffer)
{
with(buffer)
{
Reset(&ls_arena);
line_starts = AllocArray!(u64)(&ls_arena, lf_count+1);
line_starts[0] = 0;
u64 ls_idx = 1;
for(u64 i = 0; i < length; i += 1)
{
if(data[i] == '\n')
{
line_starts[ls_idx] = i+1;
ls_idx += 1;
}
}
dirty = false;
Tokenize(buffer);
}
}
LineBuffers LineBuffers
CreateLineBuffers(u64 arena_size) CreateLineBuffers(u64 arena_size)
{ {
@ -78,42 +107,41 @@ CreateLineBuffers(u64 arena_size)
} }
void void
Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos) Insert(FlatBuffer* fb, u8[] insert, u64 length, u64 pos)
{ {
Logf("%s %s %s", buffer.length, length, buffer.data.length); if(fb.length + length > fb.data.length)
if(buffer.length + length > buffer.data.length)
{ {
FlatBuffer new_buf = CreateFlatBuffer(buffer.data); FlatBuffer new_buf = CreateFlatBuffer(fb.data);
MFreeArray(buffer.data); MFreeArray(fb.data);
*buffer = new_buf; *fb = new_buf;
} }
u64 prev_lf = fb.lf_count;
for(u64 i = 0; i < length; i += 1) for(u64 i = 0; i < length; i += 1)
{ {
buffer.buf_pos += 1; fb.buf_pos += 1;
buffer.pos.x += 1;
if(insert.ptr[i] == '\n') if(insert.ptr[i] == '\n')
{ {
buffer.lf_count += 1; fb.lf_count += 1;
buffer.pos.y += 1;
buffer.pos.x = 0;
} }
} }
u64 temp_len = buffer.length-pos; u64 temp_len = fb.length-pos;
u8[] temp = AllocArray!(u8)(&buffer.arena, temp_len); u8[] temp = AllocArray!(u8)(&fb.arena, temp_len);
temp[0 .. temp_len] = buffer.data[pos .. pos+temp_len]; temp[0 .. temp_len] = fb.data[pos .. pos+temp_len];
buffer.data[pos .. pos+length] = insert[0 .. length]; fb.data[pos .. pos+length] = insert[0 .. length];
pos += length; pos += length;
buffer.data[pos .. pos+temp_len] = temp[0 .. temp_len]; fb.data[pos .. pos+temp_len] = temp[0 .. temp_len];
buffer.length += length; fb.length += length;
Reset(&buffer.arena); Fix(fb);
AdjustOffset(buffer); Reset(&fb.arena);
AdjustOffset(fb);
} }
void void
@ -122,12 +150,60 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length)
Insert(buffer, insert, length, buffer.buf_pos); Insert(buffer, insert, length, buffer.buf_pos);
} }
void pragma(inline) u64
Insert(FlatBuffer* buffer, u8[] insert, u64 length, I64Vec2 pos) CurrentLine(FlatBuffer* fb)
{ {
assert(pos.y <= buffer.lf_count, "Insert failure: y provided is greater than lf_count"); return LineFromPos(fb, fb.buf_pos);
}
Insert(buffer, insert, length, VecToPos(buffer, pos)); pragma(inline) u64
CurrentCol(FlatBuffer* fb)
{
return LinePos(fb, fb.buf_pos);
}
pragma(inline) u64
LinePos(FlatBuffer* fb, u64 pos)
{
u64 line = LineFromPos(fb, pos);
return pos - fb.line_starts[line];
}
pragma(inline) U64Vec2
VecPos(FlatBuffer* fb)
{
return U64Vec2(CurrentCol(fb), CurrentLine(fb));
}
pragma(inline) u64
LineFromPos(FlatBuffer* fb, u64 pos)
{
u64 line = 0;
for(u64 i = 0; i < fb.line_starts.length; i += 1)
{
if(fb.line_starts[i] > pos)
{
break;
}
line = i;
}
return line;
}
pragma(inline) u64
LineLength(FlatBuffer* fb, u64 line)
{
u64 len = 0;
if(line < fb.line_starts.length)
{
u64 start = fb.line_starts[line];
u64 end = fb.line_starts.length-1 > line ? fb.line_starts[line+1] : fb.length;
len = end-start;
}
return len;
} }
void void
@ -145,89 +221,94 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length)
linebufs.first = Alloc!(LineBuffer)(&linebufs.arena); linebufs.first = Alloc!(LineBuffer)(&linebufs.arena);
linebufs.count = 0; linebufs.count = 0;
i64 start = -1; u64 total_lines = buffer.line_starts.length;
u64 line = 0; start_line = Min(start_line, total_lines);
u64 end_line = Min(start_line + length, total_lines);
if(buffer.length == 0) if(buffer.length == 0)
{ {
linebufs.first.text = AllocArray!(u8)(&linebufs.arena, 1); linebufs.first.text = AllocArray!(u8)(&linebufs.arena, 1);
linebufs.first.text[0] = 0; linebufs.first.text[0] = 0;
} }
else if(start_line == end_line) with(buffer)
{
u64 start_of_line = line_starts[start_line];
u64 len = LineLength(buffer, start_line);
linebufs.first.text = AllocArray!(u8)(&linebufs.arena, len);
linebufs.first.text = data[start_of_line .. start_of_line+len];
linebufs.count += 1;
}
else with(linebufs) else with(linebufs)
{ {
LineBuffer* current = first; LineBuffer* current = first;
for(u64 i = 0; i < buffer.length; i += 1) for(u64 i = start_line; i < end_line; i += 1)
{ {
if(count == length) u64 start = buffer.line_starts[i];
u64 len = LineLength(buffer, i);
if(len > 0)
{ {
break; current.text = AllocArray!(u8)(&arena, len);
current.text[0 .. len] = buffer.data[start .. start+len];
count += 1;
current.next = Alloc!(LineBuffer)(&arena);
current = current.next;
} }
bool new_line = (buffer.data.ptr[i] == '\n'); if(i == buffer.lf_count && buffer.data[buffer.length-1] == '\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 = 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;
}
}
if(buffer.length > 0 && buffer.data[buffer.length-1] == '\n')
{
current.text = AllocArray!(u8)(&linebufs.arena, 1);
current.text[0] = 0; current.text[0] = 0;
count += 1; count += 1;
current.next = Alloc!(LineBuffer)(&arena);
}
} }
} }
} }
void void
Move(FlatBuffer* buffer, Input key, Modifier md) MoveUp(FlatBuffer* fb)
{
MoveUp(fb, LinePos(fb, fb.buf_pos));
}
void
MoveUp(FlatBuffer* fb, u64 col)
{
u64 line = CurrentLine(fb);
if(line > 0)
{
u64 end = fb.line_starts[line]-1;
line -= 1;
fb.buf_pos = Min(fb.line_starts[line]+col, end);
}
}
void
MoveDown(FlatBuffer* fb)
{
MoveDown(fb, LinePos(fb, fb.buf_pos));
}
void
MoveDown(FlatBuffer* fb, u64 col)
{
u64 line = CurrentLine(fb);
if(line+1 < fb.line_starts.length)
{
line += 1;
fb.buf_pos = fb.line_starts[line];
u64 end = fb.line_starts.length > line+1 ? fb.line_starts[line+1]-1 : fb.length;
fb.buf_pos = Min(fb.buf_pos+col, end);
}
}
void
Move(FlatBuffer* fb, Input key, Modifier md)
{ {
Logf("key: %s", key);
if(md & (MD.LeftShift | MD.RightShift)) if(md & (MD.LeftShift | MD.RightShift))
{ {
switch(key) switch(key)
@ -278,336 +359,62 @@ Move(FlatBuffer* buffer, Input key, Modifier md)
{ {
case Input.Down: case Input.Down:
{ {
if(buffer.pos.y < buffer.lf_count) MoveDown(fb);
{
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;
}
if(buffer.buf_pos == buffer.length)
{
break;
}
Walk!(WalkDir.Forward)(buffer);
}
}
} break; } break;
case Input.Up: case Input.Up:
{ {
if(buffer.pos.y > 0) MoveUp(fb);
{
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')
{
new_l += 1;
if(new_l == 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;
}
if(buffer.buf_pos == buffer.length)
{
break;
}
Walk!(WalkDir.Forward)(buffer);
}
}
} break; } break;
case Input.Left: case Input.Left:
{ {
if(buffer.pos.x != 0) if(fb.buf_pos > 0 && fb.data[fb.buf_pos-1] != '\n')
{ {
Walk!(WalkDir.Backward)(buffer); fb.buf_pos -= 1;
} }
} break; } break;
case Input.Right: case Input.Right:
{ {
if(buffer.buf_pos < buffer.length && buffer.data[buffer.buf_pos] != '\n') if(fb.buf_pos < fb.length && fb.data[fb.buf_pos] != '\n')
{ {
Walk!(WalkDir.Forward)(buffer); fb.buf_pos += 1;
} }
} break; } break;
default: break; default: break;
} }
} }
AdjustOffset(buffer); AdjustOffset(fb);
Logf("buf_pos %s pos %s", buffer.buf_pos, buffer.pos.v); U64Vec2 p = VecPos(fb);
Logf("buf_pos %s pos %s", fb.buf_pos, p.v);
//VerifyPosition(buffer, key, md);
} }
void void
AdjustOffset(FlatBuffer* buffer) AdjustOffset(FlatBuffer* fb)
{ {
with(buffer) with(fb)
{ {
i64 screen_pos = pos.y - offset; i64 screen_pos = CurrentLine(fb) - offset;
i64 start = 1; i64 start = 1;
i64 end = rows-1; i64 end = rows-1;
if(offset > 0 && screen_pos < start) if(offset > 0 && screen_pos < start)
{ {
Logf("rows %s offset %s adjusting by %s - %s", rows, offset, screen_pos, start);
offset += screen_pos - start; offset += screen_pos - start;
} }
else if(screen_pos > end) else if(screen_pos > end)
{ {
Logf("rows %s offset %s adjusting by %s - %s", rows, offset, screen_pos, end);
offset += screen_pos - end; offset += screen_pos - end;
} }
} }
} }
u64
VecToPos(FlatBuffer* buffer, I64Vec2 vec)
{
u64 pos, line, col;
bool closer_to_start = abs(buffer.pos.y - vec.x) > vec.x;
if(closer_to_start)
{
for(; pos < buffer.length; pos += 1)
{
if(vec.y == line)
{
break;
}
if(buffer.data.ptr[pos] == '\n')
{
line += 1;
}
}
for(; pos < buffer.length; pos += 1)
{
if(buffer.data.ptr[pos] == '\n')
{
break;
}
if(col == vec.x)
{
break;
}
col += 1;
}
}
else
{
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;
}
void void
Backspace(FlatBuffer* buffer, u64 length) Backspace(FlatBuffer* buffer)
{ {
if(buffer.buf_pos-length > 0) if(buffer.buf_pos-1 >= 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')
{
}
}
}
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) void
Walk(alias dir)(FlatBuffer* buffer)
{
static if(dir == WalkDir.Backward)
{ {
buffer.buf_pos -= 1; buffer.buf_pos -= 1;
if(buffer.data[buffer.buf_pos] == '\n') Delete(buffer, 1, buffer.buf_pos);
{
buffer.pos.y -= 1;
buffer.pos.x = PosInLine(buffer, buffer.buf_pos);
} }
else
{
buffer.pos.x -= 1;
}
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)
{
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;
}
}
}
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;
}
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;
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(skip_lf)
{
skip_lf = false;
}
else
{
break;
}
}
}
Logf("line_pos %s", line_pos);
return line_pos;
} }
void void
@ -616,6 +423,14 @@ Delete(FlatBuffer* buffer, u64 length, u64 pos)
u64 end = pos+length; u64 end = pos+length;
assert(end <= buffer.length, "Delete failure: pos+length is not in range"); assert(end <= buffer.length, "Delete failure: pos+length is not in range");
for(u64 i = pos; i < buffer.length && i < pos+length; i += 1)
{
if(buffer.data[i] == '\n')
{
buffer.lf_count -= 1;
}
}
u8[] temp; u8[] temp;
if(end != buffer.length) if(end != buffer.length)
{ {
@ -625,6 +440,8 @@ Delete(FlatBuffer* buffer, u64 length, u64 pos)
} }
buffer.length -= length; buffer.length -= length;
Fix(buffer);
} }
void void

View File

@ -25,6 +25,7 @@ struct EditorCtx
EditState state; EditState state;
u8[128] input_buf; u8[128] input_buf;
u32 icount; u32 icount;
Timer timer;
} }
struct Editor struct Editor
@ -35,7 +36,6 @@ 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;
@ -62,6 +62,8 @@ Cycle(EditorCtx* ctx, Inputs* inputs)
{ {
ResetScratch(MB(4)); ResetScratch(MB(4));
g_delta = DeltaTime(&ctx.timer);
assert(Nil(ctx.base_panel.next)); assert(Nil(ctx.base_panel.next));
HandleInputs(ctx, inputs); HandleInputs(ctx, inputs);
@ -88,6 +90,7 @@ InitEditorCtx(PlatformWindow* window)
ctx.base_panel = CreatePanel(&ctx); ctx.base_panel = CreatePanel(&ctx);
ctx.base_panel.ed = CreateEditor(&ctx); ctx.base_panel.ed = CreateEditor(&ctx);
ctx.timer = CreateTimer();
SetFocusedPanel(ctx.base_panel); SetFocusedPanel(ctx.base_panel);
return ctx; return ctx;
@ -330,7 +333,11 @@ HandleInputMode(EditorCtx* ctx, InputEvent ev)
mixin(CharCases()); mixin(CharCases());
case Input.Backspace: case Input.Backspace:
{ {
UIPanel* p = GetFocusedPanel();
if(!Nil(p))
{
Backspace(&p.ed.buf);
}
} break; } break;
case Input.Escape: case Input.Escape:
{ {

View File

@ -106,6 +106,7 @@ struct UICtx
Vec4 text_color; Vec4 text_color;
Axis2D layout_axis; Axis2D layout_axis;
Vec2 adjustment; Vec2 adjustment;
Vec2 offset;
u32 tab_width; u32 tab_width;
f32 text_size; f32 text_size;
f32 border_thickness; f32 border_thickness;
@ -161,6 +162,7 @@ struct UIItem
f32 corner_radius; f32 corner_radius;
f32 edge_softness; f32 edge_softness;
Vec2 last_click_pos; Vec2 last_click_pos;
Vec2 offset;
} }
struct UISize struct UISize
@ -483,6 +485,12 @@ SetAdjustment(Vec2 adj)
g_ui_ctx.adjustment = adj; g_ui_ctx.adjustment = adj;
} }
void
SetOffset(Vec2 offset)
{
g_ui_ctx.offset = offset;
}
void void
SetHighlightedChar(i64 pos) SetHighlightedChar(i64 pos)
{ {
@ -718,7 +726,7 @@ CalcPositions(alias axis)(UIItem* item)
for(UIItem* i = item; !Nil(i);) for(UIItem* i = item; !Nil(i);)
{ {
f32 end_pos = pos + i.size.v[axis]; f32 end_pos = pos + i.size.v[axis];
i.rect.vec0.v[axis] = pos; i.rect.vec0.v[axis] = pos + i.offset.v[axis];
i.rect.vec1.v[axis] = end_pos; i.rect.vec1.v[axis] = end_pos;
assert(!isNaN(i.rect.vec0.v[axis])); assert(!isNaN(i.rect.vec0.v[axis]));
@ -833,6 +841,7 @@ BuildItem(UIItem* item, UISize size_x, UISize size_y, UIFlags properties)
item.edge_softness = ctx.edge_softness; item.edge_softness = ctx.edge_softness;
item.last_frame = ctx.frame; item.last_frame = ctx.frame;
item.highlighted_char = ctx.highlighted_char; item.highlighted_char = ctx.highlighted_char;
item.offset = ctx.offset;
item.parent = ctx.top_parent == g_UI_NIL_NODE ? g_UI_NIL : ctx.top_parent.item; item.parent = ctx.top_parent == g_UI_NIL_NODE ? g_UI_NIL : ctx.top_parent.item;
if(!Nil(item.parent)) if(!Nil(item.parent))

View File

@ -6,6 +6,8 @@ import ui;
import editor; import editor;
import std.format : sformat; import std.format : sformat;
import std.math.rounding : ceil, floor; import std.math.rounding : ceil, floor;
import std.math.exponential : pow;
import core.stdc.math : fabsf;
const UIPanel g_ui_nil_panel; const UIPanel g_ui_nil_panel;
UIPanel* g_UI_NIL_PANEL; UIPanel* g_UI_NIL_PANEL;
@ -22,6 +24,7 @@ struct WidgetCtx
struct UIPanel struct UIPanel
{ {
AnimState anim;
u8[] id; u8[] id;
Editor* ed; Editor* ed;
@ -36,6 +39,18 @@ struct UIPanel
Axis2D axis; Axis2D axis;
f32 pct; f32 pct;
Vec4 color; Vec4 color;
i64 prev_offset;
i64 start_row;
i64 end_row;
}
struct AnimState
{
f32 start_value;
f32 end_value;
f32 start_time;
f32 remaining_time;
} }
struct TextPart struct TextPart
@ -171,6 +186,45 @@ TextLine(u8[] text)
UIItem* item = Get(text); UIItem* item = Get(text);
} }
void
SetPanelScroll(UIPanel* panel, i64 rows, f32 text_size)
{
i64 low = panel.prev_offset < panel.ed.buf.offset ? panel.prev_offset : panel.ed.buf.offset;
i64 high = panel.prev_offset < panel.ed.buf.offset ? panel.ed.buf.offset + rows : panel.prev_offset + rows;
panel.start_row = low;
panel.end_row = high;
f32 start = panel.anim.start_value;
f32 end = panel.anim.end_value;
if(start == 0.0 && end == 0.0)
{
f32 offset = (panel.prev_offset-panel.ed.buf.offset) * text_size;
if(offset > 0.0)
{
end = -offset;
}
else
{
start = offset;
}
}
panel.anim.start_value = start;
panel.anim.end_value = end;
panel.anim.start_time = 0.1;
panel.anim.remaining_time = 0.1;
panel.prev_offset = panel.ed.buf.offset;
}
f32
EaseOutExp(f32 v)
{
return fabsf(1.0 - v) <= 0.00009 ? 1.0 : 1.0 - pow(2, -10 * v);
}
void void
EditorView(UIPanel* panel) EditorView(UIPanel* panel)
{ {
@ -186,20 +240,50 @@ EditorView(UIPanel* panel)
i64 rows = cast(i64)ceil(item.size.y/text_size); i64 rows = cast(i64)ceil(item.size.y/text_size);
if(ed.buf.rows != rows) if(ed.buf.rows != rows)
{ {
Logf("editor height %s text_size %s rows %s", item.size.y, text_size, rows);
ed.buf.rows = rows; ed.buf.rows = rows;
} }
if(panel.prev_offset != ed.buf.offset)
{
SetPanelScroll(panel, rows, text_size);
}
i64 line_offset = ed.buf.offset;
if(panel.anim.remaining_time > 0.0) with(panel.anim)
{
remaining_time -= g_delta;
if(remaining_time < 0.0)
{
remaining_time = 0.0;
}
f32 offset = Remap(remaining_time, 0.0, start_time, 0.0, 1.0);
offset = Remap(EaseOutExp(offset), 0.0, 1.0, start_value, end_value);
SetOffset(Vec2(0.0, offset));
if(remaining_time == 0.0)
{
start_value = end_value = start_time = 0.0;
}
line_offset = panel.start_row;
GetLines(&ed.buf, &ed.linebufs, panel.start_row, panel.end_row-panel.start_row);
}
else
{
GetLines(&ed.buf, &ed.linebufs, rows); GetLines(&ed.buf, &ed.linebufs, rows);
}
U64Vec2 pos = VecPos(&ed.buf);
u64 i = 0; u64 i = 0;
for(LineBuffer* buf = ed.linebufs.first; buf != null; buf = buf.next, i += 1) for(LineBuffer* buf = ed.linebufs.first; buf != null; buf = buf.next, i += 1)
{ {
if(buf.text.length > 0) if(buf.text.length > 0)
{ {
i64 line_no = i+ed.buf.offset; i64 line_no = i+line_offset;
if(ed.buf.pos.y == line_no && focused) if(pos.y == line_no && focused)
{ {
SetHighlightedChar(ed.buf.pos.x); SetHighlightedChar(pos.x);
} }
TextPart* tp = WrappedTextLine(buf.text, panel.id, text_size, line_no); TextPart* tp = WrappedTextLine(buf.text, panel.id, text_size, line_no);
@ -221,6 +305,8 @@ EditorView(UIPanel* panel)
SetFocusedPanel(panel); SetFocusedPanel(panel);
} }
SetOffset(Vec2(0.0));
EndPanel(); EndPanel();
} }