diff --git a/lib/ugui.c3l/src/ugui_cmd.c3 b/lib/ugui.c3l/src/ugui_cmd.c3 index 77468cb..5b2ac83 100644 --- a/lib/ugui.c3l/src/ugui_cmd.c3 +++ b/lib/ugui.c3l/src/ugui_cmd.c3 @@ -138,7 +138,7 @@ fn void? Ctx.push_sprite(&ctx, Rect bounds, Rect texture, Id texture_id, int z_i ctx.push_cmd(&cmd, z_index)!; } -fn void? Ctx.push_string(&ctx, Rect bounds, String text, int z_index, Color hue = 0xffffffffu.to_rgba()) +fn void? Ctx.push_string(&ctx, Rect bounds, char[] text, int z_index, Color hue = 0xffffffffu.to_rgba()) { if (text.len == 0) { return; @@ -158,7 +158,7 @@ fn void? Ctx.push_string(&ctx, Rect bounds, String text, int z_index, Color hue short line_len; Codepoint cp; usz off, x; - while ((cp = str_to_codepoint(text[off..], &x)) != 0) { + while (off < text.len && (cp = str_to_codepoint(text[off..], &x)) != 0) { off += x; Glyph* gp; if (!ascii::is_cntrl((char)cp)) { diff --git a/lib/ugui.c3l/src/ugui_core.c3 b/lib/ugui.c3l/src/ugui_core.c3 index 7db60a6..40696d5 100644 --- a/lib/ugui.c3l/src/ugui_core.c3 +++ b/lib/ugui.c3l/src/ugui_core.c3 @@ -34,6 +34,7 @@ bitstruct ElemEvents : uint { bool mouse_release : 5; bool mouse_hold : 6; bool update : 7; + bool text_input : 8; } // element structure @@ -111,7 +112,7 @@ struct Ctx { struct keyboard { char[TEXT_MAX] text; usz text_len; - ModKeys down; + ModKeys modkeys; } } @@ -343,6 +344,7 @@ fn ElemEvents Ctx.get_elem_events(&ctx, Elem *elem) .mouse_press = hover && focus && ctx.is_mouse_pressed(BTN_ANY), .mouse_release = hover && focus && ctx.is_mouse_released(BTN_ANY), .mouse_hold = hover && focus && ctx.is_mouse_down(BTN_ANY), + .text_input = focus && (ctx.input.keyboard.text_len || ctx.input.keyboard.modkeys & KMOD_TXT), }; return ev; } diff --git a/lib/ugui.c3l/src/ugui_input.c3 b/lib/ugui.c3l/src/ugui_input.c3 index 45811c4..ac70110 100644 --- a/lib/ugui.c3l/src/ugui_input.c3 +++ b/lib/ugui.c3l/src/ugui_input.c3 @@ -25,7 +25,7 @@ bitstruct MouseButtons : uint { // FIXME: all of these names were prefixed with key_ idk if this is better, // if it is remove the prefix on MouseButtons as well -// Modifier Keys, same as SDL +// Modifier Keys, intended as any key that is not text bitstruct ModKeys : uint { bool lshift : 0; bool rshift : 1; @@ -39,12 +39,20 @@ bitstruct ModKeys : uint { bool caps : 9; bool mode : 10; bool scroll : 11; + bool bkspc : 12; + bool del : 13; + // arrow keys + bool up : 14; + bool down : 15; + bool left : 16; + bool right : 17; } const ModKeys KMOD_CTRL = {.lctrl = true, .rctrl = true}; const ModKeys KMOD_SHIFT = {.lshift = true, .rshift = true}; const ModKeys KMOD_ALT = {.lalt = true, .ralt = true}; const ModKeys KMOD_GUI = {.lgui = true, .rgui = true}; +const ModKeys KMOD_TXT = {.bkspc = true, .del = true}; // modkeys that act like text input const ModKeys KMOD_NONE = {}; const ModKeys KMOD_ANY = (ModKeys)(ModKeys.inner.max); @@ -60,7 +68,7 @@ const ModKeys KEY_ANY = (ModKeys)(ModKeys.inner.max); fn bool Ctx.check_key_combo(&ctx, ModKeys mod, String keys) { - bool is_mod = (bool)(ctx.input.keyboard.down & mod); + bool is_mod = (bool)(ctx.input.keyboard.modkeys & mod); bool is_keys = true; String haystack = (String)ctx.input.keyboard.text[0..ctx.input.keyboard.text_len]; char[2] needle; @@ -187,9 +195,10 @@ fn void Ctx.input_char(&ctx, char c) ctx.input_text_utf8(b[..]); } -// Mouse Buttons down +// Modifier keys, like control or backspace +// TODO: make this call repetible to input modkeys one by one fn void Ctx.input_mod_keys(&ctx, ModKeys modkeys) { - ctx.input.keyboard.down = modkeys; - ctx.input.events.mod_key = (uint)ctx.input.keyboard.down != 0; + ctx.input.keyboard.modkeys = modkeys; + ctx.input.events.mod_key = (uint)ctx.input.keyboard.modkeys != 0; } diff --git a/lib/ugui.c3l/src/ugui_text.c3 b/lib/ugui.c3l/src/ugui_text.c3 index bf04077..2a6d436 100644 --- a/lib/ugui.c3l/src/ugui_text.c3 +++ b/lib/ugui.c3l/src/ugui_text.c3 @@ -3,7 +3,8 @@ module ugui; import std::io; struct ElemText { - char* str; + char[] str; + usz cursor; // cursor offset } fn void? Ctx.text_unbounded(&ctx, String label, String text) @@ -28,3 +29,66 @@ fn void? Ctx.text_unbounded(&ctx, String label, String text) ctx.push_string(elem.bounds, text, parent.div.z_index)!; } + +fn ElemEvents? Ctx.text_box(&ctx, String label, Rect size, char[] text, usz* text_len) +{ + Id id = ctx.gen_id(label)!; + + Elem *parent = ctx.get_parent()!; + Elem *elem = ctx.get_elem(id)!; + // add it to the tree + ctx.tree.add(id, ctx.active_div)!; + + // 1. Fill the element fields + // this resets the flags + elem.type = ETYPE_TEXT; + elem.text.str = text; + + // layout the text box + elem.bounds = ctx.position_element(parent, size, true); + + // check input and update the text + elem.events = ctx.get_elem_events(elem); + + if (elem.events.text_input) { + usz l = ctx.input.keyboard.text_len; + char[] t = ctx.input.keyboard.text[..l]; + + if (l != 0 && l < text.len - *text_len) { + text[*text_len..*text_len+l] = t[..]; + *text_len += l; + } + + if (ctx.input.keyboard.modkeys.bkspc) { + *text_len = *text_len > 0 ? *text_len-1 : 0; + } + + } + elem.text.cursor = *text_len; + + // draw the box + short line_height = (short)ctx.font.line_height(); + Rect text_box = elem.bounds.sub({0,0,0,line_height}); + Rect input_box = { + .x = elem.bounds.x, + .y = elem.bounds.y + elem.bounds.h - line_height, + .w = elem.bounds.w, + .h = line_height, + }; + Rect cursor; + Point b = ctx.get_cursor_position((String)text[:elem.text.cursor])!; + cursor = { + .x = b.x, + .y = b.y, + .w = 3, + .h = line_height, + }; + cursor = cursor.off(elem.bounds.position()); + + ctx.push_rect(text_box, 0xabababffu.to_rgba(), parent.div.z_index, do_border: true)!; + ctx.push_string(text_box, text[:*text_len], parent.div.z_index)!; + ctx.push_rect(input_box, 0x424242ffu.to_rgba(), parent.div.z_index)!; + ctx.push_rect(cursor, 0x00000042u.to_rgba(), parent.div.z_index)!; + + return elem.events; +} diff --git a/src/main.c3 b/src/main.c3 index 1324859..71ead09 100644 --- a/src/main.c3 +++ b/src/main.c3 @@ -62,9 +62,9 @@ fn int main(String[] args) defer ui.free(); ren::Renderer ren; - ren.init("Ugui Test", 640, 480, true); + ren.init("Ugui Test", 800, 600, true); defer ren.free(); - ui.input_window_size(640, 480)!!; + ui.input_window_size(800, 600)!!; // // FONT LOADING @@ -145,6 +145,7 @@ fn int main(String[] args) case EVENT_KEY_DOWN: mod.rctrl = e.key.key == K_RCTRL ? !!(e.type == EVENT_KEY_DOWN) : mod.rctrl; mod.lctrl = e.key.key == K_LCTRL ? !!(e.type == EVENT_KEY_DOWN) : mod.lctrl; + mod.bkspc = e.key.key == K_BACKSPACE ? !!(e.type == EVENT_KEY_DOWN) : mod.bkspc; // pressing ctrl+key or alt+key does not generate a character as such no // TEXT_INPUT event is generated. When those keys are pressed we have to @@ -154,6 +155,8 @@ fn int main(String[] args) ui.input_char((char)e.key.key); } } + + if (e.type == EVENT_KEY_DOWN && e.key.key == K_RETURN) ui.input_char('\n'); case EVENT_TEXT_INPUT: ui.input_text_utf8(e.text.text.str_view()); @@ -226,19 +229,13 @@ fn int main(String[] args) static bool check; ui.checkbox("check1", "", {}, &check, "tick")!!; ui.toggle("toggle1", "", {}, &toggle)!!; - -/* - ui.layout_set_column()!!; - ui.button_label(" A ")!!; - ui.button_label(" B ")!!; - ui.layout_next_column()!!; - ui.button_label(" C ")!!; - ui.button_label(" D ")!!; - ui.layout_next_row()!!; - ui.button_label(" E ")!!; -*/ }; ui.draw_sprite("sprite1", "tux")!!; + + static char[128] text_box = "ciao mamma"; + static usz text_len = "ciao mamma".len; + ui.text_box("text input", {0,0,200,200}, text_box[..], &text_len)!!; + ui.div_end()!!; ui.div_begin("second", ugui::DIV_FILL, scroll_x: true, scroll_y: true)!!; @@ -280,7 +277,6 @@ fn int main(String[] args) }; ui.div_end()!!; - ui.frame_end()!!; /* End UI Handling */ ui_times.push(clock.mark());