130 lines
3.7 KiB
Plaintext
130 lines
3.7 KiB
Plaintext
module ugui;
|
|
|
|
import grapheme;
|
|
import std::ascii;
|
|
|
|
struct TextEdit {
|
|
char[] buffer;
|
|
usz chars;
|
|
usz cursor;
|
|
}
|
|
|
|
fn String TextEdit.to_string(&te) => (String)te.buffer[:te.chars];
|
|
fn String TextEdit.until_cursor(&te) => (String)te.buffer[:te.cursor];
|
|
fn String TextEdit.from_cursor(&te) => (String)te.buffer[te.cursor..];
|
|
|
|
// implement text editing operations on the buffer
|
|
// returns true if the buffer is full
|
|
fn bool Ctx.text_edit(&ctx, TextEdit* te)
|
|
{
|
|
String in = ctx.get_keys();
|
|
ModKeys mod = ctx.get_mod();
|
|
usz free = te.buffer.len - te.chars;
|
|
usz after = te.chars - te.cursor;
|
|
|
|
// append text input to the buffer
|
|
if (in.len <= free) {
|
|
// make space
|
|
te.buffer[te.cursor+in.len : after] = te.buffer[te.cursor : after];
|
|
// insert characters
|
|
te.buffer[te.cursor : in.len] = in[..];
|
|
// increment characters and cursor
|
|
te.chars += in.len;
|
|
te.cursor += in.len;
|
|
free -= in.len;
|
|
} else {
|
|
return true;
|
|
}
|
|
|
|
// handle modkeys
|
|
if (te.chars) {
|
|
// handle backspace and delete
|
|
if (mod.bkspc) {
|
|
if (te.cursor > 0) {
|
|
// TODO: only delete until punctuation
|
|
usz how_many = mod & KMOD_CTRL ? te.until_cursor().prev_word_off() : te.until_cursor().prev_char_off();
|
|
te.buffer[te.cursor-how_many : after] = te.buffer[te.cursor : after];
|
|
te.cursor -= how_many;
|
|
te.chars -= how_many;
|
|
free += how_many;
|
|
}
|
|
}
|
|
if (mod.del) {
|
|
if (after > 0 && te.cursor < te.chars) {
|
|
usz how_many = mod & KMOD_CTRL ? te.from_cursor().next_word_off() : te.from_cursor().next_char_off();
|
|
te.buffer[te.cursor : after] = te.buffer[te.cursor+how_many : after];
|
|
te.chars -= how_many;
|
|
after -= how_many;
|
|
free += how_many;
|
|
}
|
|
}
|
|
|
|
// handle arrow keys
|
|
if (mod.left) {
|
|
if (te.cursor > 0) {
|
|
usz how_many = mod & KMOD_CTRL ? te.until_cursor().prev_word_off() : te.until_cursor().prev_char_off();
|
|
te.cursor -= how_many;
|
|
after += how_many;
|
|
}
|
|
}
|
|
if (mod.right) {
|
|
if (after > 0) {
|
|
usz how_many = mod & KMOD_CTRL ? te.from_cursor().next_word_off() : te.from_cursor().next_char_off();
|
|
te.cursor += how_many;
|
|
after -= how_many;
|
|
}
|
|
}
|
|
// FIXME: this still doesn't work
|
|
if (mod.up) {
|
|
// back up to previous line
|
|
if (te.cursor > 0) {
|
|
usz curr_line_start = te.until_cursor().rindex_of_char('\n') ?? 0;
|
|
usz prev_line_start = curr_line_start ? te.until_cursor()[..curr_line_start-1].rindex_of_char('\n') ?? 0 : 0;
|
|
usz curr_line_off = te.cursor - curr_line_start;
|
|
usz prev_line_len = curr_line_start - prev_line_start;
|
|
te.cursor = prev_line_start + min(curr_line_off-1, prev_line_len);
|
|
after = te.chars - te.cursor;
|
|
}
|
|
}
|
|
if (mod.down) {
|
|
// down to the next line
|
|
if (after > 0) {
|
|
usz curr_line_start = te.until_cursor().rindex_of_char('\n') ?? 0;
|
|
usz curr_line_off = te.cursor - curr_line_start;
|
|
usz next_line_start = te.from_cursor().index_of_char('\n') + te.cursor + 1 ?? te.chars;
|
|
usz next_line_end = ((String)te.buffer[next_line_start..]).index_of_char('\n') + next_line_start ?? te.chars;
|
|
usz next_line_len = next_line_end - next_line_start;
|
|
te.cursor = next_line_start + min(curr_line_off, next_line_len);
|
|
after = te.chars - te.cursor;
|
|
}
|
|
}
|
|
|
|
// TODO: mod.home
|
|
// TODO: mod.end
|
|
// TODO: selection with shift+arrows
|
|
}
|
|
|
|
return free == 0;
|
|
}
|
|
|
|
macro isz char[].next_char_off(b) => grapheme::next_character_break_utf8(b.ptr, b.len);
|
|
macro isz char[].next_word_off(b) => grapheme::next_word_break_utf8(b.ptr, b.len);
|
|
|
|
fn isz char[].prev_char_off(b)
|
|
{
|
|
foreach_r (off, c: b) {
|
|
if (c & 0xC0 == 0x80) continue;
|
|
return b.len - off;
|
|
}
|
|
return b.len;
|
|
}
|
|
|
|
fn isz char[].prev_word_off(b)
|
|
{
|
|
for (isz off = b.len-1; off > 0;) {
|
|
isz c_off = b[..off].prev_char_off();
|
|
off -= c_off;
|
|
if (ascii::is_punct(b[off]) || ascii::is_space(b[off])) return b.len - off - 1;
|
|
}
|
|
return b.len;
|
|
} |