aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/TUI/module.jai117
-rw-r--r--modules/UTF8.jai48
-rw-r--r--ttt.jai2
3 files changed, 97 insertions, 70 deletions
diff --git a/modules/TUI/module.jai b/modules/TUI/module.jai
index e0bd9b4..2d23bff 100644
--- a/modules/TUI/module.jai
+++ b/modules/TUI/module.jai
@@ -1,4 +1,4 @@
-#module_parameters(COLOR_BIT_DEPTH := 24); // TODO - Do #assert module parameters to make sure they are valid.
+#module_parameters(COLOR_BIT_DEPTH := 24); // TODO - Do #assert module parameters to make sure they are valid... and maybe rename to: COLOR_MODE with a enum.
#if OS == {
case .LINUX;
@@ -111,7 +111,7 @@ Commands :: struct {
set_colors :: inline (foreground: Palette, background: Palette) {
print(
- #run sprint("% %", Commands.SetGraphicsRendition, Commands.SetGraphicsRendition),
+ #run sprint("%0%0", Commands.SetGraphicsRendition, Commands.SetGraphicsRendition),
cast(u8)foreground + 30, cast(u8)background + 40);
}
}
@@ -407,13 +407,13 @@ read_input :: (count_limit: int = -1, terminators: .. u8) -> string {
}
}
-read_input_line :: (count_limit: int, is_visible: bool = true, placeholder: string = "") -> string, Key {
+read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key {
/*
- Use the get_key to read user input and show it on screen.
- Should allow to move the cursor left and right and to delete/backspace.
- Enter should end the input, returning the input string and the Enter key.
- Escape should discard the input returning an empty string and a Escape key.
- Resize should discard the input returning an empty string and a Resize key.
+ Uses the get_key to read user input and show it on screen.
+ Allows to move the cursor left and right and to delete/backspace.
+ Enter ends the input, returning the input string and the Enter key.
+ Escape discards the input returning an empty string and a Escape key.
+ Resize discards the input returning an empty string and a Resize key.
*/
assert(count_limit >= 0, "Invalid value on count_limit parameter.");
@@ -421,32 +421,34 @@ read_input_line :: (count_limit: int, is_visible: bool = true, placeholder: stri
str := alloc_string(count_limit);
str.count = 0;
idx := 0;
-
- // placeholder: string = "",
- // {
- // copy_size := ifx placeholder.count > str.count then str.count else placeholder.count;
- // memcpy(str.data, placeholder.data, copy_size);
- // idx = copy_size;
- // }
-
+
// TODO Some of these may be nice to have:
// > https://unix.stackexchange.com/questions/255707/what-are-the-keyboard-shortcuts-for-the-command-line
x, y := get_cursor_position();
write_strings(Commands.StartBlinking, Commands.BlinkingBarShape);
- // Clear line for input.
- for 1..count_limit {
- print_character(#char " ");
- }
- set_cursor_position(x, y);
-
key := Keys.None;
while true {
auto_release_temp();
-
- key = get_key();
+ chars_count := count_characters(str);
+
+ // Preview input.
+ if is_visible {
+ set_cursor_position(x, y);
+ write_string(str);
+ for chars_count..count_limit-1 print_character(#char " ");
+ }
+ else {
+ set_cursor_position(x, y);
+ for 0..chars_count print_character(#char "*");
+ for chars_count..count_limit-1 print_character(#char " ");
+ }
+ set_cursor_position(x+idx, y);
+
+ // Process input key.
+ key = get_key();
if key == {
case Keys.Resize; #through;
case Keys.Escape; #through;
@@ -455,63 +457,47 @@ read_input_line :: (count_limit: int, is_visible: bool = true, placeholder: stri
case Keys.Left;
if idx > 0 then idx -= 1;
-
+
case Keys.Right;
- if idx < str.count then idx += 1;
-
+ if idx < chars_count then idx += 1;
+
case Keys.Home;
idx = 0;
case Keys.End;
- idx = str.count;
+ idx = chars_count;
case Keys.Delete;
- if idx == str.count continue;
- for idx..str.count-2 {
- str.data[it] = str.data[it+1];
- }
- str.data[str.count-1] = 0;
- str.count -= 1;
-
+ if idx == chars_count continue;
+ delete_character(*str, idx);
+
case Keys.Backspace;
if idx == 0 continue;
idx -= 1;
- for idx..str.count-2 {
- str.data[it] = str.data[it+1];
- }
- str.data[str.count-1] = 0;
- str.count -= 1;
-
+ delete_character(*str, idx);
+
case;
-
- // TODO FIXME Does not support UTF8 input...
-
- if idx >= count_limit continue;
if is_escape_code(key) continue;
+
+ buff_idx := map_character_to_buffer_idx(str, idx);
+ key_str := to_string(key);
- for < count_limit..idx+1 {
- str.data[it] = str.data[it-1];
+ // Make sure we have space to append the new character at the end (in case we're trying to do it).
+ if buff_idx > count_limit - key_str.count then continue;
+
+ // Move text to allow inserting new character.
+ for < count_limit..buff_idx+key_str.count {
+ str.data[it] = str.data[it-key_str.count];
}
- key_str := to_string(key);
- str.data[idx] = key_str.data[0];
+ memcpy(*str.data[buff_idx], key_str.data, key_str.count);
- if str.count < count_limit then str.count += 1;
+ if str.count < count_limit then str.count += key_str.count;
idx += 1;
+
+ // Truncate string to avoid incomplete utf8 codes on the string tail.
+ str.count = truncate_string(str, count_limit);
}
-
- if is_visible {
- set_cursor_position(x, y);
- write_string(str);
- for str.count..count_limit-1 print_character(#char " ");
- }
- else {
- set_cursor_position(x, y);
- for 0..str.count-1 print_character(#char "*");
- for str.count..count_limit-1 print_character(#char " ");
- }
-
- set_cursor_position(x+idx, y);
}
write_strings(Commands.StopBlinking, Commands.DefaultShape);
@@ -559,11 +545,6 @@ flush_input :: () {
input_string.count = 0;
}
-// TODO move style related procedures here!
-// set_style :: () {
- // print("", Commands.) --
-// }
-
draw_box :: (x: int, y: int, width: int, height: int) {
assert_is_active();
diff --git a/modules/UTF8.jai b/modules/UTF8.jai
index fac1326..eba4585 100644
--- a/modules/UTF8.jai
+++ b/modules/UTF8.jai
@@ -1,4 +1,5 @@
// BBBB BBBB & 1100 0000 == 10XX XXXX -> is continuation byte
+// TODO Maybe rename to: is_continuation_byte
is_utf8_continuation_byte :: inline (byte: u8) -> bool {
return (byte & 0xC0) == 0x80;
}
@@ -6,6 +7,7 @@ is_utf8_continuation_byte :: inline (byte: u8) -> bool {
// BBBB BBBB & 1110 0000 == 110X XXXX -> 1 initial + 1 continuation byte
// BBBB BBBB & 1111 0000 == 1110 XXXX -> 1 initial + 2 continuation byte
// BBBB BBBB & 1111 1000 == 1111 0XXX -> 1 initial + 3 continuation byte
+// TODO Maybe rename to: count_character_bytes
count_utf8_bytes :: inline (byte: u8) -> int {
if (byte & 0xE0) == 0xC0 return 1+1;
if (byte & 0xF0) == 0xE0 return 1+2;
@@ -16,6 +18,7 @@ count_utf8_bytes :: inline (byte: u8) -> int {
// Truncates the string to the length provided or shorter, in case of UTF8 strings that require so.
// Truncation is done by zeroing the tail of the string in place.
// Returns length of truncated string.
+// TODO Maybe rename to: truncate
truncate_string :: (str: string, length: int) -> length: int {
if str.data == null then return -1;
@@ -26,7 +29,6 @@ truncate_string :: (str: string, length: int) -> length: int {
// Find index of first continuation byte.
idx := length;
- // while (idx > 0 && ((data[idx - 1] & 0xC0) == 0x80)) { TODO REMOVE AFTER TESTING
while (idx > 0 && is_utf8_continuation_byte(data[idx - 1])) {
idx -= 1;
}
@@ -48,10 +50,12 @@ truncate_string :: (str: string, length: int) -> length: int {
}
memset(data + length, 0, count - length);
+ // str.count = length; TODO We should be doing this...
return length;
}
// Returns true when the string is empty or consists of space characters.
+// TODO Maybe rename to: is_empty
is_empty_string :: (str: string) -> bool {
for 0..str.count-1 {
if str[it] == {
@@ -69,3 +73,45 @@ is_empty_string :: (str: string) -> bool {
}
return true;
}
+
+// Counts number of characters in string.
+count_characters :: (str: string) -> int {
+ characters := 0;
+ idx := 0;
+ while idx < str.count {
+ idx += count_utf8_bytes(str[idx]);
+ characters += 1;
+ }
+ return characters;
+}
+
+// Delete character.
+delete_character :: (str: *string, character_idx: int) {
+ buffer_idx := map_character_to_buffer_idx(str.*, character_idx);
+ bytes_to_delete := count_utf8_bytes(str.data[buffer_idx]);
+
+ for buffer_idx..str.count-1-bytes_to_delete {
+ str.data[it] = str.data[it+bytes_to_delete];
+ }
+ for str.count-bytes_to_delete..str.count-1 {
+ str.data[it] = 0;
+ }
+
+ str.count -= bytes_to_delete;
+}
+
+// Get character index.
+// TODO Maybe rename to: map_character_to_byte_idx or get_character_byte_idx
+map_character_to_buffer_idx :: (str: string, character_idx: int) -> buffer_idx: int, success: bool {
+ if character_idx < 0 then return -1, false;
+ if character_idx > str.count then return -2, false;
+ if character_idx == 0 then return 0, true;
+
+ buff_idx := 0;
+ char_idx := 0;
+ while buff_idx < str.count && char_idx != character_idx {
+ buff_idx += count_utf8_bytes(str[buff_idx]);
+ char_idx += 1;
+ }
+ return buff_idx, char_idx == character_idx;
+}
diff --git a/ttt.jai b/ttt.jai
index 7fdd9af..ffe9494 100644
--- a/ttt.jai
+++ b/ttt.jai
@@ -1674,7 +1674,7 @@ main :: () {
TUI.flush_input();
TUI.set_next_key(TUI.Keys.F2);
- // Rename task.
+ // Rename.
case TUI.Keys.F2;
if (selected_task == null) continue;