From 469c0c8ec3abbe939f83786268313a927e118666 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Sat, 18 Jan 2025 12:02:24 +0100 Subject: [PATCH] reloading font dynamically --- src/core/core_math.c | 13 + src/render/font.c | 39 +-- src/render/render_opengl.c | 89 ++++--- src/wasm_app/_ui.c | 517 ------------------------------------ src/wasm_app/_ui.h | 157 ----------- src/wasm_app/main.c | 12 +- src/wasm_app/ui.c | 14 +- src/wasm_app/ui.h | 2 +- src/wasm_app/wasm_app.gen.c | 8 +- 9 files changed, 101 insertions(+), 750 deletions(-) delete mode 100644 src/wasm_app/_ui.c delete mode 100644 src/wasm_app/_ui.h diff --git a/src/core/core_math.c b/src/core/core_math.c index 026cab0..94ee04b 100644 --- a/src/core/core_math.c +++ b/src/core/core_math.c @@ -496,3 +496,16 @@ f32 f32_atan2(f32 y, f32 x) { else if (y >= 0) return F32_PI + f32_atan(y / x); else return -F32_PI + f32_atan(y / x); } + + +b32 f32_are_equal(f32 a, f32 b) { + f32 diff = f32_abs(a - b); + b32 result = diff <= 0.00001f; + return result; +} + +b32 f64_are_equal(f64 a, f64 b) { + f64 diff = f64_abs(a - b); + b32 result = diff <= 0.00001; + return result; +} diff --git a/src/render/font.c b/src/render/font.c index 97248fd..4b44880 100644 --- a/src/render/font.c +++ b/src/render/font.c @@ -97,31 +97,33 @@ r2f32_t rn_pack_bitmap(rn_atlas_t *atlas, u8 *bitmap, i32 width, i32 height) { return result; } -rn_font_t rn_create_font(ma_arena_t *glyph_arena, s8_t font_data, rn_atlas_t *atlas, i32 size) { - rn_font_t result = {}; +rn_font_t *rn_create_font(ma_arena_t *arena) { + rn_font_t *font = ma_push_type(arena, rn_font_t); + font->first_char = ' '; + font->last_char = '~'; + i32 glyph_count = font->last_char - font->first_char + 1; // + 1 because it's '<=' not '<' + font->glyphs = ma_push_array(arena, rn_glyph_t, glyph_count); + return font; +} + +b32 rn_reload_font_atlas(rn_font_t *font, s8_t font_data, rn_atlas_t *atlas, i32 size) { + font->glyph_count = 0; stbtt_fontinfo stb_font; i32 success = stbtt_InitFont(&stb_font, (const unsigned char *)font_data.str, 0); - if (!success) { - return result; - } + if (!success) return false; f32 scale = stbtt_ScaleForPixelHeight(&stb_font, (f32)size); // f32 em_scale = stbtt_ScaleForMappingEmToPixels(&stb_font, (f32)size); i32 ascent, descent, line_gap; stbtt_GetFontVMetrics(&stb_font, &ascent, &descent, &line_gap); - result.ascent = (f32)ascent * scale; - result.descent = (f32)descent * scale; - result.line_gap = (f32)line_gap * scale; - result.size = (f32)size; - result.first_char = ' '; - result.last_char = '~'; - result.white_texture_bounding_box = atlas->white_texture_bounding_box; + font->ascent = (f32)ascent * scale; + font->descent = (f32)descent * scale; + font->line_gap = (f32)line_gap * scale; + font->size = (f32)size; + font->white_texture_bounding_box = atlas->white_texture_bounding_box; - i32 glyph_count = result.last_char - result.first_char + 1; // + 1 because it's '<=' not '<' - result.glyphs = ma_push_array(glyph_arena, rn_glyph_t, glyph_count); - - for (u32 ascii_symbol = result.first_char; ascii_symbol <= result.last_char; ascii_symbol++) { + for (u32 ascii_symbol = font->first_char; ascii_symbol <= font->last_char; ascii_symbol++) { i32 width, height, xoff, yoff; u8 *bitmap = (u8 *)stbtt_GetCodepointBitmap(&stb_font, 0, scale, ascii_symbol, &width, &height, &xoff, &yoff); @@ -135,13 +137,12 @@ rn_font_t rn_create_font(ma_arena_t *glyph_arena, s8_t font_data, rn_atlas_t *at glyph.xadvance = (f32)xadvance * scale; glyph.left_side_bearing = (f32)left_side_bearing * scale; - assert(result.glyph_count + 1 <= glyph_count); - result.glyphs[result.glyph_count++] = glyph; + font->glyphs[font->glyph_count++] = glyph; stbtt_FreeBitmap(bitmap, 0); } - return result; + return true; } rn_glyph_t *rn_get_glyph(rn_font_t *font, u32 codepoint) { diff --git a/src/render/render_opengl.c b/src/render/render_opengl.c index 702e12f..20476c0 100644 --- a/src/render/render_opengl.c +++ b/src/render/render_opengl.c @@ -31,7 +31,7 @@ struct rn_cmd_t { typedef struct rn_state_t rn_state_t; struct rn_state_t { - rn_font_t main_font; + rn_font_t *main_font; rn_shader_t shader2d; rn_cmd_t *first_cmd; @@ -43,9 +43,9 @@ struct rn_state_t { u32 vao; u32 vbo; }; -rn_state_t rn_state; +gb rn_state_t rn_state; -rn_shader_t rn_create_shader(char *glsl_vshader, char *glsl_fshader) { +fn rn_shader_t rn_create_shader(char *glsl_vshader, char *glsl_fshader) { rn_shader_t result = {}; result.vshader = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &glsl_vshader); result.fshader = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &glsl_fshader); @@ -71,7 +71,7 @@ rn_shader_t rn_create_shader(char *glsl_vshader, char *glsl_fshader) { return result; } -void gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *user) { +fn void gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *user) { if (severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM) { fatalf("opengl message: %s", message); } else { @@ -79,7 +79,7 @@ void gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, G } } -rn_cmd_t *rn_get_cmd(rn_cmd_kind_t kind) { +fn rn_cmd_t *rn_get_cmd(rn_cmd_kind_t kind) { b32 alloc_new = false; if (rn_state.last_cmd == NULL) { alloc_new = true; @@ -101,14 +101,14 @@ rn_cmd_t *rn_get_cmd(rn_cmd_kind_t kind) { return result; } -rn_vertex_t *rn_push_vertex(rn_cmd_t *cmd, u32 count) { +fn rn_vertex_t *rn_push_vertex(rn_cmd_t *cmd, u32 count) { rn_vertex_t *result = cmd->vertex + cmd->len; rn_state.len += count; cmd->len += count; return result; } -void rn_push_quad(r2f32_t rect, r2f32_t tex, v4f32_t color) { +fn void rn_push_quad(r2f32_t rect, r2f32_t tex, v4f32_t color) { rn_cmd_t *cmd = rn_get_cmd(rn_cmd_kind_quad); rn_vertex_t *v = rn_push_vertex(cmd, 6); v[0] = (rn_vertex_t){ @@ -143,33 +143,33 @@ void rn_push_quad(r2f32_t rect, r2f32_t tex, v4f32_t color) { }; } -void rn_draw_rect(r2f32_t rect, v4f32_t color) { - rn_push_quad(rect, rn_state.main_font.white_texture_bounding_box, color); +fn void rn_draw_rect(r2f32_t rect, v4f32_t color) { + rn_push_quad(rect, rn_state.main_font->white_texture_bounding_box, color); } -void rn_draw_rect_border(r2f32_t rect, v4f32_t color) { +fn void rn_draw_rect_border(r2f32_t rect, v4f32_t color) { r2f32_t left = r2f32_cut_left(&rect, 1); r2f32_t right = r2f32_cut_right(&rect, 1); r2f32_t top = r2f32_cut_top(&rect, 1); r2f32_t bottom = r2f32_cut_bottom(&rect, 1); - rn_push_quad(left, rn_state.main_font.white_texture_bounding_box, color); - rn_push_quad(right, rn_state.main_font.white_texture_bounding_box, color); - rn_push_quad(top, rn_state.main_font.white_texture_bounding_box, color); - rn_push_quad(bottom, rn_state.main_font.white_texture_bounding_box, color); + rn_push_quad(left, rn_state.main_font->white_texture_bounding_box, color); + rn_push_quad(right, rn_state.main_font->white_texture_bounding_box, color); + rn_push_quad(top, rn_state.main_font->white_texture_bounding_box, color); + rn_push_quad(bottom, rn_state.main_font->white_texture_bounding_box, color); } -i64 rn_get_char_spacing(rn_font_t *font, u32 codepoint) { +fn i64 rn_get_char_spacing(rn_font_t *font, u32 codepoint) { rn_glyph_t *g = rn_get_glyph(font, codepoint); if (g->xadvance) return (i64)g->xadvance; return (i64)g->size.x; } -i64 rn_get_line_spacing(rn_font_t *font) { +fn i64 rn_get_line_spacing(rn_font_t *font) { i64 result = (i64)(font->ascent - font->descent + font->line_gap); return result; } -v2f32_t rn_base_draw_string(rn_font_t *font, s8_t string, v2f32_t pos, v4f32_t color, b32 draw) { +fn v2f32_t rn_base_draw_string(rn_font_t *font, s8_t string, v2f32_t pos, v4f32_t color, b32 draw) { pos.y += rn_get_line_spacing(font) + font->descent; v2f32_t original_pos = pos; @@ -188,48 +188,51 @@ v2f32_t rn_base_draw_string(rn_font_t *font, s8_t string, v2f32_t pos, v4f32_t c return result; } -v2f32_t rn_draw_string(rn_font_t *font, v2f32_t pos, v4f32_t color, s8_t string) { +fn v2f32_t rn_draw_string(rn_font_t *font, v2f32_t pos, v4f32_t color, s8_t string) { return rn_base_draw_string(font, string, pos, color, true); } -v2f32_t rn_draw_stringf(rn_font_t *font, v2f32_t pos, v4f32_t color, char *str, ...) { +fn v2f32_t rn_draw_stringf(rn_font_t *font, v2f32_t pos, v4f32_t color, char *str, ...) { S8_FMT(tcx.temp, str, result); return rn_draw_string(font, pos, color, result); } -v2f32_t rn_measure_string(rn_font_t *font, s8_t string) { +fn v2f32_t rn_measure_string(rn_font_t *font, s8_t string) { return rn_base_draw_string(font, string, v2f32(0,0), v4f32(0,0,0,0), false); } -void rn_set_clip(r2f32_t rect) { +fn void rn_set_clip(r2f32_t rect) { rn_cmd_t *cmd = rn_get_cmd(rn_cmd_kind_set_clip); cmd->rect = rect; } -void rn_init(ma_arena_t *perm, s8_t font_data, f32 _font_size) { +fn void rn_reload_font(s8_t font_data, f32 font_size) { + if (rn_state.main_font->texture_id) { + glDeleteTextures(1, &rn_state.main_font->texture_id); + } + + ma_temp_t scratch = ma_begin_scratch(); + rn_atlas_t *atlas = rn_create_atlas(scratch.arena, (v2i32_t){2048, 2048}); + rn_reload_font_atlas(rn_state.main_font, font_data, atlas, (i32)font_size); + + GLint filter = GL_NEAREST; + glCreateTextures(GL_TEXTURE_2D, 1, &atlas->texture_id); + glTextureParameteri(atlas->texture_id, GL_TEXTURE_MIN_FILTER, filter); + glTextureParameteri(atlas->texture_id, GL_TEXTURE_MAG_FILTER, filter); + glTextureParameteri(atlas->texture_id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTextureParameteri(atlas->texture_id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTextureStorage2D(atlas->texture_id, 1, GL_R8, (GLsizei)atlas->size.x, (GLsizei)atlas->size.y); + glTextureSubImage2D(atlas->texture_id, 0, 0, 0, (GLsizei)atlas->size.x, (GLsizei)atlas->size.y, GL_RED, GL_UNSIGNED_BYTE, atlas->bitmap); + rn_state.main_font->texture_id = atlas->texture_id; + ma_end_scratch(scratch); +} + +fn void rn_init(ma_arena_t *perm, s8_t font_data, f32 font_size) { rn_state.cap = 1024*256; rn_state.vertices = ma_push_array(perm, rn_vertex_t, rn_state.cap); - { - ma_temp_t scratch = ma_begin_scratch1(perm); - // s8_t font_data = ;//rn_get_default_font(tcx.temp); - rn_atlas_t *atlas = rn_create_atlas(scratch.arena, (v2i32_t){2048, 2048}); - - u32 font_size = (u32)_font_size; - rn_state.main_font = rn_create_font(perm, font_data, atlas, font_size); - - GLint filter = GL_NEAREST; - // filter = GL_LINEAR; - glCreateTextures(GL_TEXTURE_2D, 1, &atlas->texture_id); - glTextureParameteri(atlas->texture_id, GL_TEXTURE_MIN_FILTER, filter); - glTextureParameteri(atlas->texture_id, GL_TEXTURE_MAG_FILTER, filter); - glTextureParameteri(atlas->texture_id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTextureParameteri(atlas->texture_id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTextureStorage2D(atlas->texture_id, 1, GL_R8, (GLsizei)atlas->size.x, (GLsizei)atlas->size.y); - glTextureSubImage2D(atlas->texture_id, 0, 0, 0, (GLsizei)atlas->size.x, (GLsizei)atlas->size.y, GL_RED, GL_UNSIGNED_BYTE, atlas->bitmap); - rn_state.main_font.texture_id = atlas->texture_id; - ma_end_scratch(scratch); - } + rn_state.main_font = rn_create_font(perm); + rn_reload_font(font_data, font_size); glDebugMessageCallback(&gl_debug_callback, NULL); @@ -326,7 +329,7 @@ void rn_end(v2f32_t window_size, v4f32_t color) { glNamedBufferSubData(rn_state.vbo, 0, it->len * sizeof(rn_vertex_t), it->vertex); glBindVertexArray(rn_state.vao); GLint s_texture = 0; // texture unit that sampler2D will use in GLSL code - glBindTextureUnit(s_texture, rn_state.main_font.texture_id); + glBindTextureUnit(s_texture, rn_state.main_font->texture_id); glDrawArrays(GL_TRIANGLES, 0, it->len); } else if (it->kind == rn_cmd_kind_set_clip) { GLint x = (GLint)it->rect.min.x; diff --git a/src/wasm_app/_ui.c b/src/wasm_app/_ui.c deleted file mode 100644 index 8253c62..0000000 --- a/src/wasm_app/_ui.c +++ /dev/null @@ -1,517 +0,0 @@ -#if 0 -fn s8_t ui_get_display_string(s8_t string) { - s8_t result = string; - if (s8_seek(result, s8_lit("##"), s8_seek_none, &result.len)) { - int a = 10; - } - return result; -} - -fn s8_t ui_get_hash_string(s8_t string) { - i64 len = 0; - if (s8_seek(string, s8_lit("##"), s8_seek_none, &len)) { - string = s8_skip(string, len + 2); - } - return string; -} - -fn ui_id_t ui_find_valid_id(ui_box_t *box) { - for (ui_box_t *it = box; it; it = it->parent) { - if (!ui_is_null_box(it)) return it->id; - } - return ui_null_id; -} - -fn ui_id_t ui_find_top_id(void) { - ui_id_t parent_id = ui_find_valid_id(ui->top); - if (ui_is_null_id(parent_id)) parent_id.value = 1423; - return parent_id; -} - -fn ui_box_t *ui_find_box(ui_id_t id) { - if (id.value == 0) return NULL; - for (ui_box_t *it = ui->hash_first; it; it = it->hash_next) { - if (it->id.value == id.value) { - return it; - } - } - return NULL; -} - -fn ui_box_t *ui_alloc_box(void) { - ui_box_t *box = NULL; - if (ui->free_first) { - SLLS_POP_AND_STORE(ui->free_first, box); - } else { - box = ma_push_type(ui->box_arena, ui_box_t); - ui->allocated_boxes += 1; - } - return box; -} - -fn void ui_push(ui_box_t *parent, ui_box_t *box) { - box->parent = parent; - DLLQ_APPEND(parent->first, parent->last, box); - parent->node_count += 1; -} - -fn void ui_set_semantic_size(ui_box_t *box, ui_size_t x, ui_size_t y) { - box->semantic_size[0] = x; - box->semantic_size[1] = y; -} - -fn s8_t ui_tprint_loc(ui_code_loc_t loc) { - return s8_printf(tcx.temp, "%s(%d)", loc.file, loc.line); -} - -fn ui_box_t *ui_build_box_from_id(ui_code_loc_t loc, ui_box_flags_t flags, ui_id_t id) { - ui_box_t *box = ui_find_box(id); - if (box) { - expect (box->last_touched_event_id != ui->event->id) { - fatalf("likely ui id collision between: %S and %S", ui_tprint_loc(loc), ui_tprint_loc(box->loc)); - } - memory_zero(box, offsetof(ui_box_t, id)); - } else { - box = ui_alloc_box(); - box->created_new = true; - DLLQ_APPEND_EX(ui->hash_first, ui->hash_last, box, hash_next, hash_prev); - } - box->loc = loc; - box->last_touched_event_id = ui->event->id; - box->id = id; - box->flags = flags; - ui_push(ui->top, box); - return box; -} - -fn ui_id_t ui_gen_id(ui_id_flags_t flags, ui_code_loc_t loc, s8_t string) { - u64 result = 42523423493; - if (flags.use_string) { - u64 string_hash = hash_data(string); - result = hash_mix(string_hash, result); - } - if (flags.use_hierarchy) { - ui_id_t top_id = ui_find_top_id(); - result = hash_mix(top_id.value, result); - } - if (flags.use_code_loc) { - u64 file_hash = hash_data(s8_from_char(loc.file)); - u64 line_hash = hash_data(s8_struct(loc.line)); - u64 cont_hash = hash_data(s8_struct(loc.counter)); - result = hash_mix(result, file_hash); - result = hash_mix(result, line_hash); - result = hash_mix(result, cont_hash); - } - ui_id_t id = {result}; - return id; -} - -fn ui_box_t *ui_build_box_from_string(ui_code_loc_t loc, ui_box_flags_t flags, s8_t string) { - ui_id_t id = ui_gen_id(ui->id_flags, loc, ui_get_hash_string(string)); - ui_box_t *box = ui_build_box_from_id(loc, flags, id); - box->string = ui_get_display_string(string); - return box; -} - -fn ui_signal_t ui_signal_from_box(ui_box_t *box) { - ui_signal_t result = {box}; - app_event_t *ev = ui->event; - - b32 move = ev->kind == app_event_kind_mouse_move; - b32 inside = r2f32_contains(box->rect, ev->mouse_pos); - - if (ui_is_active_box(box)) { - result.dragging = true; - if (ev_left_up(ev)) { - if (ui_is_hot_box(box)) { - result.clicked = true; - } else { - ui->active.value = 0; - } - } - } else if (ui_is_hot_box(box) && ev_left_down(ev)) { - ui->active = box->id; - } - - if (inside) { - ui->hot.value = box->id.value; - } else if (!inside && ui_is_hot_box(box)) { - ui->hot = ui_null_id; - } - - return result; -} - -fn void ui_init(ma_arena_t *arena) { - assert(arena != tcx.temp); - - ui = ma_push_type(arena, ui_t); - ui->box_arena = arena; - ui->root = ma_push_type(arena, ui_box_t); - ui->id_flags = (ui_id_flags_t){.use_string = true, .use_code_loc = true}; - - ui->box_array = ma_push_type(arena, ui_box_t); - SLLS_PUSH(ui->free_first, ui->box_array); -} - -fn void ui_begin_build(ui_code_loc_t loc, app_event_t *event) { - ui->event = event; - - for (ui_box_t *box = ui->hash_first, *next = NULL; box; box = next) { - next = box->hash_next; - if (box->id.value == 0) { - DLLQ_REMOVE_EX(ui->hash_first, ui->hash_last, box, hash_next, hash_prev); - zero_struct(box); - SLLS_PUSH(ui->free_first, box); - } - } - - zero_struct(ui->root); - ui->top = ui->root; - ui->root->loc = UI_CODE_LOC; -} - -fn void ui_end_build(void) { - assert(ui->top == ui->root); - - for (ui_box_t *box = ui->hash_first, *next = NULL; box; box = next) { - next = box->hash_next; - b32 touched_box_during_update = ui->event->id <= box->last_touched_event_id; - if (!touched_box_during_update) { - DLLQ_REMOVE_EX(ui->hash_first, ui->hash_last, box, hash_next, hash_prev); - zero_struct(box); - SLLS_PUSH(ui->free_first, box); - } - } -} - -fn void ui_begin_frame(app_frame_t *frame) { - ui->frame = frame; -} - -fn void ui_push_parent(ui_box_t *box) { - ui->top = box; -} - -fn ui_box_t *ui_pop_parent(void) { - ui_box_t *top = ui->top; - ui->top = ui->top->parent; - return top; -} - -fn ui_box_t *ui_spacer(ui_code_loc_t loc, ui_size_t x, ui_size_t y) { - ui_box_t *box = ui_build_box_from_id(loc, (ui_box_flags_t){.draw_rect = true, .draw_border = true}, ui_null_id); - ui_set_semantic_size(box, x, y); - return box; -} - -fn ui_signal_t ui_scroller_button(ui_code_loc_t loc, ui_size_t x, ui_size_t y) { - ui_id_t id = ui_gen_id(ui->id_flags, loc, s8_lit("spacer")); - ui_box_t *box = ui_build_box_from_id(loc, (ui_box_flags_t){.draw_rect = true, .draw_border = true}, id); - ui_set_semantic_size(box, x, y); - ui_signal_t signal = ui_signal_from_box(box); - return signal; -} - -fn ui_box_t *ui_push_container(ui_code_loc_t loc, ui_size_t x, ui_size_t y) { - ui_box_t *box = ui_build_box_from_id(loc, (ui_box_flags_t){.draw_rect = true}, ui_null_id); - ui_push_parent(box); - ui_set_semantic_size(box, x, y); - box->grow_axis = ui_axis2_y; - return box; -} - -fn ui_box_t *ui_push_xcontainer(ui_code_loc_t loc, ui_size_t x, ui_size_t y) { - ui_box_t *box = ui_push_container(loc, x, y); - box->grow_axis = ui_axis2_x; - return box; -} - -fn ui_box_t *ui_push_list_container(ui_code_loc_t loc, ui_size_t size) { - return ui_push_container(loc, size, ui_children_sum()); -} - -fn void ui_set_indented_string(ui_box_t *box, s8_t string) { box->string = s8_printf(tcx.temp, "%.*s%S", ui->indent_stack, " ", string); } - -fn ui_signal_t ui_push_exp(ui_code_loc_t loc, char *str, ...) { - S8_FMT(tcx.temp, str, string); - ui_box_t *box = ui_build_box_from_string(loc, (ui_box_flags_t){.draw_text = true}, string); - ui_set_semantic_size(box, ui_percent(1), ui_text()); - - if (box->created_new) box->expanded = true; - - ui_signal_t signal = ui_signal_from_box(box); - if (signal.clicked) box->expanded = !box->expanded; - signal.clicked = box->expanded; - - if (signal.clicked) { - box->string = s8_printf(tcx.temp, "* %S", box->string); // ▼ - } else { - box->string = s8_printf(tcx.temp, "> %S", box->string); // ► - } - ui_set_indented_string(box, box->string); - - - if (signal.clicked) ui->indent_stack += 1; - return signal; -} - -fn void ui_pop_exp(void) { - ui->indent_stack -= 1; -} - -fn ui_box_t *ui_label(ui_code_loc_t loc, char *fmt, ...) { - S8_FMT(tcx.temp, fmt, string); - ui_box_t *box = ui_build_box_from_id(loc, (ui_box_flags_t){.draw_text = true}, ui_null_id); - ui_set_indented_string(box, string); - ui_set_semantic_size(box, ui_text(), ui_text()); - return box; -} - -fn void ui_end_frame(void) { - for (app_event_t *ev = ui->frame->first_event; ev; ev = ev->next) { - if (ev_left_up(ev)) { - ui->active = ui_null_id; - } - } -} - -fn void ui_layout(void) { - rn_font_t *font = &rn_state.main_font; - - // compute standalone sizes: (pixels, text_content) - for (ui_preorder_iter_t it = ui_iterate_preorder(ui->root); ui_preorder_iter_is_valid(it); ui_iter_advance_preorder(&it)) { - ui_box_t *box = it.box; - if (box == ui->root) continue; // @todo: remove somehow - for (ui_axis2_t axis = 0; axis < ui_axis2_count; axis += 1) { - ui_size_t sem = box->semantic_size[axis]; - assert(sem.kind != ui_size_kind_null); - if (sem.kind == ui_size_kind_pixels) { - box->computed_size[axis] = sem.value; - } else if (sem.kind == ui_size_kind_text_content) { - box->computed_size[axis] = rn_measure_string(font, box->string).e[axis]; - } - } - } - - // compute: (percent_of_parent) - for (ui_preorder_iter_t it = ui_iterate_preorder(ui->root); ui_preorder_iter_is_valid(it); ui_iter_advance_preorder(&it)) { - ui_box_t *box = it.box; - ui_box_t *parent = box->parent; // @todo: I'm not sure why Ryan uses a loop to find a parent with fixed size_kind - for (ui_axis2_t axis = 0; axis < ui_axis2_count; axis += 1) { - ui_size_t sem = box->semantic_size[axis]; - if (sem.kind == ui_size_kind_percent_of_parent) { - assert(parent->semantic_size[axis].kind == ui_size_kind_pixels || - parent->semantic_size[axis].kind == ui_size_kind_text_content || - parent->semantic_size[axis].kind == ui_size_kind_percent_of_parent); - box->computed_size[axis] = (sem.value) * parent->computed_size[axis]; - } - } - } - - // compute: (children_sum) - for (ui_postorder_iter_t it = ui_iterate_postorder(ui->root); ui_postorder_iter_is_valid(it); ui_iter_advance_postorder(&it)) { - ui_box_t *box = it.box; - for (ui_axis2_t axis = 0; axis < ui_axis2_count; axis += 1) { - ui_size_t sem = box->semantic_size[axis]; - if (sem.kind != ui_size_kind_children_sum) continue; - - for (ui_box_t *child = box->first; child; child = child->next) { - assert(child->computed_size[axis] != 0.f); - if (box->grow_axis == axis) { - box->computed_size[axis] += child->computed_size[axis]; - } else { - box->computed_size[axis] = MAX(box->computed_size[axis], child->computed_size[axis]); - } - } - } - } - - // solve violations - - // compute relative positions - for (ui_preorder_iter_t it = ui_iterate_preorder(ui->root); ui_preorder_iter_is_valid(it); ui_iter_advance_preorder(&it)) { - ui_box_t *box = it.box; - ui_box_t *parent = box->parent; - if (ui->root == box) continue; // @todo: how to remove this? - - for (ui_axis2_t axis = 0; axis < ui_axis2_count; axis += 1) { - f32 *pos = &box->computed_rel_pos[axis]; - f32 size = box->computed_size[axis]; - - f32 parent_pos = parent->computed_rel_pos[axis]; - f32 *iter_pos = &parent->iter_pos[axis]; - - *pos = parent_pos + *iter_pos; - if (parent->flags.scroll) { - *pos -= parent->view_offset.e[axis]; - } - - if (parent->grow_axis == axis) *iter_pos += size; - } - - v2f32_t pos = v2f32(box->computed_rel_pos[0], box->computed_rel_pos[1]); - v2f32_t size = v2f32(box->computed_size[0], box->computed_size[1]); - box->rect = r2f32_mindim(pos, size); - } -} - -fn void ui_draw(void) { - rn_font_t *font = &rn_state.main_font; - ui_test_iterator(); - ui_layout(); - - // actually draw - for (ui_preorder_iter_t it = ui_iterate_preorder(ui->root); ui_preorder_iter_is_valid(it); ui_iter_advance_preorder(&it)) { - ui_box_t *box = it.box; - r2f32_t clip_rect = box->rect; - if (box->parent) { - clip_rect = r2f32_intersect(clip_rect, box->parent->rect); - } - - v4f32_t rect_color = primary_color_global; - v4f32_t text_color = black_color_global; - if (ui_is_hot_box(box)) { - rect_color = secondary_color_global; - text_color = accent2_color_global; - } - if (ui_is_active_box(box)) { - rect_color = accent1_color_global; - text_color = accent1_color_global; - } - - rn_set_clip(clip_rect); - if (box->flags.draw_rect) { - rn_draw_rect(box->rect, rect_color); - } - if (box->flags.draw_border) { - rn_draw_rect_border(box->rect, accent2_color_global); - } - if (box->flags.draw_text) { - rn_draw_string(font, box->rect.min, text_color, box->string); - } - } -} - -void ui_demo(app_frame_t *frame) { - assert(frame != NULL); - ui_begin_frame(frame); - - assert(frame->first_event); - - for (app_event_t *ev = frame->first_event; ev; ev = ev->next) { - static f32 scroller_value; - defer_block(ui_begin_build(UI_CODE_LOC, ev), ui_end_build()) { - defer_block(ui_push_xcontainer(UI_CODE_LOC, ui_em(25), ui_em(30)), ui_pop_parent()) { - - - ui_box_t *item_box = ui_build_box_from_string(UI_CODE_LOC, (ui_box_flags_t){.scroll = true, .draw_rect = true}, s8_lit("scrolled item_box")); - ui_set_semantic_size(item_box, ui_percent(0.97f), ui_percent(1)); - item_box->grow_axis = ui_axis2_y; - defer_block(ui_push_parent(item_box), ui_pop_parent()) { - defer_if (ui_push_exp(UI_CODE_LOC, "app_event_t").clicked, ui_pop_exp()) { - defer_if (ui_push_exp(UI_CODE_LOC, "mouse_wheel_delta: v3f64_t").clicked, ui_pop_exp()) { - ui_label(UI_CODE_LOC, "x: f64 = value"); - ui_label(UI_CODE_LOC, "y: f64 = value"); - ui_label(UI_CODE_LOC, "z: f64 = value"); - } - ui_label(UI_CODE_LOC, "kind: app_event_kind_t = value"); - ui_label(UI_CODE_LOC, "ctrl: b8 = value"); - ui_label(UI_CODE_LOC, "shift: b8 = value"); - defer_if (ui_push_exp(UI_CODE_LOC, "pos: v2f64_t").clicked, ui_pop_exp()) { - defer_if (ui_push_exp(UI_CODE_LOC, "inner_pos: v2f64_t##asd").clicked, ui_pop_exp()) { - ui_label(UI_CODE_LOC, "x: f64 = value"); - ui_label(UI_CODE_LOC, "y: f64 = value"); - } - defer_if (ui_push_exp(UI_CODE_LOC, "inner_pos: v2f64_t##qwe").clicked, ui_pop_exp()) { - ui_label(UI_CODE_LOC, "x: f64 = value"); - ui_label(UI_CODE_LOC, "y: f64 = value"); - } - ui_label(UI_CODE_LOC, "y: f64 = value"); - } - ui_label(UI_CODE_LOC, "alt: b8 = value"); - ui_label(UI_CODE_LOC, "meta: b8 = value"); - } - defer_if (ui_push_exp(UI_CODE_LOC, "app_event_t").clicked, ui_pop_exp()) { - defer_if (ui_push_exp(UI_CODE_LOC, "mouse_wheel_delta: v3f64_t").clicked, ui_pop_exp()) { - ui_label(UI_CODE_LOC, "x: f64 = value"); - ui_label(UI_CODE_LOC, "y: f64 = value"); - ui_label(UI_CODE_LOC, "z: f64 = value"); - } - ui_label(UI_CODE_LOC, "kind: app_event_kind_t = value"); - ui_label(UI_CODE_LOC, "ctrl: b8 = value"); - ui_label(UI_CODE_LOC, "shift: b8 = value"); - defer_if (ui_push_exp(UI_CODE_LOC, "pos: v2f64_t").clicked, ui_pop_exp()) { - defer_if (ui_push_exp(UI_CODE_LOC, "inner_pos: v2f64_t##asd").clicked, ui_pop_exp()) { - ui_label(UI_CODE_LOC, "x: f64 = value"); - ui_label(UI_CODE_LOC, "y: f64 = value"); - } - defer_if (ui_push_exp(UI_CODE_LOC, "inner_pos: v2f64_t##qwe").clicked, ui_pop_exp()) { - ui_label(UI_CODE_LOC, "x: f64 = value"); - ui_label(UI_CODE_LOC, "y: f64 = value"); - } - ui_label(UI_CODE_LOC, "y: f64 = value"); - } - ui_label(UI_CODE_LOC, "alt: b8 = value"); - ui_label(UI_CODE_LOC, "meta: b8 = value"); - } - defer_if (ui_push_exp(UI_CODE_LOC, "app_event_t").clicked, ui_pop_exp()) { - defer_if (ui_push_exp(UI_CODE_LOC, "mouse_wheel_delta: v3f64_t").clicked, ui_pop_exp()) { - ui_label(UI_CODE_LOC, "x: f64 = value"); - ui_label(UI_CODE_LOC, "y: f64 = value"); - ui_label(UI_CODE_LOC, "z: f64 = value"); - } - ui_label(UI_CODE_LOC, "kind: app_event_kind_t = value"); - ui_label(UI_CODE_LOC, "ctrl: b8 = value"); - ui_label(UI_CODE_LOC, "shift: b8 = value"); - defer_if (ui_push_exp(UI_CODE_LOC, "pos: v2f64_t").clicked, ui_pop_exp()) { - defer_if (ui_push_exp(UI_CODE_LOC, "inner_pos: v2f64_t##asd").clicked, ui_pop_exp()) { - ui_label(UI_CODE_LOC, "x: f64 = value"); - ui_label(UI_CODE_LOC, "y: f64 = value"); - } - defer_if (ui_push_exp(UI_CODE_LOC, "inner_pos: v2f64_t##qwe").clicked, ui_pop_exp()) { - ui_label(UI_CODE_LOC, "x: f64 = value"); - ui_label(UI_CODE_LOC, "y: f64 = value"); - } - ui_label(UI_CODE_LOC, "y: f64 = value"); - } - ui_label(UI_CODE_LOC, "alt: b8 = value"); - ui_label(UI_CODE_LOC, "meta: b8 = value"); - } - } - - // scroller - { - f32 all_items_size = (f32)item_box->node_count * rn_state.main_font.size; - f32 item_box_size = r2f32_get_size(item_box->rect).y; - - f32 scroller_size = CLAMP(item_box_size / all_items_size, 0, 1.0f); - f32 scrollable_space = (1 - scroller_size); - f32 scroller_norm = scroller_value / (all_items_size); - f32 scroller_percent = scroller_norm * scrollable_space; - f32 scroller_second = scrollable_space - scroller_percent; - - ui_push_container(UI_CODE_LOC, ui_percent(0.03f), ui_percent(1)); - ui_spacer(UI_CODE_LOC, ui_percent(1), ui_percent(scroller_percent)); - ui_signal_t sig = ui_scroller_button(UI_CODE_LOC, ui_percent(1), ui_percent(scroller_size)); - if (sig.dragging) { - scroller_value += (ev->mouse_delta.y / item_box_size * 2) * (all_items_size); - scroller_value = CLAMP(scroller_value, 0, all_items_size); - } - ui_spacer(UI_CODE_LOC, ui_percent(1), ui_percent(scroller_second)); - ui_pop_parent(); - - item_box->view_offset.y = scroller_value; - } - } - } - } - - rn_begin(); - - ui_draw(); - rn_draw_stringf(&rn_state.main_font, v2f32(0,frame->window_size.y - 100), black_color_global, "ui_boxes: %d delta: %f update: %f event_count: %d, delta: %f %f dpr: %f", ui->allocated_boxes, frame->delta, frame->update, frame->event_count, frame->last_event->mouse_delta.x, frame->last_event->mouse_delta.y, frame->dpr); - rn_end(frame->window_size, white_color_global); - - ui_end_frame(); -} \ No newline at end of file diff --git a/src/wasm_app/_ui.h b/src/wasm_app/_ui.h deleted file mode 100644 index 5aa81e7..0000000 --- a/src/wasm_app/_ui.h +++ /dev/null @@ -1,157 +0,0 @@ -#if 0 -/* -** [x] Choosing a keying strategy from user code -** [x] Using parents -** [x] Using file and line -** [ ] Keyboard friendliness -** [x] ui_em size -** [ ] use strictness to 'solve violations' -** [ ] scrolling -** [ ] vieweing type info -** [ ] slider -** [ ] button -** [ ] pernament state -*/ - -typedef struct ui_id_t ui_id_t; -struct ui_id_t { - u64 value; -}; - -typedef struct ui_code_loc_t ui_code_loc_t; -struct ui_code_loc_t { - char *file; - int line; - int counter; -}; -#define UI_CODE_LOC (ui_code_loc_t){.file = __FILE__, .line = __LINE__, .counter = __COUNTER__} - -typedef enum { - ui_axis2_x, - ui_axis2_y, - ui_axis2_count, -} ui_axis2_t; - -typedef enum { - ui_size_kind_null, - ui_size_kind_pixels, - ui_size_kind_text_content, - ui_size_kind_percent_of_parent, - ui_size_kind_children_sum, -} ui_size_kind_t; - -typedef struct ui_size_t ui_size_t; -struct ui_size_t { - ui_size_kind_t kind; - f32 value; - f32 strictness; -}; - -typedef struct ui_box_flags_t ui_box_flags_t; -struct ui_box_flags_t { - b8 draw_border : 1; - b8 draw_text : 1; - b8 draw_rect : 1; - - b8 scroll : 1; -}; - -typedef struct ui_box_t ui_box_t; -struct ui_box_t { - // recreated every frame in building code - ui_box_t *next; - ui_box_t *prev; - ui_box_t *parent; - ui_box_t *first; - ui_box_t *last; - i32 node_count; - - ui_box_flags_t flags; - s8_t string; - ui_size_t semantic_size[ui_axis2_count]; - ui_axis2_t grow_axis; - ui_code_loc_t loc; - b32 created_new; - - // layout - f32 iter_pos[ui_axis2_count]; - - // preserving state - ui_id_t id; // important position!: offset(id) used for partial zeroing - u64 last_touched_event_id; - ui_box_t *hash_next; - ui_box_t *hash_prev; - - // layout - f32 computed_rel_pos[ui_axis2_count]; - f32 computed_size[ui_axis2_count]; - r2f32_t rect; - v2f32_t view_offset; - - // state - b32 expanded; -}; - -typedef struct ui_signal_t ui_signal_t; -struct ui_signal_t { - ui_box_t *box; - b8 clicked; - b8 dragging; - - // b8 double_clicked; - // b8 right_clicked; - // b8 pressed; - // b8 released; - // b8 dragging; - // b8 hovering; -}; - -typedef struct ui_id_flags_t ui_id_flags_t; -struct ui_id_flags_t { - b8 use_hierarchy; - b8 use_code_loc; - b8 use_string; -}; - -typedef struct ui_t ui_t; -struct ui_t { - ma_arena_t *box_arena; // required to be only used for boxes - app_event_t *event; - app_frame_t *frame; - - i32 allocated_boxes; - ui_box_t *box_array; // first item on arena - ui_box_t *root; - ui_box_t *top; - - ui_box_t *free_first; - ui_box_t *hash_first; - ui_box_t *hash_last; - - ui_id_t hot; - ui_id_t active; - - int indent_stack; - - ui_id_flags_t id_flags; -}; - -gb ui_t *ui = NULL; -gb_read_only ui_id_t ui_null_id; -gb_read_only ui_box_t ui_null_box; - -fn b32 ui_is_null_id(ui_id_t id) { return id.value == 0; } -fn b32 ui_is_null_box(ui_box_t *box) { return box->id.value == 0; } -fn b32 ui_is_hot_box(ui_box_t *box) { return !ui_is_null_box(box) && box->id.value == ui->hot.value; } -fn b32 ui_is_active_box(ui_box_t *box) { return !ui_is_null_box(box) && box->id.value == ui->active.value; } - -#define ev_left(ev) ((ev)->mouse_button == app_mouse_button_left) -#define ev_left_up(ev) ((ev)->kind == app_event_kind_mouse_up && ev_left(ev)) -#define ev_left_down(ev) ((ev)->kind == app_event_kind_mouse_down && ev_left(ev)) - -fn ui_size_t ui_size(ui_size_kind_t kind, f32 value) { return (ui_size_t){.kind = kind, .value = value}; } -#define ui_pixels(value) ui_size(ui_size_kind_pixels, value) -#define ui_em(value) ui_size(ui_size_kind_pixels, value * rn_state.main_font.size) -#define ui_text() ui_size(ui_size_kind_text_content, 0) -#define ui_children_sum() ui_size(ui_size_kind_children_sum, 0) -#define ui_percent(value) ui_size(ui_size_kind_percent_of_parent, value) diff --git a/src/wasm_app/main.c b/src/wasm_app/main.c index 45e1733..71d7e60 100644 --- a/src/wasm_app/main.c +++ b/src/wasm_app/main.c @@ -31,13 +31,19 @@ fn void app_init(f32 dpr) { ma_arena_t *perm = &tcx._perm; mt_embed_file(font_liberation_mono, "package/liberation-mono.ttf"); - rn_init(perm, s8_array(font_liberation_mono), 50 * dpr); + mt_tweak_f32(font_size, 50, 4, 200); + mt_tweak_f32(_font_size, 50, 50, 50); + + rn_init(perm, s8_array(font_liberation_mono), font_size * dpr); ui_demo_init(perm); - mt_tweak_b32(memes, true); - mt_tweak_f32(scrollasdasd, 32.0f, 0, 100.f); } fn b32 app_update(app_frame_t *frame) { + if (!f32_are_equal(font_size, _font_size)) { + _font_size = font_size; + rn_reload_font(s8_array(font_liberation_mono), font_size); + debugf("reloading font"); + } ui_demo_update(frame); return true; } diff --git a/src/wasm_app/ui.c b/src/wasm_app/ui.c index c1f9210..ab20865 100644 --- a/src/wasm_app/ui.c +++ b/src/wasm_app/ui.c @@ -130,7 +130,7 @@ fn ui_box_t *ui_build_box_from_string(ui_code_loc_t loc, ui_box_flags_t flags, s ui_id_t id = ui_id(string); ui_box_t *box = ui_build_box_from_id(loc, flags, id); box->string = ui_get_display_string(string); - v2f32_t string_size = rn_measure_string(&rn_state.main_font, box->string); + v2f32_t string_size = rn_measure_string(rn_state.main_font, box->string); string_size.x += ui_em(1); r2f32_t rect = ui_next_rect(ui->top->op, &ui->top->rect, string_size); ui_set_rect(box, rect); @@ -277,7 +277,7 @@ fn void ui__draw_box(app_frame_t *frame, ui_box_t *box) { rn_draw_rect_border(rect, accent2_color_global); } if (box->flags.draw_text) { - v2f32_t string_size = rn_measure_string(&rn_state.main_font, box->string); + v2f32_t string_size = rn_measure_string(rn_state.main_font, box->string); v2f32_t rect_size = r2f32_get_size(rect); v2f32_t rect_string_diff = v2f32_sub(rect_size, string_size); v2f32_t center_pos = v2f32_divs(rect_string_diff, 2); @@ -286,7 +286,7 @@ fn void ui__draw_box(app_frame_t *frame, ui_box_t *box) { pos_in_rect = center_pos; } v2f32_t pos = v2f32_add(pos_in_rect, rect.min); - rn_draw_string(&rn_state.main_font, pos, text_color, box->string); + rn_draw_string(rn_state.main_font, pos, text_color, box->string); } r2f32_t prev_clip_rect = ui->clip_rect; @@ -523,6 +523,8 @@ fn void ui_demo_update(app_frame_t *frame) { defer_block(ui_push_top(item_box), ui_pop_top()) { for (i32 i = 0; i < lengthof(tweak_table); i += 1) { mt_tweak_t *tweak = tweak_table + i; + if (s8_starts_with(tweak->name, s8_lit("_"), false)) continue; + if (tweak->type->kind == type_kind_b32) { b32 *n = (b32 *)tweak->ptr; if (ui_list_button("%S: %s##slider%S", tweak->name, *n ? "true" : "false", tweak->name).clicked) { @@ -533,7 +535,7 @@ fn void ui_demo_update(app_frame_t *frame) { ui_signal_t signal = ui_list_button("%S: %f##slider%S", tweak->name, *n, tweak->name); if (signal.dragging) { f32 size = tweak->max - tweak->min; - v2f32_t string_size = rn_measure_string(&rn_state.main_font, signal.box->string); + v2f32_t string_size = rn_measure_string(rn_state.main_font, signal.box->string); f32 delta = (ui->event->mouse_delta.x / string_size.x) * size; *n = CLAMP(*n + delta, tweak->min, tweak->max); } @@ -561,7 +563,7 @@ fn void ui_demo_update(app_frame_t *frame) { // @todo: fix scroller to match mouse defer_block(ui_push_top(scroller_box), ui_pop_top()) { f32 item_count = (f32)item_box->node_count; - f32 all_items_size = item_count * rn_state.main_font.size; + f32 all_items_size = item_count * rn_state.main_font->size; f32 item_box_size = r2f32_get_size(ui->top->full_rect).y; f32 scroller_box_size = r2f32_get_size(scroller_box->full_rect).y; f32 scroller_size = CLAMP(item_box_size / all_items_size, 0, 1.0f); @@ -596,7 +598,7 @@ fn void ui_demo_update(app_frame_t *frame) { rn_begin(); ui_draw(frame); - rn_draw_stringf(&rn_state.main_font, v2f32(0, frame->window_size.y - rn_state.main_font.size), black_color_global, "boxes: %d", ui->allocated_boxes); + rn_draw_stringf(rn_state.main_font, v2f32(0, frame->window_size.y - rn_state.main_font->size), black_color_global, "boxes: %d", ui->allocated_boxes); rn_end(frame->window_size, white_color_global); diff --git a/src/wasm_app/ui.h b/src/wasm_app/ui.h index ced7ed0..a555896 100644 --- a/src/wasm_app/ui.h +++ b/src/wasm_app/ui.h @@ -111,6 +111,6 @@ fn b32 ui_is_active_box(ui_box_t *box) { return !ui_is_null_box(box) && box->id. fn void ui_set_rect(ui_box_t *box, r2f32_t rect) { box->rect = box->full_rect = rect; } #define UILOC (ui_code_loc_t){.file = __FILE__, .line = __LINE__, .counter = __COUNTER__} -#define ui_em(x) ((x) * rn_state.main_font.size) +#define ui_em(x) ((x) * rn_state.main_font->size) #define ui_max 200000000.f #define ui_box_flags(...) (ui_box_flag_t){__VA_ARGS__} diff --git a/src/wasm_app/wasm_app.gen.c b/src/wasm_app/wasm_app.gen.c index 10edba3..7335349 100644 --- a/src/wasm_app/wasm_app.gen.c +++ b/src/wasm_app/wasm_app.gen.c @@ -1691,10 +1691,10 @@ u8 font_liberation_mono[] = { 255,255,0,3,0,1,0,0,0,14,0,0,0,24,0,32,0,0,0,2,0,1,0,1,2,160,0,1,0,4,0,0,0,2,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,201,137,111,49,0,0,0,0,189,118,137,36, 0,0,0,0,202,159,29,99, }; -gb b32 memes = true; -gb f32 scrollasdasd = 32.0f; +gb f32 font_size = 50; +gb f32 _font_size = 50; mt_tweak_t tweak_table[] = { -{type(b32), s8_const_lit("memes"), &memes, 0, 0}, -{type(f32), s8_const_lit("scrollasdasd"), &scrollasdasd, 0, 100.f}, +{type(f32), s8_const_lit("font_size"), &font_size, 4, 200}, +{type(f32), s8_const_lit("_font_size"), &_font_size, 50, 50}, };