added checkboxes to ui package
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 10 Jun 2021 02:58:25 +0000 (21:58 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 10 Jun 2021 02:58:25 +0000 (21:58 -0500)
modules/bmfont/bmfont_loader.onyx
modules/bmfont/types.onyx
modules/bmfont/utils.onyx
modules/ui/components/button.onyx
modules/ui/components/checkbox.onyx [new file with mode: 0644]
modules/ui/flow.onyx
modules/ui/module.onyx
modules/ui/ui.onyx

index 014144df0b53968d85436f0515a864dd6cb2fb3f..837292242e2904bec3fb4c821bb10954f6bc160f 100644 (file)
@@ -18,9 +18,6 @@ load_bmfont :: (fnt_data: [] u8) -> BMFont {
         glyph.value.tex_h = ~~ glyph.value.h / cast(f32) font.common.scale_width;        
     });
 
-    m_glyph := map.get_ptr(^bmf.glyphs, #char "M");
-    bmf.em = ~~m_glyph.h / cast(f32) bmf.common.line_height;
-
     return bmf;
 }
 
index 03456c4b9e6a9598db8fc01a6556b49c9b84aca3..fa97bc59573f9ec9624a033d0a6175618af36cd9 100644 (file)
@@ -10,8 +10,6 @@ BMFont :: struct {
     pages  : map.Map(i32, str);
     glyphs : map.Map(i32, BMFont_Glyph);
 
-    em : f32;
-
     get_glyph :: (use bmfont: ^BMFont, char: u8) -> ^BMFont_Glyph {
         return map.get_ptr(^glyphs, ~~char);
     }
index c4702976ea77bee6045725f91db65113f4851c27..87f160a6996a6ac92c2bc1194b5f987204e41c8d 100644 (file)
@@ -28,24 +28,37 @@ get_width :: (use font: ^BMFont, text: str, size: f32) -> f32 {
 
 @Incomplete // does not use the size parameter
 get_height :: (use font: ^BMFont, text: str, size: f32) -> f32 {
-    max_y := 0.0f;
-    y := 0.0f;
+    line_count := 0;
 
     for char: text {
         if char == #char "\n" {
-            y += max_y;
-            max_y = 0;
-            continue;
+            line_count += 1;
         }
+    }
 
-        glyph := font->get_glyph(char);
+    return ~~(line_count + 1) * size * ~~font.common.line_height;
 
-        if glyph == null {
-            glyph = font->get_glyph(0);
+    // Old way that was wrong
+    #if false {
+        max_y := 0.0f;
+        y := 0.0f;
+
+        for char: text {
+            if char == #char "\n" {
+                y += max_y;
+                max_y = 0;
+                continue;
+            }
+
+            glyph := font->get_glyph(char);
+
+            if glyph == null {
+                glyph = font->get_glyph(0);
+            }
+
+            max_y = math.max(max_y, ~~glyph.h * size);
         }
 
-        max_y = math.max(max_y, ~~glyph.h * size);
+        return y + max_y;
     }
-
-    return y + max_y;
 }
\ No newline at end of file
index f5e3d9477f63ec005cfd109f8ee7f7ed93c7098a..c165a99c3db7508289494fab0df1ec1f3ee592b3 100644 (file)
@@ -59,8 +59,6 @@ button :: (use r: Rectangle, text: str, theme := ^default_button_theme, site :=
         move_towards(^button_state.hover_time, 0.0f, 0.1f);  @ThemeConfiguration
     }
 
-    move_towards(^button_state.click_time, 0.0f, 0.08f);     @ThemeConfiguration
-
     border_width  := theme.border_width;
     width, height := Rectangle.dimensions(r);
 
@@ -74,7 +72,12 @@ button :: (use r: Rectangle, text: str, theme := ^default_button_theme, site :=
     text_height := bmfont.get_height(^font, text, theme.font_size);
 
     @ThemeConfiguration // This always draws the text centered on the button surface.
-    draw_text_raw(text, x0 + (width - text_width) / 2, y0 + ~~ font.common.baseline * theme.font_size + (height - text_height) / 2, theme.font_size, theme.text_color);
+    draw_text_raw(text,
+            x0 + (width - text_width) / 2,
+            y0 + ~~ font.common.baseline * theme.font_size + (height - text_height) / 2,
+            theme.font_size, theme.text_color);
+
+    move_towards(^button_state.click_time, 0.0f, 0.08f);     @ThemeConfiguration
 
     if button_state.click_time > 0 || button_state.hover_time > 0 {
         map.put(^button_states, hash, button_state);
diff --git a/modules/ui/components/checkbox.onyx b/modules/ui/components/checkbox.onyx
new file mode 100644 (file)
index 0000000..5cda0fd
--- /dev/null
@@ -0,0 +1,109 @@
+package ui
+use package core
+
+#private checkbox_states : map.Map(UI_Id, Checkbox_State);
+#private Checkbox_State :: struct {
+    hover_time := 0.0f;
+    click_time := 0.0f;
+}
+
+Checkbox_Theme :: struct {
+    use text_theme := Text_Theme.{};
+
+    box_color := gfx.Color4.{ 0.2, 0.2, 0.2 };
+    box_width := 4.0f;    @InPixels
+    box_size  := 20.0f;   @InPixels
+
+    checked_color       := gfx.Color4.{ 1, 0, 0 };
+    checked_hover_color := gfx.Color4.{ 1, 0.3, 0.3 };
+
+    background_color := gfx.Color4.{ 0.05, 0.05, 0.05 };  // Background of the checkbox button.
+    hover_color      := gfx.Color4.{ 0.3, 0.3, 0.3 };
+    click_color      := gfx.Color4.{ 0.5, 0.5, 0.7 };
+}
+
+default_checkbox_theme: Checkbox_Theme = Checkbox_Theme.{};
+
+@Themeing
+checkbox :: (use r: Rectangle, value: ^bool, text: str, theme := ^default_checkbox_theme, site := #callsite, increment := 0) -> bool {
+    result := false;
+
+    hash := get_site_hash(site, increment);
+    checkbox_state := map.get(^checkbox_states, hash);
+
+    if is_active_item(hash) {
+        if mouse_state.left_button_just_up {
+            if is_hot_item(hash) && Rectangle.contains(r, mouse_state.x, mouse_state.y) {
+                result = true;
+                *value = !*value;
+                checkbox_state.click_time = 1.0f;
+            }
+
+            set_active_item(0);
+        }
+
+    } elseif is_hot_item(hash) {
+        if mouse_state.left_button_down {
+            set_active_item(hash);
+        }
+    }
+
+    if Rectangle.contains(r, mouse_state.x, mouse_state.y) {
+        set_hot_item(hash);
+    }
+
+    if is_hot_item(hash) {
+        move_towards(^checkbox_state.hover_time, 1.0f, 0.1f);  @ThemeConfiguration
+    } else {
+        move_towards(^checkbox_state.hover_time, 0.0f, 0.1f);  @ThemeConfiguration
+    }
+
+
+    box_width := theme.box_width;
+    box_size  := theme.box_size;
+    width, height := Rectangle.dimensions(r);
+
+    gfx.set_texture();
+    gfx.rect(
+        .{ x0 + 4, y0 + (height - box_size) / 2 },   @Cleanup @ThemeConfiguration // Arbitrary padding on left
+        .{ box_size, box_size },
+        color=theme.box_color,
+    );
+
+    surface_color : gfx.Color4;
+    if *value {
+        surface_color = theme.checked_color;
+        surface_color = color_lerp(checkbox_state.hover_time, surface_color, theme.checked_hover_color);
+
+    } else {
+        surface_color = theme.background_color;
+        surface_color = color_lerp(checkbox_state.hover_time, surface_color, theme.hover_color);
+    }
+
+    surface_color = color_lerp(checkbox_state.click_time, surface_color, theme.click_color);
+    
+    gfx.rect(
+        .{ x0 + 4 + box_width, y0 + (height - box_size) / 2 + box_width },
+        .{ box_size - box_width * 2, box_size - box_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,
+        x0 + box_size + 4 * 2,   @Cleanup @ThemeConfiguration
+        y0 + ~~ font.common.baseline * theme.font_size + (height - text_height) / 2,
+        theme.font_size, theme.text_color);
+
+    move_towards(^checkbox_state.click_time, 0.0f, 0.08f);   @ThemeConfiguration
+
+    if checkbox_state.click_time > 0 || checkbox_state.hover_time > 0 {
+        map.put(^checkbox_states, hash, checkbox_state);
+    } else {
+        map.delete(^checkbox_states, hash);
+    }
+
+    return result;
+}
\ No newline at end of file
index 3c389455b24edc23a43d881673041f3a9d2a3bac..3079298af742021293e18295bdeac4cf1ad748d1 100644 (file)
@@ -55,4 +55,12 @@ Flow :: struct {
                    .{ x0=x0, x1=x1, y0=y1-bottom_height, y1=y1 };
         }
     }
+
+
+    padding :: (r: Rectangle, top := 0.0f, bottom := 0.0f, left := 0.0f, right := 0.0f) -> Rectangle {
+        x0, y0 := Rectangle.top_left(r);
+        x1, y1 := Rectangle.bottom_right(r);
+
+        return .{ x0=x0 + left, x1=x1 - right, y0=y0 + top, y1=y1 - bottom };
+    }
 }
\ No newline at end of file
index 82cf6c92fa6b9a0fd684bd3b364d64abd5f1731d..1586000aa01c14265c294f01a2f7fbc684d7020c 100644 (file)
@@ -20,6 +20,7 @@ package ui
 #load "./ui"
 #load "./flow"
 #load "./components/button"
+#load "./components/checkbox"
 
 
 // Package inclusions that are part of all files in the "ui" package.
index 167679891a1849da227a9ce7ecfa0029af24cf9b..7afcf88cb732521fca347f7552e95acaa1fde7f3 100644 (file)
@@ -35,6 +35,7 @@ init_ui :: () {
     init_font();
 
     map.init(^button_states, default=.{}, hash_count=4);
+    map.init(^checkbox_states, default=.{}, hash_count=4);
 }
 
 clear_buttons :: () {
@@ -100,6 +101,7 @@ is_hot_item :: (id: UI_Id) -> bool {
     return hot_item == id;
 }
 
+@FontSizing // Currently, `size` is just a multipler for the baked font size. This should be changed to be height in pixels, or 'em's.
 draw_text_raw :: (text: str, x: f32, y: f32, size := DEFAULT_TEXT_SIZE, color := gfx.Color4.{1,1,1}) {
     gl.activeTexture(gl.TEXTURE0);
     gl.bindTexture(gl.TEXTURE_2D, font_texture);
@@ -188,7 +190,7 @@ Rectangle :: struct {
 
 
 
+
 @Relocate
 Text_Theme :: struct {
     text_color := gfx.Color4.{ 1, 1, 1 };
@@ -196,7 +198,7 @@ Text_Theme :: struct {
 }
 
 default_text_theme:   Text_Theme   = Text_Theme.{};
-
+    
 
 
 
@@ -234,8 +236,8 @@ get_text_width :: (text: str, size := DEFAULT_TEXT_SIZE) -> f32 {
     font_texture = gl.createTexture();
     gl.bindTexture(gl.TEXTURE_2D, font_texture);
     gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, tex_width, tex_height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, texture_data);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
     gl.bindTexture(gl.TEXTURE_2D, -1);
@@ -256,6 +258,6 @@ get_text_width :: (text: str, size := DEFAULT_TEXT_SIZE) -> f32 {
         r = c1.r * (1 - t) + c2.r * t,
         g = c1.g * (1 - t) + c2.g * t,
         b = c1.b * (1 - t) + c2.b * t,
-        a = c1.a + c2.a,
+        a = c1.a * (1 - t) + c2.a * t,   @Cleanup // should this be interpolating alphas?
     };
 }
\ No newline at end of file