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;
}
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);
}
@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
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);
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);
--- /dev/null
+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
.{ 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
#load "./ui"
#load "./flow"
#load "./components/button"
+#load "./components/checkbox"
// Package inclusions that are part of all files in the "ui" package.
init_font();
map.init(^button_states, default=.{}, hash_count=4);
+ map.init(^checkbox_states, default=.{}, hash_count=4);
}
clear_buttons :: () {
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);
-
+
@Relocate
Text_Theme :: struct {
text_color := gfx.Color4.{ 1, 1, 1 };
}
default_text_theme: Text_Theme = Text_Theme.{};
-
+
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);
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