text input box

This commit is contained in:
Alessandro Mauri 2025-06-30 13:10:00 +02:00
parent 6d2594db2d
commit 972c9b581d
5 changed files with 94 additions and 23 deletions

View File

@ -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)!; 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) { if (text.len == 0) {
return; return;
@ -158,7 +158,7 @@ fn void? Ctx.push_string(&ctx, Rect bounds, String text, int z_index, Color hue
short line_len; short line_len;
Codepoint cp; Codepoint cp;
usz off, x; 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; off += x;
Glyph* gp; Glyph* gp;
if (!ascii::is_cntrl((char)cp)) { if (!ascii::is_cntrl((char)cp)) {

View File

@ -34,6 +34,7 @@ bitstruct ElemEvents : uint {
bool mouse_release : 5; bool mouse_release : 5;
bool mouse_hold : 6; bool mouse_hold : 6;
bool update : 7; bool update : 7;
bool text_input : 8;
} }
// element structure // element structure
@ -111,7 +112,7 @@ struct Ctx {
struct keyboard { struct keyboard {
char[TEXT_MAX] text; char[TEXT_MAX] text;
usz text_len; 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_press = hover && focus && ctx.is_mouse_pressed(BTN_ANY),
.mouse_release = hover && focus && ctx.is_mouse_released(BTN_ANY), .mouse_release = hover && focus && ctx.is_mouse_released(BTN_ANY),
.mouse_hold = hover && focus && ctx.is_mouse_down(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; return ev;
} }

View File

@ -25,7 +25,7 @@ bitstruct MouseButtons : uint {
// FIXME: all of these names were prefixed with key_ idk if this is better, // FIXME: all of these names were prefixed with key_ idk if this is better,
// if it is remove the prefix on MouseButtons as well // 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 { bitstruct ModKeys : uint {
bool lshift : 0; bool lshift : 0;
bool rshift : 1; bool rshift : 1;
@ -39,12 +39,20 @@ bitstruct ModKeys : uint {
bool caps : 9; bool caps : 9;
bool mode : 10; bool mode : 10;
bool scroll : 11; 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_CTRL = {.lctrl = true, .rctrl = true};
const ModKeys KMOD_SHIFT = {.lshift = true, .rshift = true}; const ModKeys KMOD_SHIFT = {.lshift = true, .rshift = true};
const ModKeys KMOD_ALT = {.lalt = true, .ralt = true}; const ModKeys KMOD_ALT = {.lalt = true, .ralt = true};
const ModKeys KMOD_GUI = {.lgui = true, .rgui = 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_NONE = {};
const ModKeys KMOD_ANY = (ModKeys)(ModKeys.inner.max); 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) 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; bool is_keys = true;
String haystack = (String)ctx.input.keyboard.text[0..ctx.input.keyboard.text_len]; String haystack = (String)ctx.input.keyboard.text[0..ctx.input.keyboard.text_len];
char[2] needle; char[2] needle;
@ -187,9 +195,10 @@ fn void Ctx.input_char(&ctx, char c)
ctx.input_text_utf8(b[..]); 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) fn void Ctx.input_mod_keys(&ctx, ModKeys modkeys)
{ {
ctx.input.keyboard.down = modkeys; ctx.input.keyboard.modkeys = modkeys;
ctx.input.events.mod_key = (uint)ctx.input.keyboard.down != 0; ctx.input.events.mod_key = (uint)ctx.input.keyboard.modkeys != 0;
} }

View File

@ -3,7 +3,8 @@ module ugui;
import std::io; import std::io;
struct ElemText { struct ElemText {
char* str; char[] str;
usz cursor; // cursor offset
} }
fn void? Ctx.text_unbounded(&ctx, String label, String text) 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)!; 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;
}

View File

@ -62,9 +62,9 @@ fn int main(String[] args)
defer ui.free(); defer ui.free();
ren::Renderer ren; ren::Renderer ren;
ren.init("Ugui Test", 640, 480, true); ren.init("Ugui Test", 800, 600, true);
defer ren.free(); defer ren.free();
ui.input_window_size(640, 480)!!; ui.input_window_size(800, 600)!!;
// //
// FONT LOADING // FONT LOADING
@ -145,6 +145,7 @@ fn int main(String[] args)
case EVENT_KEY_DOWN: case EVENT_KEY_DOWN:
mod.rctrl = e.key.key == K_RCTRL ? !!(e.type == EVENT_KEY_DOWN) : mod.rctrl; 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.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 // 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 // TEXT_INPUT event is generated. When those keys are pressed we have to
@ -155,6 +156,8 @@ fn int main(String[] args)
} }
} }
if (e.type == EVENT_KEY_DOWN && e.key.key == K_RETURN) ui.input_char('\n');
case EVENT_TEXT_INPUT: case EVENT_TEXT_INPUT:
ui.input_text_utf8(e.text.text.str_view()); ui.input_text_utf8(e.text.text.str_view());
case EVENT_WINDOW_RESIZED: case EVENT_WINDOW_RESIZED:
@ -226,19 +229,13 @@ fn int main(String[] args)
static bool check; static bool check;
ui.checkbox("check1", "", {}, &check, "tick")!!; ui.checkbox("check1", "", {}, &check, "tick")!!;
ui.toggle("toggle1", "", {}, &toggle)!!; 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")!!; 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_end()!!;
ui.div_begin("second", ugui::DIV_FILL, scroll_x: true, scroll_y: true)!!; 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.div_end()!!;
ui.frame_end()!!; ui.frame_end()!!;
/* End UI Handling */ /* End UI Handling */
ui_times.push(clock.mark()); ui_times.push(clock.mark());