2024-12-11 01:14:14 +01:00
|
|
|
module ugui;
|
|
|
|
|
|
|
|
|
|
import std::io;
|
2024-12-11 22:25:53 +01:00
|
|
|
|
2024-12-20 19:50:58 +01:00
|
|
|
struct ElemText {
|
2025-07-10 11:05:39 +02:00
|
|
|
Id hash;
|
2025-09-09 19:10:04 +02:00
|
|
|
TextSize size;
|
2025-09-13 19:47:58 +02:00
|
|
|
TextEdit* te;
|
2024-12-20 19:50:58 +01:00
|
|
|
}
|
|
|
|
|
|
2025-09-20 00:27:24 +02:00
|
|
|
/* Layout some text without bounds.
|
|
|
|
|
* There is a limitation where the current frame bounds are based on the last frame, this is usually
|
|
|
|
|
* not a problem but it is in the situation where the text changes almost all frames.
|
|
|
|
|
*/
|
2025-09-09 19:10:04 +02:00
|
|
|
macro Ctx.text(&ctx, String text, ...)
|
|
|
|
|
=> ctx.text_id(@compute_id($vasplat), text);
|
|
|
|
|
fn void? Ctx.text_id(&ctx, Id id, String text)
|
2024-12-11 01:14:14 +01:00
|
|
|
{
|
2025-06-30 18:24:50 +02:00
|
|
|
id = ctx.gen_id(id)!;
|
2024-12-11 01:14:14 +01:00
|
|
|
|
2025-10-03 15:19:52 +02:00
|
|
|
Elem* parent, elem;
|
|
|
|
|
ctx.get_elem(id, ETYPE_TEXT)!.unpack(&elem, &parent);
|
2025-07-06 23:13:16 +02:00
|
|
|
Style* style = ctx.styles.get_style(@str_hash("text"));
|
2024-12-11 01:14:14 +01:00
|
|
|
|
2025-07-10 11:05:39 +02:00
|
|
|
Id text_hash = text.hash();
|
|
|
|
|
if (elem.flags.is_new || elem.text.hash != text_hash) {
|
2025-09-09 19:10:04 +02:00
|
|
|
elem.text.size = ctx.measure_string(text)!;
|
2025-07-10 11:05:39 +02:00
|
|
|
}
|
|
|
|
|
elem.text.hash = text_hash;
|
2024-12-11 01:14:14 +01:00
|
|
|
|
2025-09-09 19:10:04 +02:00
|
|
|
elem.layout.w = @fit(style.size);
|
|
|
|
|
elem.layout.h = @fit(style.size);
|
|
|
|
|
elem.layout.text = elem.text.size;
|
|
|
|
|
elem.layout.content_offset = style.margin + style.border + style.padding;
|
|
|
|
|
|
|
|
|
|
update_parent_size(elem, parent);
|
2024-12-11 01:14:14 +01:00
|
|
|
|
2025-10-02 23:19:42 +02:00
|
|
|
ctx.layout_string(text, elem.bounds.pad(elem.layout.content_offset), TOP_LEFT, parent.z_index, style.fg)!;
|
2024-12-11 01:14:14 +01:00
|
|
|
}
|
2025-06-30 13:10:00 +02:00
|
|
|
|
2025-09-13 19:47:58 +02:00
|
|
|
|
2025-10-13 23:55:41 +02:00
|
|
|
macro Ctx.text_box(&ctx, Size w, Size h, TextEdit* te, Anchor text_alignment = TOP_LEFT, bool reflow = true, ...)
|
|
|
|
|
=> ctx.text_box_id(@compute_id($vasplat), w, h, te, text_alignment, reflow);
|
|
|
|
|
fn ElemEvents? Ctx.text_box_id(&ctx, Id id, Size w, Size h, TextEdit* te, Anchor text_alignment, bool reflow)
|
2025-06-30 13:10:00 +02:00
|
|
|
{
|
2025-06-30 18:24:50 +02:00
|
|
|
id = ctx.gen_id(id)!;
|
2025-06-30 13:10:00 +02:00
|
|
|
|
2025-10-03 15:19:52 +02:00
|
|
|
Elem* parent, elem;
|
|
|
|
|
ctx.get_elem(id, ETYPE_TEXT)!.unpack(&elem, &parent);
|
2025-07-06 23:13:16 +02:00
|
|
|
Style* style = ctx.styles.get_style(@str_hash("text-box"));
|
2025-07-05 16:37:08 +02:00
|
|
|
|
2025-09-13 19:47:58 +02:00
|
|
|
elem.text.te = te;
|
2025-09-14 20:32:31 +02:00
|
|
|
|
2025-09-13 19:47:58 +02:00
|
|
|
Id text_hash = te.to_string().hash();
|
|
|
|
|
if (elem.flags.is_new || elem.text.hash != text_hash) {
|
|
|
|
|
elem.text.size = ctx.measure_string(te.to_string())!;
|
|
|
|
|
}
|
|
|
|
|
elem.text.hash = text_hash;
|
|
|
|
|
|
|
|
|
|
elem.layout.w = w;
|
|
|
|
|
elem.layout.h = h;
|
|
|
|
|
elem.layout.text = elem.text.size;
|
|
|
|
|
elem.layout.content_offset = style.margin + style.border + style.padding;
|
|
|
|
|
|
|
|
|
|
update_parent_size(elem, parent);
|
2025-06-30 13:10:00 +02:00
|
|
|
|
|
|
|
|
// check input and update the text
|
2025-10-08 22:16:17 +02:00
|
|
|
//elem.events = ctx.get_elem_events(elem);
|
2025-09-14 20:32:31 +02:00
|
|
|
|
2025-09-13 19:47:58 +02:00
|
|
|
if (elem.events.text_input || elem.events.key_press) {
|
|
|
|
|
ctx.text_edit(elem.text.te);
|
2025-06-30 13:10:00 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-13 19:47:58 +02:00
|
|
|
Rect bg_bounds = elem.bounds.pad(style.margin);
|
|
|
|
|
Rect text_bounds = elem.bounds.pad(elem.layout.content_offset);
|
2025-10-02 23:19:42 +02:00
|
|
|
ctx.push_rect(bg_bounds, parent.z_index, style)!;
|
2025-10-18 17:40:27 +02:00
|
|
|
String s = elem.text.te.to_string();
|
2025-10-19 21:10:51 +02:00
|
|
|
if (te.sel_len) {
|
|
|
|
|
usz start = te.sel_len > 0 ? te.cursor : te.cursor + te.sel_len;
|
|
|
|
|
usz end = (te.sel_len > 0 ? te.cursor + te.sel_len : te.cursor) - 1;
|
2025-10-18 17:40:27 +02:00
|
|
|
ctx.draw_string_selection(s, text_bounds, text_alignment, start, end, parent.z_index, style.accent, reflow)!;
|
|
|
|
|
}
|
|
|
|
|
ctx.layout_string(s, text_bounds, text_alignment, parent.z_index, style.fg, reflow)!;
|
2025-06-30 13:10:00 +02:00
|
|
|
|
2025-09-13 19:47:58 +02:00
|
|
|
// draw the cursor if the element has focus
|
|
|
|
|
if (elem.events.has_focus) {
|
2025-10-19 21:10:51 +02:00
|
|
|
if (elem.events.mouse_press || elem.events.mouse_hold) {
|
|
|
|
|
usz cur = ctx.hit_test_string(s, text_bounds, text_alignment, ctx.input.mouse.pos, reflow)!;
|
2025-10-20 16:15:57 +02:00
|
|
|
bool select = (elem.events.mouse_hold && !elem.events.mouse_press) || (ctx.get_mod() & KMOD_SHIFT);
|
|
|
|
|
te.set_cursor(cur, select);
|
2025-10-14 10:44:13 +02:00
|
|
|
}
|
2025-10-19 21:10:51 +02:00
|
|
|
Rect cur = ctx.get_cursor_position(s, text_bounds, text_alignment, te.cursor, reflow)!;
|
2025-10-13 23:55:41 +02:00
|
|
|
cur.w = 2;
|
2025-10-02 23:19:42 +02:00
|
|
|
ctx.push_rect(cur, parent.z_index, &&(Style){.bg = style.fg})!;
|
2025-09-13 19:47:58 +02:00
|
|
|
}
|
2025-06-30 13:10:00 +02:00
|
|
|
return elem.events;
|
2025-09-13 19:47:58 +02:00
|
|
|
}
|