From: Brendan Hansen Date: Sun, 13 Jun 2021 21:34:51 +0000 (-0500) Subject: added String_Buffer; refactored some textbox code X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=fdd8fcbc3fdb88208cf32a1ade0d42cf57b289bd;p=onyx.git added String_Buffer; refactored some textbox code --- diff --git a/bin/onyx b/bin/onyx index e2a16a3c..4ca266c4 100755 Binary files a/bin/onyx and b/bin/onyx differ diff --git a/core/std.onyx b/core/std.onyx index 776d715d..3c0b5527 100644 --- a/core/std.onyx +++ b/core/std.onyx @@ -18,6 +18,7 @@ package core #load "./string" #load "./string/builder" #load "./string/reader" +#load "./string/buffer" #load "./intrinsics/onyx" #load "./intrinsics/wasm" diff --git a/core/string/buffer.onyx b/core/string/buffer.onyx new file mode 100644 index 00000000..9e274512 --- /dev/null +++ b/core/string/buffer.onyx @@ -0,0 +1,66 @@ +// String_Buffer was originally made to support the UI library's textbox buffer, +// where single character editing was the only operation that was needed. Therefore, +// this is very barebones and does not contain a lot of functionality at the moment. +// It basically just represents a string stored in a non-resizable buffer, but the +// string can be resized within the buffer. + +package core.string + +#private_file math :: package core.math + +String_Buffer :: struct { + data : ^u8; + count : u32; + capacity : u32; +} + +buffer_make :: (buffer_memory: [] u8, initial_str := null_str) -> String_Buffer { + buffer : String_Buffer; + buffer.data = buffer_memory.data; + buffer.capacity = buffer_memory.count; + buffer.count = 0; + + if initial_str.count > 0 { + initial_length := math.min(initial_str.count, buffer.capacity); + buffer.count += initial_length; + + for i: initial_length { + buffer.data[i] = initial_str.data[i]; + } + } + + return buffer; +} + +buffer_insert :: (use buffer: ^String_Buffer, position: i32, ch: u8) -> bool { + if position >= capacity do return false; + if count >= capacity do return false; + + while i := cast(i32) count; i > position { + defer i -= 1; + + data[i] = data[i - 1]; + } + + data[position] = ch; + count += 1; + return true; +} + +buffer_delete :: (use buffer: ^String_Buffer, position: i32) -> bool { + if position > capacity do return false; + if position > count do return false; + if count == 0 do return false; + if position == 0 do return false; + + while i := position - 1; i < cast(i32) count - 1 { + defer i += 1; + + data[i] = data[i + 1]; + } + + count -= 1; + return true; +} + +buffer_to_str :: (use buffer: ^String_Buffer) -> str do return .{data, count}; \ No newline at end of file diff --git a/core/string/reader.onyx b/core/string/reader.onyx index 4c323792..ed690798 100644 --- a/core/string/reader.onyx +++ b/core/string/reader.onyx @@ -1,6 +1,6 @@ package core.string.reader -StringReader :: struct { +String_Reader :: struct { // Think of this as `use s : str`; data : ^u8; count : u32; @@ -9,26 +9,26 @@ StringReader :: struct { original_str : str; } -make :: proc (s: str) -> StringReader { - reader : StringReader; +make :: proc (s: str) -> String_Reader { + reader : String_Reader; init(^reader, s); return reader; } -init :: proc (use reader: ^StringReader, orig_str: str) { +init :: proc (use reader: ^String_Reader, orig_str: str) { original_str = orig_str; data = original_str.data; count = original_str.count; } -reset :: proc (use reader: ^StringReader) { +reset :: proc (use reader: ^String_Reader) { data = original_str.data; count = original_str.count; } -empty :: proc (use reader: ^StringReader) -> bool do return count == 0; +empty :: proc (use reader: ^String_Reader) -> bool do return count == 0; -read_u32 :: proc (use reader: ^StringReader) -> u32 { +read_u32 :: proc (use reader: ^String_Reader) -> u32 { n: u32 = 0; skip_whitespace(reader); @@ -44,7 +44,7 @@ read_u32 :: proc (use reader: ^StringReader) -> u32 { return n; } -read_u64 :: proc (use reader: ^StringReader) -> u64 { +read_u64 :: proc (use reader: ^String_Reader) -> u64 { n: u64 = 0; skip_whitespace(reader); @@ -60,7 +60,7 @@ read_u64 :: proc (use reader: ^StringReader) -> u64 { return n; } -read_byte :: proc (use reader: ^StringReader) -> u8 { +read_byte :: proc (use reader: ^String_Reader) -> u8 { if count == 0 do return #char "\0"; defer { @@ -71,7 +71,7 @@ read_byte :: proc (use reader: ^StringReader) -> u8 { return data[0]; } -read_bytes :: proc (use reader: ^StringReader, byte_count := 1) -> str { +read_bytes :: proc (use reader: ^String_Reader, byte_count := 1) -> str { bc := byte_count; if count < bc do bc = count; @@ -83,7 +83,7 @@ read_bytes :: proc (use reader: ^StringReader, byte_count := 1) -> str { return str.{ data, bc }; } -read_line :: proc (use reader: ^StringReader) -> str { +read_line :: proc (use reader: ^String_Reader) -> str { out : str; out.data = data; out.count = 0; @@ -104,7 +104,7 @@ read_line :: proc (use reader: ^StringReader) -> str { return out; } -read_until :: proc (use reader: ^StringReader, skip: u32, uptos: ..u8) -> str { +read_until :: proc (use reader: ^String_Reader, skip: u32, uptos: ..u8) -> str { if count == 0 do return str.{ null, 0 }; out : str; @@ -131,7 +131,7 @@ read_until :: proc (use reader: ^StringReader, skip: u32, uptos: ..u8) -> str { } // Reads a continuous string of alphabetic chars along with underscores '_' -read_word :: proc (use reader: ^StringReader) -> str { +read_word :: proc (use reader: ^String_Reader) -> str { if count == 0 do return str.{ null, 0 }; out := str.{ data, 0 }; @@ -150,7 +150,7 @@ read_word :: proc (use reader: ^StringReader) -> str { return out; } -advance_line :: proc (use reader: ^StringReader) { +advance_line :: proc (use reader: ^String_Reader) { if count == 0 do return; adv := 0; @@ -161,7 +161,7 @@ advance_line :: proc (use reader: ^StringReader) { } -skip_whitespace :: proc (use reader: ^StringReader) { +skip_whitespace :: proc (use reader: ^String_Reader) { while count > 0 do switch data[0] { case #char " ", #char "\t", #char "\n", #char "\r" { data += 1; @@ -172,7 +172,7 @@ skip_whitespace :: proc (use reader: ^StringReader) { } } -skip_bytes :: proc (use reader: ^StringReader, byte_count := 1) { +skip_bytes :: proc (use reader: ^String_Reader, byte_count := 1) { bc := byte_count; if count < bc do bc = count; @@ -182,7 +182,7 @@ skip_bytes :: proc (use reader: ^StringReader, byte_count := 1) { -starts_with :: proc (use reader: ^StringReader, s: str) -> bool { +starts_with :: proc (use reader: ^String_Reader, s: str) -> bool { if count < s.count do return false; while i := 0; i < s.count { if data[i] != s[i] do return false; diff --git a/modules/ui/components/textbox.onyx b/modules/ui/components/textbox.onyx index d6e4d75c..c4a60f23 100644 --- a/modules/ui/components/textbox.onyx +++ b/modules/ui/components/textbox.onyx @@ -4,8 +4,13 @@ use package core Textbox_Theme :: struct { use text_theme := Text_Theme.{ text_color = .{ 0, 0, 0 } + // text_color = .{1, 1, 1} }; + // background_color := gfx.Color4.{ 0.1, 0.1, 0.1 }; + // hover_color := gfx.Color4.{ 0.3, 0.3, 0.3 }; + // click_color := gfx.Color4.{ 0.5, 0.5, 0.7 }; + background_color := gfx.Color4.{ 0.8, 0.8, 0.8 }; hover_color := gfx.Color4.{ 1.0, 1.0, 1.0 }; click_color := gfx.Color4.{ 0.5, 0.5, 0.7 }; @@ -13,7 +18,7 @@ Textbox_Theme :: struct { border_color := gfx.Color4.{ 0.2, 0.2, 0.2 }; border_width := 6.0f; @InPixels - cursor_color := gfx.Color4.{ 0, 0, 0 }; + cursor_color := gfx.Color4.{ 0.5, 0.5, 0.5 }; } default_textbox_theme := Textbox_Theme.{}; @@ -28,9 +33,10 @@ Textbox_Editing_State :: struct { cursor_animation_speed := 0.02f; } +#private textbox_editing_state := Textbox_Editing_State.{}; -textbox :: (use r: Rectangle, text_buffer: ^String_Buffer, theme := ^default_textbox_theme, site := #callsite, increment := 0) -> bool { +textbox :: (use r: Rectangle, text_buffer: ^string.String_Buffer, theme := ^default_textbox_theme, site := #callsite, increment := 0) -> bool { result := false; hash := get_site_hash(site, increment); @@ -39,7 +45,7 @@ textbox :: (use r: Rectangle, text_buffer: ^String_Buffer, theme := ^default_tex border_width := theme.border_width; width, height := Rectangle.dimensions(r); - text := text_buffer.buffer; + text := string.buffer_to_str(text_buffer); text_width := bmfont.get_width(^font, text, theme.font_size); text_height := bmfont.get_height(^font, text, theme.font_size); @@ -50,6 +56,8 @@ textbox :: (use r: Rectangle, text_buffer: ^String_Buffer, theme := ^default_tex if mouse_state.left_button_down && Rectangle.contains(r, mouse_state.x, mouse_state.y) { set_active_item(hash); textbox_editing_state.hash = hash; + + // animation_state.click_time = 1.0f; } } @@ -82,19 +90,19 @@ textbox :: (use r: Rectangle, text_buffer: ^String_Buffer, theme := ^default_tex switch key.code { case 0x25 do textbox_editing_state.cursor_position -= 1; case 0x27 do textbox_editing_state.cursor_position += 1; - case 0x23 do textbox_editing_state.cursor_position = text_buffer.buffer.count; + case 0x23 do textbox_editing_state.cursor_position = text_buffer.count; case 0x24 do textbox_editing_state.cursor_position = 0; case 0x08 { // Backspace - if text_buffer->delete(textbox_editing_state.cursor_position) { + if string.buffer_delete(text_buffer, textbox_editing_state.cursor_position) { textbox_editing_state.cursor_position -= 1; } } case 0x2E { // Delete - text_buffer->delete(textbox_editing_state.cursor_position + 1); + string.buffer_delete(text_buffer, textbox_editing_state.cursor_position + 1); } case #default { @@ -104,14 +112,14 @@ textbox :: (use r: Rectangle, text_buffer: ^String_Buffer, theme := ^default_tex char := key_map[index]; if char != #char "\0" { - if text_buffer->insert(textbox_editing_state.cursor_position, char) { + if string.buffer_insert(text_buffer, textbox_editing_state.cursor_position, char) { textbox_editing_state.cursor_position += 1; } } } } - textbox_editing_state.cursor_position = math.clamp(textbox_editing_state.cursor_position, 0, text_buffer.buffer.count); + textbox_editing_state.cursor_position = math.clamp(textbox_editing_state.cursor_position, 0, text_buffer.count); textbox_editing_state.cursor_animation = 1.0f; } } @@ -157,12 +165,14 @@ textbox :: (use r: Rectangle, text_buffer: ^String_Buffer, theme := ^default_tex } #private_file -get_cursor_location :: (text_buffer: ^String_Buffer, text_x: f32, text_y: f32, text_size: f32, cursor_position: i32) -> f32 { +get_cursor_location :: (text_buffer: ^string.String_Buffer, text_x: f32, text_y: f32, text_size: f32, cursor_position: i32) -> f32 { countdown := cursor_position + 1; last_x : f32 = text_x; last_w : f32; - for glyph: bmfont.get_character_positions(^font, text_size, text_buffer.buffer, text_x, text_y) { + text := string.buffer_to_str(text_buffer); + + for glyph: bmfont.get_character_positions(^font, text_size, text, text_x, text_y) { if countdown == 0 do return last_x; last_x = glyph.pos_x; @@ -177,12 +187,13 @@ get_cursor_location :: (text_buffer: ^String_Buffer, text_x: f32, text_y: f32, t } #private_file -get_cursor_position :: (text_buffer: ^String_Buffer, text_x: f32, text_y: f32, text_size: f32, mouse_x: f32, mouse_y: f32) -> i32 { +get_cursor_position :: (text_buffer: ^string.String_Buffer, text_x: f32, text_y: f32, text_size: f32, mouse_x: f32, mouse_y: f32) -> i32 { cursor_position := 0; last_x: f32 = text_x; + text := string.buffer_to_str(text_buffer); - for glyph: bmfont.get_character_positions(^font, text_size, text_buffer.buffer, text_x, text_y) { + for glyph: bmfont.get_character_positions(^font, text_size, text, text_x, text_y) { cursor_position += 1; if cursor_position == 1 do continue; @@ -194,47 +205,9 @@ get_cursor_position :: (text_buffer: ^String_Buffer, text_x: f32, text_y: f32, t last_x = glyph.pos_x + glyph.pos_w / 2; } - return text_buffer.buffer.count; + return text_buffer.count; } -@Relocate // I think this should be part of the standard library? Or something similar to it? -String_Buffer :: struct { - @Todo // make 'use buffer : [] u8' work here - buffer : [] u8; - capacity : u32; - - insert :: (use sb: ^String_Buffer, position: i32, ch: u8) -> bool { - if position >= capacity do return false; - if buffer.count >= capacity do return false; - - while i := cast(i32) buffer.count; i > position { - defer i -= 1; - - buffer.data[i] = buffer.data[i - 1]; - } - - buffer.data[position] = ch; - buffer.count += 1; - return true; - } - - delete :: (use sb: ^String_Buffer, position: i32) -> bool { - if position > capacity do return false; - if buffer.count == 0 do return false; - if position == 0 do return false; - - while i := position - 1; i < cast(i32) buffer.count - 1{ - defer i += 1; - - buffer.data[i] = buffer.data[i + 1]; - } - - buffer.count -= 1; - return true; - } -} - - @Relocate @Cleanup // This keymap is very wrong in a lot of ways. It works for my standard US keyboard, but will break horribly // for any other keyboard layout. I would like to use something else from the browser, but unsurprisingly the diff --git a/modules/ui/ui.onyx b/modules/ui/ui.onyx index ae027920..526c2ca1 100644 --- a/modules/ui/ui.onyx +++ b/modules/ui/ui.onyx @@ -61,16 +61,20 @@ init_ui :: () { map.init(^animation_states, default=.{}, hash_count=4); } +// This function should be called at the end of drawing a frame, after all of the UI elements +// had a chance to interact with the hardware events. clear_buttons :: () { mouse_state.left_button_just_up = false; mouse_state.left_button_just_down = false; mouse_state.right_button_just_up = false; mouse_state.right_button_just_down = false; - for ^key: keyboard_state.keycodes_down_this_frame do *key = .{}; - keyboard_state.keys_down_this_frame = 0; + // I don't think these need to be cleared every frame, so long as you don't try + // to use them without checking keys_down_this_frame first. + // for ^key: keyboard_state.keycodes_down_this_frame do *key = .{}; + // for ^key: keyboard_state.keycodes_up_this_frame do *key = .{}; - for ^key: keyboard_state.keycodes_up_this_frame do *key = .{}; + keyboard_state.keys_down_this_frame = 0; keyboard_state.keys_up_this_frame = 0; if !hot_item_was_set do set_hot_item(0); @@ -129,7 +133,7 @@ key_up :: (keycode: u32, modifiers: Keyboard_State.Key_State.Modifiers) { keycode, modifiers }; - + keyboard_state.keys_up_this_frame += 1; } diff --git a/tests/aoc-2020/day16.onyx b/tests/aoc-2020/day16.onyx index f15c9db1..5228289d 100644 --- a/tests/aoc-2020/day16.onyx +++ b/tests/aoc-2020/day16.onyx @@ -20,7 +20,7 @@ field_valid :: (use field: ^Field, n: u32) -> bool { total_scanning_error := 0; -read_ticket_and_validate :: (file: ^reader.StringReader, fields: [..] Field, ticket_store: ^u32) -> bool { +read_ticket_and_validate :: (file: ^reader.String_Reader, fields: [..] Field, ticket_store: ^u32) -> bool { retval := true; for i: 0 .. fields.count { diff --git a/tests/aoc-2020/day18.onyx b/tests/aoc-2020/day18.onyx index 294fe09a..64512186 100644 --- a/tests/aoc-2020/day18.onyx +++ b/tests/aoc-2020/day18.onyx @@ -3,7 +3,7 @@ use package core reader :: package core.string.reader -parse_factor :: (file: ^reader.StringReader) -> u64 { +parse_factor :: (file: ^reader.String_Reader) -> u64 { reader.skip_whitespace(file); switch *file.data { @@ -27,7 +27,7 @@ parse_factor :: (file: ^reader.StringReader) -> u64 { return 0; } -parse_expression_add :: (file: ^reader.StringReader) -> u64 { +parse_expression_add :: (file: ^reader.String_Reader) -> u64 { reader.skip_whitespace(file); left := parse_factor(file); @@ -45,7 +45,7 @@ parse_expression_add :: (file: ^reader.StringReader) -> u64 { return left; } -parse_expression_mul :: (file: ^reader.StringReader) -> u64 { +parse_expression_mul :: (file: ^reader.String_Reader) -> u64 { reader.skip_whitespace(file); left := parse_expression_add(file);