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)!;
}
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)) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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());