From 744d0a043b25ab4c4d533230cdb74a42d6a2333a Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Wed, 9 Jun 2021 21:58:25 -0500 Subject: [PATCH] added checkboxes to ui package --- modules/bmfont/bmfont_loader.onyx | 3 - modules/bmfont/types.onyx | 2 - modules/bmfont/utils.onyx | 35 ++++++--- modules/ui/components/button.onyx | 9 ++- modules/ui/components/checkbox.onyx | 109 ++++++++++++++++++++++++++++ modules/ui/flow.onyx | 8 ++ modules/ui/module.onyx | 1 + modules/ui/ui.onyx | 12 +-- 8 files changed, 155 insertions(+), 24 deletions(-) create mode 100644 modules/ui/components/checkbox.onyx diff --git a/modules/bmfont/bmfont_loader.onyx b/modules/bmfont/bmfont_loader.onyx index 014144df..83729224 100644 --- a/modules/bmfont/bmfont_loader.onyx +++ b/modules/bmfont/bmfont_loader.onyx @@ -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; } diff --git a/modules/bmfont/types.onyx b/modules/bmfont/types.onyx index 03456c4b..fa97bc59 100644 --- a/modules/bmfont/types.onyx +++ b/modules/bmfont/types.onyx @@ -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); } diff --git a/modules/bmfont/utils.onyx b/modules/bmfont/utils.onyx index c4702976..87f160a6 100644 --- a/modules/bmfont/utils.onyx +++ b/modules/bmfont/utils.onyx @@ -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 diff --git a/modules/ui/components/button.onyx b/modules/ui/components/button.onyx index f5e3d947..c165a99c 100644 --- a/modules/ui/components/button.onyx +++ b/modules/ui/components/button.onyx @@ -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 index 00000000..5cda0fdf --- /dev/null +++ b/modules/ui/components/checkbox.onyx @@ -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 diff --git a/modules/ui/flow.onyx b/modules/ui/flow.onyx index 3c389455..3079298a 100644 --- a/modules/ui/flow.onyx +++ b/modules/ui/flow.onyx @@ -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 diff --git a/modules/ui/module.onyx b/modules/ui/module.onyx index 82cf6c92..1586000a 100644 --- a/modules/ui/module.onyx +++ b/modules/ui/module.onyx @@ -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. diff --git a/modules/ui/ui.onyx b/modules/ui/ui.onyx index 16767989..7afcf88c 100644 --- a/modules/ui/ui.onyx +++ b/modules/ui/ui.onyx @@ -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 -- 2.25.1