border_color := gfx.Color4.{ 0.2, 0.2, 0.2 };
border_width := 6.0f; @InPixels
+
+ cursor_color := gfx.Color4.{ 0, 0, 0 };
}
default_textbox_theme := Textbox_Theme.{};
-textbox :: (use r: Rectangle, text: str, theme := ^default_textbox_theme, site := #callsite, increment := 0) -> bool {
+#private_file
+Textbox_Editing_State :: struct {
+ hash: UI_Id = 0;
+
+ cursor_position: i32 = 0;
+
+ cursor_animation := 0.0f;
+ cursor_animation_speed := 0.02f;
+}
+
+textbox_editing_state := Textbox_Editing_State.{};
+
+textbox :: (use r: Rectangle, text_buffer: ^String_Buffer, theme := ^default_textbox_theme, site := #callsite, increment := 0) -> bool {
result := false;
hash := get_site_hash(site, increment);
animation_state := map.get(^animation_states, hash);
+ border_width := theme.border_width;
+ width, height := Rectangle.dimensions(r);
+
+ text := text_buffer.buffer;
+ text_width := bmfont.get_width(^font, text, theme.font_size);
+ text_height := bmfont.get_height(^font, text, theme.font_size);
+
+ text_x := x0 + border_width;
+ text_y := y0 + ~~ font.common.baseline * theme.font_size + (height - text_height) / 2;
+
+ if is_hot_item(hash) && !is_active_item(hash) {
+ if mouse_state.left_button_down && Rectangle.contains(r, mouse_state.x, mouse_state.y) {
+ set_active_item(hash);
+ textbox_editing_state.hash = hash;
+ }
+ }
+
+ if is_active_item(hash) {
+ if mouse_state.left_button_just_down && !Rectangle.contains(r, mouse_state.x, mouse_state.y) {
+ set_active_item(0);
+ textbox_editing_state.hash = 0;
+ textbox_editing_state.cursor_position = 0;
+ }
+ }
+
if Rectangle.contains(r, mouse_state.x, mouse_state.y) {
set_hot_item(hash);
}
+ if textbox_editing_state.hash == hash {
+ // This is the actively edited textbox
+ move_towards(^textbox_editing_state.cursor_animation, 0.0f, textbox_editing_state.cursor_animation_speed);
+ if textbox_editing_state.cursor_animation <= 0.0f do textbox_editing_state.cursor_animation = 1.0f;
+
+ if mouse_state.left_button_down && Rectangle.contains(r, mouse_state.x, mouse_state.y) {
+ textbox_editing_state.cursor_animation = 1.0f;
+ textbox_editing_state.cursor_position = get_cursor_position(text_buffer, text_x, text_y, theme.font_size, mouse_state.x, mouse_state.y);
+ }
+
+ if keyboard_state.keys_down_this_frame > 0 {
+ for key_index: keyboard_state.keys_down_this_frame {
+ key := keyboard_state.keycodes_down_this_frame[key_index];
+
+ 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 0x24 do textbox_editing_state.cursor_position = 0;
+
+ case 0x08 {
+ // Backspace
+ if text_buffer->delete(textbox_editing_state.cursor_position) {
+ textbox_editing_state.cursor_position -= 1;
+ }
+ }
+
+ case 0x2E {
+ // Delete
+ text_buffer->delete(textbox_editing_state.cursor_position + 1);
+ }
+
+ case #default {
+ shift_is_pressed := key.modifiers & .SHIFT;
+ index := key.code * 2;
+ if shift_is_pressed do index += 1;
+
+ char := key_map[index];
+ if char != #char "\0" {
+ if text_buffer->insert(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_animation = 1.0f;
+ }
+ }
+ }
+
if is_hot_item(hash) {
move_towards(^animation_state.hover_time, 1.0f, 0.1f); @ThemeConfiguration
} else {
move_towards(^animation_state.hover_time, 0.0f, 0.1f); @ThemeConfiguration
}
- border_width := theme.border_width;
- width, height := Rectangle.dimensions(r);
-
gfx.set_texture();
gfx.rect(.{ x0, y0 }, .{ width, height }, theme.border_color);
surface_color = color_lerp(animation_state.click_time, surface_color, theme.click_color);
gfx.rect(.{ x0 + border_width, y0 + border_width }, .{ width - border_width * 2, height - border_width * 2 }, surface_color);
- text_width := bmfont.get_width(^font, text, theme.font_size);
- text_height := bmfont.get_height(^font, text, theme.font_size);
+ draw_text_raw(text, text_x, text_y, theme.font_size, theme.text_color);
- draw_text_raw(text,
- x0 + border_width,
- y0 + ~~ font.common.baseline * theme.font_size + (height - text_height) / 2,
- theme.font_size, theme.text_color);
+ if textbox_editing_state.hash == hash {
+ cursor_x := get_cursor_location(text_buffer, text_x, text_y, theme.font_size, textbox_editing_state.cursor_position);
+ cursor_y := y0 + theme.border_width;
+ cursor_h := height - theme.border_width * 2;
+
+ cursor_color := theme.cursor_color;
+ cursor_color.a = textbox_editing_state.cursor_animation;
+
+ draw_rect(
+ .{ cursor_x, cursor_y, cursor_x + 4, cursor_y + cursor_h },
+ color=cursor_color); @ThemeConfiguration
+ }
move_towards(^animation_state.click_time, 0.0f, 0.08f); @ThemeConfiguration
} else {
map.delete(^animation_states, hash);
}
-}
\ No newline at end of file
+
+ return result;
+}
+
+#private_file
+get_cursor_location :: (text_buffer: ^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) {
+ if countdown == 0 do return last_x;
+
+ last_x = glyph.pos_x;
+ last_w = glyph.x_advance;
+
+ countdown -= 1;
+ }
+
+ if countdown == 0 do return last_x;
+
+ return last_x + last_w;
+}
+
+#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 {
+ cursor_position := 0;
+
+ last_x: f32 = text_x;
+
+ for glyph: bmfont.get_character_positions(^font, text_size, text_buffer.buffer, text_x, text_y) {
+ cursor_position += 1;
+ if cursor_position == 1 do continue;
+
+ @Incomplete // This is still very wrong but it is better than nothing
+ if mouse_x <= glyph.pos_x + glyph.pos_w / 2 && mouse_x >= last_x {
+ return cursor_position - 1;
+ }
+
+ last_x = glyph.pos_x + glyph.pos_w / 2;
+ }
+
+ return text_buffer.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
+// browser does not make this easy. Gotta love web "standards"....
+key_map := u8.[
+ // Keycode Normal Shift
+ /* 00 */ #char "\0", #char "\0",
+ /* 01 */ #char "\0", #char "\0",
+ /* 02 */ #char "\0", #char "\0",
+ /* 03 */ #char "\0", #char "\0",
+ /* 04 */ #char "\0", #char "\0",
+ /* 05 */ #char "\0", #char "\0",
+ /* 06 */ #char "\0", #char "\0",
+ /* 07 */ #char "\0", #char "\0",
+ /* 08 */ #char "\0", #char "\0",
+ /* 09 */ #char "\0", #char "\0",
+ /* 10 */ #char "\0", #char "\0",
+ /* 11 */ #char "\0", #char "\0",
+ /* 12 */ #char "\0", #char "\0",
+ /* 13 */ #char "\0", #char "\0",
+ /* 14 */ #char "\0", #char "\0",
+ /* 15 */ #char "\0", #char "\0",
+ /* 16 */ #char "\0", #char "\0",
+ /* 17 */ #char "\0", #char "\0",
+ /* 18 */ #char "\0", #char "\0",
+ /* 19 */ #char "\0", #char "\0",
+ /* 20 */ #char "\0", #char "\0",
+ /* 21 */ #char "\0", #char "\0",
+ /* 22 */ #char "\0", #char "\0",
+ /* 23 */ #char "\0", #char "\0",
+ /* 24 */ #char "\0", #char "\0",
+ /* 25 */ #char "\0", #char "\0",
+ /* 26 */ #char "\0", #char "\0",
+ /* 27 */ #char "\0", #char "\0",
+ /* 28 */ #char "\0", #char "\0",
+ /* 29 */ #char "\0", #char "\0",
+ /* 30 */ #char "\0", #char "\0",
+ /* 31 */ #char "\0", #char "\0",
+ /* 32 */ #char " ", #char " ",
+ /* 33 */ #char "\0", #char "\0",
+ /* 34 */ #char "\0", #char "\0",
+ /* 35 */ #char "\0", #char "\0",
+ /* 36 */ #char "\0", #char "\0",
+ /* 37 */ #char "\0", #char "\0",
+ /* 38 */ #char "\0", #char "\0",
+ /* 39 */ #char "\0", #char "\0",
+ /* 40 */ #char "\0", #char "\0",
+ /* 41 */ #char "\0", #char "\0",
+ /* 42 */ #char "\0", #char "\0",
+ /* 43 */ #char "\0", #char "\0",
+ /* 44 */ #char "\0", #char "\0",
+ /* 45 */ #char "\0", #char "\0",
+ /* 46 */ #char "\0", #char "\0",
+ /* 47 */ #char "\0", #char "\0",
+ /* 48 */ #char "0", #char ")",
+ /* 49 */ #char "1", #char "!",
+ /* 50 */ #char "2", #char "@",
+ /* 51 */ #char "3", #char "#",
+ /* 52 */ #char "4", #char "$",
+ /* 53 */ #char "5", #char "%",
+ /* 54 */ #char "6", #char "^",
+ /* 55 */ #char "7", #char "&",
+ /* 56 */ #char "8", #char "*",
+ /* 57 */ #char "9", #char "(",
+ /* 58 */ #char "\0", #char "\0",
+ /* 59 */ #char ";", #char ":",
+ /* 60 */ #char "\0", #char "\0",
+ /* 61 */ #char "=", #char "+",
+ /* 62 */ #char "\0", #char "\0",
+ /* 63 */ #char "\0", #char "\0",
+ /* 64 */ #char "\0", #char "\0",
+ /* 65 */ #char "a", #char "A",
+ /* 66 */ #char "b", #char "B",
+ /* 67 */ #char "c", #char "C",
+ /* 68 */ #char "d", #char "D",
+ /* 69 */ #char "e", #char "E",
+ /* 70 */ #char "f", #char "F",
+ /* 71 */ #char "g", #char "G",
+ /* 72 */ #char "h", #char "H",
+ /* 73 */ #char "i", #char "I",
+ /* 74 */ #char "j", #char "J",
+ /* 75 */ #char "k", #char "K",
+ /* 76 */ #char "l", #char "L",
+ /* 77 */ #char "m", #char "M",
+ /* 78 */ #char "n", #char "N",
+ /* 79 */ #char "o", #char "O",
+ /* 80 */ #char "p", #char "P",
+ /* 81 */ #char "q", #char "Q",
+ /* 82 */ #char "r", #char "R",
+ /* 83 */ #char "s", #char "S",
+ /* 84 */ #char "t", #char "T",
+ /* 85 */ #char "u", #char "U",
+ /* 86 */ #char "v", #char "V",
+ /* 87 */ #char "w", #char "W",
+ /* 88 */ #char "x", #char "X",
+ /* 89 */ #char "y", #char "Y",
+ /* 90 */ #char "z", #char "Z",
+ /* 91 */ #char "\0", #char "\0",
+ /* 92 */ #char "\0", #char "\0",
+ /* 93 */ #char "\0", #char "\0",
+ /* 94 */ #char "\0", #char "\0",
+ /* 95 */ #char "\0", #char "\0",
+ /* 96 */ #char "\0", #char "\0",
+ /* 97 */ #char "\0", #char "\0",
+ /* 98 */ #char "\0", #char "\0",
+ /* 99 */ #char "\0", #char "\0",
+ /* 100 */ #char "\0", #char "\0",
+ /* 101 */ #char "\0", #char "\0",
+ /* 102 */ #char "\0", #char "\0",
+ /* 103 */ #char "\0", #char "\0",
+ /* 104 */ #char "\0", #char "\0",
+ /* 105 */ #char "\0", #char "\0",
+ /* 106 */ #char "\0", #char "\0",
+ /* 107 */ #char "\0", #char "\0",
+ /* 108 */ #char "\0", #char "\0",
+ /* 109 */ #char "\0", #char "\0",
+ /* 110 */ #char "\0", #char "\0",
+ /* 111 */ #char "\0", #char "\0",
+ /* 112 */ #char "\0", #char "\0",
+ /* 113 */ #char "\0", #char "\0",
+ /* 114 */ #char "\0", #char "\0",
+ /* 115 */ #char "\0", #char "\0",
+ /* 116 */ #char "\0", #char "\0",
+ /* 117 */ #char "\0", #char "\0",
+ /* 118 */ #char "\0", #char "\0",
+ /* 119 */ #char "\0", #char "\0",
+ /* 120 */ #char "\0", #char "\0",
+ /* 121 */ #char "\0", #char "\0",
+ /* 122 */ #char "\0", #char "\0",
+ /* 123 */ #char "\0", #char "\0",
+ /* 124 */ #char "\0", #char "\0",
+ /* 125 */ #char "\0", #char "\0",
+ /* 126 */ #char "\0", #char "\0",
+ /* 127 */ #char "\0", #char "\0",
+ /* 128 */ #char "\0", #char "\0",
+ /* 129 */ #char "\0", #char "\0",
+ /* 130 */ #char "\0", #char "\0",
+ /* 131 */ #char "\0", #char "\0",
+ /* 132 */ #char "\0", #char "\0",
+ /* 133 */ #char "\0", #char "\0",
+ /* 134 */ #char "\0", #char "\0",
+ /* 135 */ #char "\0", #char "\0",
+ /* 136 */ #char "\0", #char "\0",
+ /* 137 */ #char "\0", #char "\0",
+ /* 138 */ #char "\0", #char "\0",
+ /* 139 */ #char "\0", #char "\0",
+ /* 140 */ #char "\0", #char "\0",
+ /* 141 */ #char "\0", #char "\0",
+ /* 142 */ #char "\0", #char "\0",
+ /* 143 */ #char "\0", #char "\0",
+ /* 144 */ #char "\0", #char "\0",
+ /* 145 */ #char "\0", #char "\0",
+ /* 146 */ #char "\0", #char "\0",
+ /* 147 */ #char "\0", #char "\0",
+ /* 148 */ #char "\0", #char "\0",
+ /* 149 */ #char "\0", #char "\0",
+ /* 150 */ #char "\0", #char "\0",
+ /* 151 */ #char "\0", #char "\0",
+ /* 152 */ #char "\0", #char "\0",
+ /* 153 */ #char "\0", #char "\0",
+ /* 154 */ #char "\0", #char "\0",
+ /* 155 */ #char "\0", #char "\0",
+ /* 156 */ #char "\0", #char "\0",
+ /* 157 */ #char "\0", #char "\0",
+ /* 158 */ #char "\0", #char "\0",
+ /* 159 */ #char "\0", #char "\0",
+ /* 160 */ #char "\0", #char "\0",
+ /* 161 */ #char "\0", #char "\0",
+ /* 162 */ #char "\0", #char "\0",
+ /* 163 */ #char "\0", #char "\0",
+ /* 164 */ #char "\0", #char "\0",
+ /* 165 */ #char "\0", #char "\0",
+ /* 166 */ #char "\0", #char "\0",
+ /* 167 */ #char "\0", #char "\0",
+ /* 168 */ #char "\0", #char "\0",
+ /* 169 */ #char "\0", #char "\0",
+ /* 170 */ #char "\0", #char "\0",
+ /* 171 */ #char "\0", #char "\0",
+ /* 172 */ #char "\0", #char "\0",
+ /* 173 */ #char "-", #char "_",
+ /* 174 */ #char "\0", #char "\0",
+ /* 175 */ #char "\0", #char "\0",
+ /* 176 */ #char "\0", #char "\0",
+ /* 177 */ #char "\0", #char "\0",
+ /* 178 */ #char "\0", #char "\0",
+ /* 179 */ #char "\0", #char "\0",
+ /* 180 */ #char "\0", #char "\0",
+ /* 181 */ #char "\0", #char "\0",
+ /* 182 */ #char "\0", #char "\0",
+ /* 183 */ #char "\0", #char "\0",
+ /* 184 */ #char "\0", #char "\0",
+ /* 185 */ #char "\0", #char "\0",
+ /* 186 */ #char ";", #char ":",
+ /* 187 */ #char "=", #char "+",
+ /* 188 */ #char ",", #char "<",
+ /* 189 */ #char "-", #char "_",
+ /* 190 */ #char ".", #char ">",
+ /* 191 */ #char "/", #char "?",
+ /* 192 */ #char "`", #char "~",
+ /* 193 */ #char "\0", #char "\0",
+ /* 194 */ #char "\0", #char "\0",
+ /* 195 */ #char "\0", #char "\0",
+ /* 196 */ #char "\0", #char "\0",
+ /* 197 */ #char "\0", #char "\0",
+ /* 198 */ #char "\0", #char "\0",
+ /* 199 */ #char "\0", #char "\0",
+ /* 200 */ #char "\0", #char "\0",
+ /* 201 */ #char "\0", #char "\0",
+ /* 202 */ #char "\0", #char "\0",
+ /* 203 */ #char "\0", #char "\0",
+ /* 204 */ #char "\0", #char "\0",
+ /* 205 */ #char "\0", #char "\0",
+ /* 206 */ #char "\0", #char "\0",
+ /* 207 */ #char "\0", #char "\0",
+ /* 208 */ #char "\0", #char "\0",
+ /* 209 */ #char "\0", #char "\0",
+ /* 210 */ #char "\0", #char "\0",
+ /* 211 */ #char "\0", #char "\0",
+ /* 212 */ #char "\0", #char "\0",
+ /* 213 */ #char "\0", #char "\0",
+ /* 214 */ #char "\0", #char "\0",
+ /* 215 */ #char "\0", #char "\0",
+ /* 216 */ #char "\0", #char "\0",
+ /* 217 */ #char "\0", #char "\0",
+ /* 218 */ #char "\0", #char "\0",
+ /* 219 */ #char "[", #char "{",
+ /* 220 */ #char "\\", #char "|",
+ /* 221 */ #char "]", #char "}",
+ /* 222 */ #char "'", #char "\"",
+ /* 223 */ #char "\0", #char "\0",
+ /* 224 */ #char "\0", #char "\0",
+ /* 225 */ #char "\0", #char "\0",
+ /* 226 */ #char "\0", #char "\0",
+ /* 227 */ #char "\0", #char "\0",
+ /* 228 */ #char "\0", #char "\0",
+ /* 229 */ #char "\0", #char "\0",
+];
\ No newline at end of file