small parser bugfix; working on textboxes in ui library
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 13 Jun 2021 19:59:12 +0000 (14:59 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 13 Jun 2021 19:59:12 +0000 (14:59 -0500)
modules/bmfont/position.onyx
modules/ui/components/textbox.onyx
modules/ui/ui.onyx
src/onyxparser.c

index d598e796dac03094ea1861ed574fcdb48bfa99fb..75e229963a84b24fad82294c10991254e5d44fdf 100644 (file)
@@ -8,6 +8,8 @@ package bmfont
 
     tex_x, tex_y : f32;
     tex_w, tex_h : f32;
+
+    x_advance : f32;
 }
 #private_file renderable_glyph: Renderable_Glyph;
 
@@ -62,8 +64,7 @@ get_character_positions :: (font: ^BMFont, size: f32, text: str, x: f32, y: f32)
             glyph := rc.font->get_glyph(char);
 
             if glyph == null {
-                glyph = rc.font->get_glyph(255);
-                assert(glyph != null, "NO NULL GLYPH");
+                continue;
             }
 
             renderable_glyph.pos_x = math.floor(rc.x + ~~glyph.xoffset * rc.size + .5);
@@ -76,6 +77,7 @@ get_character_positions :: (font: ^BMFont, size: f32, text: str, x: f32, y: f32)
             renderable_glyph.tex_w = glyph.tex_w;
             renderable_glyph.tex_h = glyph.tex_h;
 
+            renderable_glyph.x_advance = ~~glyph.xadvance * rc.size;
             rc.x += ~~glyph.xadvance * rc.size;
 
             return ^renderable_glyph, true;
index 6d1bf6c07f8b30f3bc90a8d08c6a0558a2e54924..d6e4d75ccc508b718e02cadf1462fa224ca3c357 100644 (file)
@@ -12,29 +12,117 @@ 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 };
 }
 
 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);
 
@@ -42,13 +130,20 @@ textbox :: (use r: Rectangle, text: str, theme := ^default_textbox_theme, site :
     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
 
@@ -57,4 +152,323 @@ textbox :: (use r: Rectangle, text: str, theme := ^default_textbox_theme, site :
     } 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
index c7922100d8748ab3516f661a4264708e7a8a3118..ae02792063cfadc4e0c8739ac3ee4b18b863d10d 100644 (file)
@@ -17,18 +17,42 @@ UI_Id :: #type u32
 #private active_item : UI_Id = 0
 #private hot_item_was_set := false
 
-MouseState :: struct {
-    left_button_down    := false;
-    left_button_just_up := false;
+Mouse_State :: struct {
+    left_button_down      := false;
+    left_button_just_down := false;
+    left_button_just_up   := false;
 
-    right_button_down    := false;
-    right_button_just_up := false;
+    right_button_down      := false;
+    right_button_just_down := false;
+    right_button_just_up   := false;
 
     x: f32 = 0;
     y: f32 = 0;
 }
 
-mouse_state := MouseState.{};
+mouse_state := Mouse_State.{};
+
+Keyboard_State :: struct {
+    Max_Keys_Per_Frame :: 4;
+
+    Key_State :: struct {
+        code: u32 = 0;
+        modifiers: Modifiers = ~~0;
+
+        Modifiers :: enum #flags {
+            CTRL; ALT; SHIFT; META;
+        }
+    }
+
+    keycodes_down_this_frame  : [Max_Keys_Per_Frame] Key_State;
+    keys_down_this_frame      : u32;
+
+    keycodes_up_this_frame    : [Max_Keys_Per_Frame] Key_State;
+    keys_up_this_frame        : u32;
+}
+
+@Note // This assumes that this gets zero intialized.
+keyboard_state: Keyboard_State;
 
 
 init_ui :: () {
@@ -38,8 +62,16 @@ init_ui :: () {
 }
 
 clear_buttons :: () {
-    mouse_state.left_button_just_up = false;
-    mouse_state.right_button_just_up = false;
+    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;
+
+    for ^key: keyboard_state.keycodes_up_this_frame do *key = .{};
+    keyboard_state.keys_up_this_frame = 0;
 
     if !hot_item_was_set do set_hot_item(0);
     hot_item_was_set = false;
@@ -54,16 +86,22 @@ update_mouse_position :: (new_x: f32, new_y: f32) {
     mouse_state.y = new_y;
 }
 
-#private_file ButtonKind :: enum { Left; Right; Middle; }
+#private_file Mouse_Button_Kind :: enum { Left; Right; Middle; }
 
-button_pressed :: (kind: ButtonKind) {
+button_pressed :: (kind: Mouse_Button_Kind) {
     switch kind {
-        case .Left  do mouse_state.left_button_down  = true;
-        case .Right do mouse_state.right_button_down = true;
+        case .Left {
+            mouse_state.left_button_down      = true;
+            mouse_state.left_button_just_down = true;
+        }
+        case .Right {
+            mouse_state.right_button_down      = true;
+            mouse_state.right_button_just_down = true;
+        }
     }
 }
 
-button_released :: (kind: ButtonKind) {
+button_released :: (kind: Mouse_Button_Kind) {
     switch kind {
         case .Left {
             mouse_state.left_button_down    = false;
@@ -77,6 +115,23 @@ button_released :: (kind: ButtonKind) {
     }
 }
 
+key_down :: (keycode: u32, modifiers: Keyboard_State.Key_State.Modifiers) {
+    keyboard_state.keycodes_down_this_frame[keyboard_state.keys_down_this_frame] = .{
+        keycode,
+        modifiers
+    };
+
+    keyboard_state.keys_down_this_frame += 1;
+}
+
+key_up :: (keycode: u32, modifiers: Keyboard_State.Key_State.Modifiers) {
+    keyboard_state.keycodes_up_this_frame[keyboard_state.keys_up_this_frame] = .{
+        keycode,
+        modifiers
+    };
+    
+    keyboard_state.keys_up_this_frame += 1;
+}
 
 set_active_item :: (id: UI_Id) -> bool {
     active_item = id;
index 00e38aafc50f74889eafc7d894dd8eb0f6c77659..0616cbce643085371ea9cb780ce44003affe0668 100644 (file)
@@ -259,6 +259,8 @@ static b32 parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstT
     expect_token(parser, '.');
     expect_token(parser, '[');
     while (!consume_token_if_next(parser, ']')) {
+        if (parser->hit_unexpected_token) return 1;
+        
         AstTyped* value = parse_expression(parser, 0);
         bh_arr_push(al->values, value);