add animated text scrolling
This commit is contained in:
parent
c9617b701b
commit
7769a861e3
@ -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
|
||||||
|
|||||||
@ -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:
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user