module ugui; import std::io; // button element struct ElemButton { int filler; } // draw a button, return the events on that button macro Ctx.button(&ctx, Rect size, bool state = false, ...) => ctx.button_id(@compute_id($vasplat), size, state); fn ElemEvents? Ctx.button_id(&ctx, Id id, Rect size, bool active) { id = ctx.gen_id(id)!; Elem *parent = ctx.get_parent()!; Elem *elem = ctx.get_elem(id, ETYPE_BUTTON)!; Style* style_norm = ctx.styles.get_style(@str_hash("button")); elem.bounds = ctx.position_element(parent, size, style_norm); // if the bounds are null the element is outside the div view, // no interaction should occur so just return if (elem.bounds.is_null()) { return {}; } Style* style_active = ctx.styles.get_style(@str_hash("button-active")); elem.events = ctx.get_elem_events(elem); bool is_active = active || ctx.elem_focus(elem) || elem.events.mouse_hover; // Draw the button ctx.push_rect(elem.bounds, parent.div.z_index, is_active ? style_active : style_norm)!; return elem.events; } macro Ctx.button_label(&ctx, String label, Rect size = {0,0,short.max,short.max}, bool active = false, ...) => ctx.button_label_id(@compute_id($vasplat), label, size, active); fn ElemEvents? Ctx.button_label_id(&ctx, Id id, String label, Rect size, bool active) { id = ctx.gen_id(id)!; Elem *parent = ctx.get_parent()!; Elem *elem = ctx.get_elem(id, ETYPE_BUTTON)!; short line_height = (short)ctx.font.ascender - (short)ctx.font.descender; Rect text_size = ctx.get_text_bounds(label)!; Rect btn_size = text_size.add({0,0,10,10}); Style* style_norm = ctx.styles.get_style(@str_hash("button")); // 2. Layout elem.bounds = ctx.position_element(parent, btn_size, style_norm); if (elem.bounds.is_null()) { return {}; } Style* style_active = ctx.styles.get_style(@str_hash("button-active")); elem.events = ctx.get_elem_events(elem); bool is_active = active || ctx.elem_focus(elem) || elem.events.mouse_hover; Style* style = is_active ? style_active : style_norm; // Draw the button text_size.x = elem.bounds.x; text_size.y = elem.bounds.y; Point off = ctx.center_text(text_size, elem.bounds); text_size.x += off.x; text_size.y += off.y; ctx.push_rect(elem.bounds, parent.div.z_index, style)!; ctx.push_string(text_size, label, parent.div.z_index, style.fg)!; return elem.events; } macro Ctx.button_icon(&ctx, String icon, String on_icon = "", bool active = false, ...) => ctx.button_icon_id(@compute_id($vasplat), icon, on_icon, active); fn ElemEvents? Ctx.button_icon_id(&ctx, Id id, String icon, String on_icon, bool active) { id = ctx.gen_id(id)!; Elem *parent = ctx.get_parent()!; Elem *elem = ctx.get_elem(id, ETYPE_BUTTON)!; Sprite* def_sprite = ctx.sprite_atlas.get(icon)!; Sprite* on_sprite = ctx.sprite_atlas.get(on_icon) ?? &&(Sprite){}; Rect max_size = def_sprite.rect().max(on_sprite.rect()); Style* style_norm = ctx.styles.get_style(@str_hash("button")); elem.bounds = ctx.position_element(parent, max_size, style_norm); // if the bounds are null the element is outside the div view, // no interaction should occur so just return if (elem.bounds.is_null()) { return {}; } Style* style_active = ctx.styles.get_style(@str_hash("button-active")); elem.events = ctx.get_elem_events(elem); bool is_active = active || ctx.elem_focus(elem) || elem.events.mouse_hover; Style* style = is_active ? style_active : style_norm; Id tex_id = ctx.sprite_atlas.id; if (active && on_icon != "") { ctx.push_sprite(elem.bounds, on_sprite.uv(), tex_id, parent.div.z_index, type: on_sprite.type)!; } else { ctx.push_sprite(elem.bounds, def_sprite.uv(), tex_id, parent.div.z_index, type: def_sprite.type)!; } // Draw the button ctx.push_rect(elem.bounds, parent.div.z_index, style)!; return elem.events; } // FIXME: this should be inside the style const ushort DEFAULT_CHECKBOX_SIZE = 16; macro Ctx.checkbox(&ctx, String desc, Point off, bool* active, String tick_sprite = {}, ...) => ctx.checkbox_id(@compute_id($vasplat), desc, off, active, tick_sprite); fn void? Ctx.checkbox_id(&ctx, Id id, String description, Point off, bool* active, String tick_sprite) { id = ctx.gen_id(id)!; Elem *parent = ctx.get_parent()!; Elem *elem = ctx.get_elem(id, ETYPE_BUTTON)!; Style* style = ctx.styles.get_style(@str_hash("checkbox")); Rect size = {off.x, off.y, DEFAULT_CHECKBOX_SIZE, DEFAULT_CHECKBOX_SIZE}; elem.bounds = ctx.position_element(parent, size, style); // if the bounds are null the element is outside the div view, // no interaction should occur so just return if (elem.bounds.is_null()) return; elem.events = ctx.get_elem_events(elem); if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active); if (tick_sprite != {}) { ctx.push_rect(elem.bounds, parent.div.z_index, style)!; if (*active) { ctx.draw_sprite_raw(tick_sprite, elem.bounds, center: true)!; } } else { ctx.push_rect(elem.bounds, parent.div.z_index, style)!; if (*active) { ushort x = DEFAULT_CHECKBOX_SIZE / 4; Rect check = elem.bounds.add({x, x, -x*2, -x*2}); Style s = *style; s.bg = s.primary; s.margin = s.border = s.padding = {}; ctx.push_rect(check, parent.div.z_index, &s)!; } } } // FIXME: this should be inside the style const short DEFAULT_SWITCH_SIZE = 16; macro Ctx.toggle(&ctx, String desc, Point off, bool* active) => ctx.toggle_id(@compute_id($vasplat), desc, off, active); fn void? Ctx.toggle_id(&ctx, Id id, String description, Point off, bool* active) { id = ctx.gen_id(id)!; Elem *parent = ctx.get_parent()!; Elem *elem = ctx.get_elem(id, ETYPE_BUTTON)!; Style* style = ctx.styles.get_style(@str_hash("toggle")); Rect size = {off.x, off.y, DEFAULT_SWITCH_SIZE*2, DEFAULT_SWITCH_SIZE}; elem.bounds = ctx.position_element(parent, size, style); // if the bounds are null the element is outside the div view, // no interaction should occur so just return if (elem.bounds.is_null()) return; elem.events = ctx.get_elem_events(elem); if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active); // Draw the button // FIXME: THIS IS SHIT ctx.push_rect(elem.bounds, parent.div.z_index, style)!; Rect t = elem.bounds.add({*active ? (DEFAULT_SWITCH_SIZE+3) : +3, +3, -DEFAULT_SWITCH_SIZE-6, -6}); Style s = *style; s.bg = s.primary; s.margin = s.border = s.padding = {}; ctx.push_rect(t, parent.div.z_index, &s)!; }