diff options
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/TUI/module.jai | 117 | ||||
| -rw-r--r-- | modules/UTF8.jai | 48 |
2 files changed, 96 insertions, 69 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; +} |
