From 0b6ea60fa7fe13014e4e42a3386484e578120154 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Thu, 23 Jan 2025 19:02:20 +0100 Subject: [PATCH] fn_test and ui_text_input work --- src/app/app_win32.c | 2 +- src/core/core.h | 1 + src/core/core_math.gen.c | 24 +++++ src/core/core_math_gen.py | 15 ++++ src/ui/ui.c | 166 +++++++++++++++++++++++++++++------ src/ui/ui.h | 65 ++++++++++++-- src/wasm_app/main.c | 1 + src/wasm_app/wasm_app.gen.c | 7 +- src/wasm_app/wasm_app.meta.c | 19 +++- 9 files changed, 265 insertions(+), 35 deletions(-) diff --git a/src/app/app_win32.c b/src/app/app_win32.c index ea96036..2fd2afe 100644 --- a/src/app/app_win32.c +++ b/src/app/app_win32.c @@ -81,7 +81,7 @@ fn void w32_push_event(app_frame_t *frame, app_event_t event) { } fn void w32_push_wm_char_event(WPARAM wparam) { - if (wparam <= 32 || wparam == 127) return; + if (wparam < 32 || wparam == 127) return; s8_t string = s8_lit("?"); utf32_result_t encode = utf16_to_utf32((u16 *)&wparam, 1); diff --git a/src/core/core.h b/src/core/core.h index 6266fcd..98bcc3f 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -26,6 +26,7 @@ typedef double f64; #endif #define fn +#define fn_test #define gb #define locl static diff --git a/src/core/core_math.gen.c b/src/core/core_math.gen.c index fe9c26f..b7f3d38 100644 --- a/src/core/core_math.gen.c +++ b/src/core/core_math.gen.c @@ -775,3 +775,27 @@ fn_inline r2i64_t r2i64_add_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i6 fn_inline r2i64_t r2i64_sub_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_sub(a.min, b), v2i64_sub(a.max, b) }; } fn_inline r2i64_t r2i64_mul_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_mul(a.min, b), v2i64_mul(a.max, b) }; } fn_inline r2i64_t r2i64_div_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_div(a.min, b), v2i64_div(a.max, b) }; } +fn_inline f32 r1f32_size(r1f32_t a) { return a.max - a.min; } +fn_inline r1f32_t r1f32(f32 min, f32 max) { return (r1f32_t){min,max}; } +fn_inline r1f32_t r1f32_auto(f32 a, f32 b) { return (r1f32_t){MIN(a,b),MAX(a,b)}; } +fn_inline r1f32_t r1f32s(f32 a) { return (r1f32_t){a,a}; } +fn_inline r1f32_t r1f32_clamp(r1f32_t a, f32 min, f32 max) { return (r1f32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; } +gb_read_only r1f32_t r1f32_null; +fn_inline f64 r1f64_size(r1f64_t a) { return a.max - a.min; } +fn_inline r1f64_t r1f64(f64 min, f64 max) { return (r1f64_t){min,max}; } +fn_inline r1f64_t r1f64_auto(f64 a, f64 b) { return (r1f64_t){MIN(a,b),MAX(a,b)}; } +fn_inline r1f64_t r1f64s(f64 a) { return (r1f64_t){a,a}; } +fn_inline r1f64_t r1f64_clamp(r1f64_t a, f64 min, f64 max) { return (r1f64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; } +gb_read_only r1f64_t r1f64_null; +fn_inline i32 r1i32_size(r1i32_t a) { return a.max - a.min; } +fn_inline r1i32_t r1i32(i32 min, i32 max) { return (r1i32_t){min,max}; } +fn_inline r1i32_t r1i32_auto(i32 a, i32 b) { return (r1i32_t){MIN(a,b),MAX(a,b)}; } +fn_inline r1i32_t r1i32s(i32 a) { return (r1i32_t){a,a}; } +fn_inline r1i32_t r1i32_clamp(r1i32_t a, i32 min, i32 max) { return (r1i32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; } +gb_read_only r1i32_t r1i32_null; +fn_inline i64 r1i64_size(r1i64_t a) { return a.max - a.min; } +fn_inline r1i64_t r1i64(i64 min, i64 max) { return (r1i64_t){min,max}; } +fn_inline r1i64_t r1i64_auto(i64 a, i64 b) { return (r1i64_t){MIN(a,b),MAX(a,b)}; } +fn_inline r1i64_t r1i64s(i64 a) { return (r1i64_t){a,a}; } +fn_inline r1i64_t r1i64_clamp(r1i64_t a, i64 min, i64 max) { return (r1i64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; } +gb_read_only r1i64_t r1i64_null; diff --git a/src/core/core_math_gen.py b/src/core/core_math_gen.py index 46e115e..32b1c9b 100644 --- a/src/core/core_math_gen.py +++ b/src/core/core_math_gen.py @@ -349,3 +349,18 @@ fn_inline r2f64_t r2f64_sub_v2f64(r2f64_t a, v2f64_t b) { return (r2f64_t){ v2f6 s = s.replace("f64", bt) s = s.replace("sub", word_op) print(s) + + +########## +# r1 type stuff +for bt in basic_types: + s = """ +fn_inline i32 r1i32_size(r1i32_t a) { return a.max - a.min; } +fn_inline r1i32_t r1i32(i32 min, i32 max) { return (r1i32_t){min,max}; } +fn_inline r1i32_t r1i32_auto(i32 a, i32 b) { return (r1i32_t){MIN(a,b),MAX(a,b)}; } +fn_inline r1i32_t r1i32s(i32 a) { return (r1i32_t){a,a}; } +fn_inline r1i32_t r1i32_clamp(r1i32_t a, i32 min, i32 max) { return (r1i32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; } +gb_read_only r1i32_t r1i32_null; + """.strip() + s = s.replace("i32", bt) + print(s) diff --git a/src/ui/ui.c b/src/ui/ui.c index 452dac6..5ea118f 100644 --- a/src/ui/ui.c +++ b/src/ui/ui.c @@ -206,38 +206,147 @@ fn ui_signal_t ui_signal_from_box(ui_box_t *box) { } fn void ui_text_input_draw(ui_box_t *box) { + r2f32_t rect = box->final_rect; + v4f32_t background_color = box->background_color; + v4f32_t text_color = box->text_color; v4f32_t border_color = box->border_color; - r2f32_t rect = box->final_rect; // @todo: this or clipped? + if (ui_is_active_box(box)) { + f32 active_t = f32_ease_out_n(box->active_t, 50.f); + active_t = f32_clamp01(active_t); + background_color = v4f32_lerp(background_color, box->bg_active_color, 1.0); + text_color = v4f32_lerp(text_color, box->text_active_color, active_t); + } else if (ui_is_hot_box(box)) { + f32 hot_t = f32_ease_out_n(box->hot_t, 3.f); + hot_t = f32_clamp01(hot_t); + background_color = v4f32_lerp(background_color, box->bg_hot_color, hot_t); + text_color = v4f32_lerp(background_color, box->text_hot_color, hot_t); + } else if (ui_is_focused_box(box)) { + background_color = v4f32_hsla_to_rgba((v4f32_t){0.1f, 0.6f, 0.95f, 1.0f}); + text_color = v4f32_hsla_to_rgba((v4f32_t){0.1f, 0.6f, 0.7f, 1.0f}); + } + rn_draw_rect(rect, background_color); rn_draw_rect_border(rect, border_color, box->border_thickness); - s8_t string = s8(box->ti_buffer, box->ti_buffer_len); - v2f32_t string_size = rn_measure_string(rn_state.main_font, 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); - v2f32_t pos_in_rect = v2f32(0, center_pos.y); - if (box->text_align == ui_text_align_center) { - pos_in_rect = center_pos; + ui_text_input_t *ti = &box->text_input; + s8_t string = s8(ti->str, ti->len); + v2f32_t pos = ui_aligned_text_pos(box->text_align, rect, string); + rn_draw_string(rn_state.main_font, pos, text_color, string); + + ti->caret = ui_caret_clamp(ti->caret, 0, (i32)ti->len); + { + s8_t string_min = s8(ti->str, ti->caret.range.min); + s8_t string_max = s8(ti->str, ti->caret.range.max); + + v2f32_t size_min = rn_measure_string(rn_state.main_font, string_min); + v2f32_t size_max = rn_measure_string(rn_state.main_font, string_max); + r2f32_t selection_rect = r2f32(rect.min.x + size_min.x, rect.min.y, rect.min.x + size_max.x, rect.min.y + ui_em(1)); + rn_draw_rect(selection_rect, v4f32(1,1,1,0.2f)); + + + v2f32_t size_front = ti->caret.ifront == 0 ? size_min : size_max; + r2f32_t caret_rect = r2f32(rect.min.x + size_front.x, rect.min.y, rect.min.x + size_front.x + 1, rect.min.y + ui_em(1)); + rn_draw_rect(caret_rect, text_color); } - v2f32_t pos = v2f32_add(pos_in_rect, rect.min); - rn_draw_string(rn_state.main_font, pos, box->text_color, string); +} + +fn i32 ui_text_replace(ui_text_input_t *ti, r1i32_t range, s8_t string) { + range.min = CLAMP(range.min, 0, (i32)ti->len); + range.max = CLAMP(range.max, range.min, (i32)ti->len); + assert(range.min >= 0 && range.max <= ti->len); + i32 size_to_remove = r1i32_size(range); + i32 size_to_add = (i32)string.len; + i32 change_size = size_to_add - size_to_remove; + if (ti->len + change_size > ti->cap) { + i32 rem = ((i32)ti->len + change_size) - ti->cap; + size_to_add -= rem; + change_size -= rem; + assert(change_size >= 0); + } + + u8 *begin_remove = ti->str + range.min; + u8 *end_remove = begin_remove + size_to_remove; + i32 remain_len = (i32)ti->len - (range.min + size_to_remove); + + u8 *begin_add = begin_remove; + u8 *end_add = begin_add + size_to_add; + memory_move(end_add, end_remove, remain_len); + memory_copy(begin_add, string.str, size_to_add); + ti->len = ti->len + change_size; + return change_size; +} + +fn_test void ui_test_text_replace(void) { + ui_text_input_t ti = {0}; + char buff[4] = {0}; + ti.str = buff; + ti.cap = lengthof(buff); + ui_text_replace(&ti, r1i32(0,0), s8_lit("astfpo")); + assert(s8_are_equal(ti.string, s8_lit("astf"))); + + ui_text_replace(&ti, r1i32(0,4), s8_lit("qwer")); + assert(s8_are_equal(ti.string, s8_lit("qwer"))); + + ui_text_replace(&ti, r1i32(1,2), s8_lit("a")); + assert(s8_are_equal(ti.string, s8_lit("qaer"))); } fn ui_signal_t ui_text_input(ui_code_loc_t loc, char *buffer, i32 buffer_size) { ui_box_t *box = ui_build_box_from_string(loc, (ui_box_flags_t){ .draw_border = true, .draw_rect = true, .draw_text = true }, s8_lit("text_input")); box->custom_draw = ui_text_input_draw; - box->ti_buffer = buffer; - box->ti_buffer_cap = buffer_size; + + ui_text_input_t *ti = &box->text_input; + ti->str = buffer; + ti->cap = buffer_size; ui_signal_t signal = ui_signal_from_box(box); if (ui_is_focused_box(box)) { app_event_t *ev = ui->event; + i32 sel_size = r1i32_size(ti->caret.range); if (ev->kind == app_event_kind_text) { - for (i64 i = 0; i < ev->text.len; i += 1) { - box->ti_buffer[box->ti_buffer_len++] = ev->text.str[i]; + ui_text_replace(ti, ti->caret.range, ev->text); + if (sel_size) { + ti->caret = ui_carets(ti->caret.range.min); + } else { + ti->caret = ui_carets(ti->caret.range.min + 1); } + } else if (ev->kind == app_event_kind_key_down) { + if (ev->key == app_key_backspace) { + if (sel_size) { + ui_text_replace(ti, ti->caret.range, s8_lit("")); + } else { + ui_text_replace(ti, r1i32(ti->caret.e[0] - 1, ti->caret.e[0]), s8_null); + ti->caret = ui_carets(ti->caret.range.min - 1); + } + } else if (ev->key == app_key_delete) { + if (sel_size) { + ui_text_replace(ti, ti->caret.range, s8_lit("")); + } else { + ui_text_replace(ti, r1i32(ti->caret.e[0], ti->caret.e[0] + 1), s8_null); + } + } else if (ev->key == app_key_left) { + if (ev->shift) { + ti->caret = ui_caret_set_front(ti->caret, ui_caret_front(ti->caret) - 1); + } else { + ti->caret = ui_carets(ui_caret_front(ti->caret) - 1); + } + } else if (ev->key == app_key_right) { + if (ev->shift) { + ti->caret = ui_caret_set_front(ti->caret, ui_caret_front(ti->caret) + 1); + } else { + ti->caret = ui_carets(ui_caret_front(ti->caret) + 1); + } + } + } else if (signal.dragging) { + v2f32_t size = rn_measure_string(rn_state.main_font, s8_lit("_")); + v2f32_t pos = v2f32_sub(ev->mouse_pos, box->final_rect.min); + i32 p = (i32)f32_round(pos.x / size.x); + if (ev->kind == app_event_kind_mouse_down && ev->mouse_button == app_mouse_button_left) { + ti->caret = ui_carets(p); + } + ti->caret = ui_caret_set_front(ti->caret, p); } + ti->caret = ui_caret_clamp(ti->caret, 0, (i32)ti->len); } return signal; } @@ -336,12 +445,25 @@ fn void ui_end_build(void) { } } +fn v2f32_t ui_aligned_text_pos(ui_text_align_t text_align, r2f32_t rect, s8_t string) { + v2f32_t string_size = rn_measure_string(rn_state.main_font, 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); + v2f32_t pos_in_rect = v2f32(0, center_pos.y); + if (text_align == ui_text_align_center) { + pos_in_rect = center_pos; + } + v2f32_t pos = v2f32_add(pos_in_rect, rect.min); + return pos; +} + fn void ui_default_draw_box(ui_box_t *box) { + r2f32_t rect = box->final_rect; + v4f32_t background_color = box->background_color; v4f32_t text_color = box->text_color; v4f32_t border_color = box->border_color; - r2f32_t rect = box->final_rect; // @todo: this or clipped? - if (ui_is_active_box(box)) { f32 active_t = f32_ease_out_n(box->active_t, 50.f); active_t = f32_clamp01(active_t); @@ -365,15 +487,7 @@ fn void ui_default_draw_box(ui_box_t *box) { rn_draw_rect_border(rect, border_color, box->border_thickness); } if (box->flags.draw_text) { - 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); - v2f32_t pos_in_rect = v2f32(0, center_pos.y); - if (box->text_align == ui_text_align_center) { - pos_in_rect = center_pos; - } - v2f32_t pos = v2f32_add(pos_in_rect, rect.min); + v2f32_t pos = ui_aligned_text_pos(box->text_align, rect, box->string); rn_draw_string(rn_state.main_font, pos, text_color, box->string); } } diff --git a/src/ui/ui.h b/src/ui/ui.h index 186b9d4..16d3b2e 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -42,6 +42,63 @@ typedef enum { } ui_lop_t; #include "ui.gen.h" + +typedef struct ui_caret_t ui_caret_t; +struct ui_caret_t { + union { + i32 e[2]; + struct { r1i32_t range; }; + }; + i32 ifront; +}; + +fn ui_caret_t ui_caret_clamp(ui_caret_t c, i32 min, i32 max) { + return (ui_caret_t){CLAMP(c.e[0],min,max), CLAMP(c.e[1],min,max), c.ifront}; +} +fn i32 ui_caret_front(ui_caret_t c) { return c.e[c.ifront]; } +fn i32 ui_caret_back(ui_caret_t c) { return c.e[c.ifront ? 0 : 1]; } + +ui_caret_t ui_caret(i32 front, i32 back) { + ui_caret_t result = {0}; + if (front >= back) { + result.range.min = back; + result.range.max = front; + result.ifront = 1; + } else { + result.range.min = front; + result.range.max = back; + result.ifront = 0; + } + return result; +} + +ui_caret_t ui_caret_set_back(ui_caret_t caret, i32 back) { + i32 front = ui_caret_front(caret); + ui_caret_t result = ui_caret(front, back); + return result; +} + +ui_caret_t ui_caret_set_front(ui_caret_t caret, i32 front) { + i32 back = ui_caret_back(caret); + ui_caret_t result = ui_caret(front, back); + return result; +} + +fn ui_caret_t ui_carets(i32 x) { + return ui_caret(x, x); +} + +typedef struct ui_text_input_t ui_text_input_t; +struct ui_text_input_t { + union { + struct { char *str; i64 len; }; + s8_t string; + }; + + i32 cap; + ui_caret_t caret; +}; + typedef struct ui_box_t ui_box_t; struct ui_box_t { ui_box_t *next; @@ -74,10 +131,7 @@ struct ui_box_t { r2f32_t final_rect; b32 expanded; - i32 ti_cursor; - char *ti_buffer; - i32 ti_buffer_len; - i32 ti_buffer_cap; + ui_text_input_t text_input; }; typedef struct ui_signal_t ui_signal_t; @@ -138,4 +192,5 @@ fn void ui_set_rect(ui_box_t *box, r2f32_t rect) { box->rect = box->full_rect = #define ui_box_flags(...) (ui_box_flag_t){__VA_ARGS__} fn ui_id_t ui_id(s8_t string); -fn ui_id_t ui_idf(char *str, ...); \ No newline at end of file +fn ui_id_t ui_idf(char *str, ...); +fn v2f32_t ui_aligned_text_pos(ui_text_align_t text_align, r2f32_t rect, s8_t string); \ No newline at end of file diff --git a/src/wasm_app/main.c b/src/wasm_app/main.c index 0f33e14..b56ab22 100644 --- a/src/wasm_app/main.c +++ b/src/wasm_app/main.c @@ -10,6 +10,7 @@ #include "wasm_app.gen.c" fn void app_init(f32 dpr) { + run_all_tests(); ma_arena_t *perm = &tcx._perm; mt_tweak_f32(font_size, 50, 4, 200); mt_tweak_f32(_font_size, 50, 50, 50); diff --git a/src/wasm_app/wasm_app.gen.c b/src/wasm_app/wasm_app.gen.c index 12b1107..6229385 100644 --- a/src/wasm_app/wasm_app.gen.c +++ b/src/wasm_app/wasm_app.gen.c @@ -2,7 +2,10 @@ gb f32 font_size = 50; gb f32 _font_size = 50; gb_read_only mt_tweak_t tweak_table[] = { -{type(f32), s8_const_lit("font_size"), &font_size, 4, 200}, -{type(f32), s8_const_lit("_font_size"), &_font_size, 50, 50}, + {type(f32), s8_const_lit("font_size"), &font_size, 4, 200}, + {type(f32), s8_const_lit("_font_size"), &_font_size, 50, 50}, }; +void run_all_tests(void) { + ui_test_text_replace(); +}// run_all_tests() diff --git a/src/wasm_app/wasm_app.meta.c b/src/wasm_app/wasm_app.meta.c index d07b7b8..8e5b637 100644 --- a/src/wasm_app/wasm_app.meta.c +++ b/src/wasm_app/wasm_app.meta.c @@ -15,6 +15,8 @@ void mt_wasm_app(ma_arena_t *arena) { cg_tweak_t *first_tweak = NULL; cg_tweak_t *last_tweak = NULL; + sb8_t *tests = sb8_serial_begin(arena); + sb8_t *sb_embeds = sb8_serial_begin(arena); sb8_printf(sb_embeds, "// automatically generated using: " __FILE__ "\n"); for (mt_file_t *it = files.first; it; it = it->next) { @@ -22,6 +24,13 @@ void mt_wasm_app(ma_arena_t *arena) { for (;par->at->kind != lex_kind_eof;) { b32 matched = false; + if (par->at->inside_macro == false && parser_matchi(par, s8_lit("fn_test"))) { + parser_expecti(par, s8_lit("void")); + lex_t *ident = parser_match(par, lex_kind_ident); + sb8_append(tests, ident->string); + matched = true; + } + if (par->at->inside_macro == false && parser_matchi(par, s8_lit("mt_embed_file"))) { parser_expect(par, lex_kind_open_paren); lex_t *var_name = parser_expect(par, lex_kind_ident); @@ -88,17 +97,25 @@ void mt_wasm_app(ma_arena_t *arena) { } } + + if (first_tweak != NULL) { for (cg_tweak_t *it = first_tweak; it; it = it->next) { sb8_printf(sb_embeds, "gb %S %S = %S;\n", it->type->name, it->name, it->value); } sb8_printf(sb_embeds, "gb_read_only mt_tweak_t tweak_table[] = {\n"); for (cg_tweak_t *it = first_tweak; it; it = it->next) { - sb8_printf(sb_embeds, "{type(%S), s8_const_lit(\"%S\"), &%S, %S, %S},\n", it->type->name, it->name, it->name, it->min, it->max); + sb8_printf(sb_embeds, " {type(%S), s8_const_lit(\"%S\"), &%S, %S, %S},\n", it->type->name, it->name, it->name, it->min, it->max); } sb8_printf(sb_embeds, "\n};\n"); } + sb8_printf(sb_embeds, "void run_all_tests(void) {\n"); + for (sb8_node_t *it = tests->first; it; it = it->next) { + sb8_printf(sb_embeds, " %S();\n", it->string); + } + sb8_printf(sb_embeds, "}// run_all_tests()\n"); + s8_t embeds = sb8_serial_end(arena, sb_embeds); os_write_file(mt_cpath(arena), embeds);