diff --git a/lib/ugui.c3l/src/cache.c3 b/lib/ugui.c3l/src/cache.c3 index 04ea3a3..5d85494 100644 --- a/lib/ugui.c3l/src/cache.c3 +++ b/lib/ugui.c3l/src/cache.c3 @@ -9,10 +9,6 @@ module cache{Key, Value, SIZE}; * the elements that were not recently used. */ -// FIXME: this module should really allocate all resources on an arena or temp -// allocator, since all memory allocations are connected and freeing -// happens at the same time - import std::core::mem; import std::core::mem::allocator; import std::collections::bitset; @@ -79,6 +75,7 @@ fn Value*? Cache.search(&cache, Key id) } /* HIT, set as recently used */ + //io::printfn("HIT: %d [%d]", entry.value, entry.key); cache.used[entry.value] = true; return &(cache.pool[entry.value]); } @@ -99,16 +96,19 @@ fn void Cache.remove(&cache, Key id) /* If there is no free space left then just return the first position */ fn usz Cache.get_free_spot(&cache) @private { + // TODO: in the upgrade to c3 1.7.5 use @bitsof() const BITS = $typeof(cache.present.data[0]).sizeof*8; foreach (idx, d: cache.present.data) { - if (d.clz() != BITS) { - return idx*BITS + BITS-d.clz(); + if (d != $typeof(d).max) { + usz spot = idx*BITS + BITS-d.clz(); + if (cache.used[spot]) unreachable("free spot is not actually free: %d", spot); + return spot; } } return 0; } -fn Value*? Cache.insert_at(&cache, Value *g, Key id, usz index) @private +fn Value*? Cache.insert_at(&cache, Value* g, Key id, usz index) @private { // TODO: verify index, g and id Value* spot; diff --git a/lib/ugui.c3l/src/ugui_cmd.c3 b/lib/ugui.c3l/src/ugui_cmd.c3 index a4982fc..fd99218 100644 --- a/lib/ugui.c3l/src/ugui_cmd.c3 +++ b/lib/ugui.c3l/src/ugui_cmd.c3 @@ -97,9 +97,7 @@ macro Ctx.push_cmd(&ctx, Cmd *cmd, int z_index) default: return ctx.cmd_queue.enqueue(cmd); } if (cull_rect(rect, ctx.div_scissor)) { -// io::print("NOPE: "); -// io::print(cmd.rect.rect); -// io::printn(cmd.z_index); +// println("NOPE: ", cmd.rect.rect, cmd.z_index); // unreachable(); return; } diff --git a/lib/ugui.c3l/src/ugui_core.c3 b/lib/ugui.c3l/src/ugui_core.c3 index 19f0a83..f23560c 100644 --- a/lib/ugui.c3l/src/ugui_core.c3 +++ b/lib/ugui.c3l/src/ugui_core.c3 @@ -165,10 +165,9 @@ macro Id @compute_id(...) // resets all flags except is_new which is set accordingly fn Elem*? Ctx.get_elem(&ctx, Id id, ElemType type) { - Elem empty_elem; bool is_new; Elem* elem; - elem = ctx.cache.get_or_insert(&empty_elem, id, &is_new)!; + elem = ctx.cache.get_or_insert(&&(Elem){}, id, &is_new)!; elem.flags = (ElemFlags)0; elem.flags.is_new = is_new; elem.id = id; diff --git a/lib/ugui.c3l/src/ugui_layout.c3 b/lib/ugui.c3l/src/ugui_layout.c3 index bae4ea7..7b23e0b 100644 --- a/lib/ugui.c3l/src/ugui_layout.c3 +++ b/lib/ugui.c3l/src/ugui_layout.c3 @@ -55,27 +55,52 @@ macro Size Layout.total_height(&el) // Returns the width and height of a @FIT() element based on it's wanted size (min/max) // and the content size, this function is used to both update the parent's children size and // give the dimensions of a fit element +// TODO: test and cleanup this function macro Point Layout.get_dimensions(&el) { - Size content_width = el.children.w + el.text.width; - Size width = el.w.combine(content_width); - short final_width = width.greater(); - - short text_height; - if (el.text.area != 0) { - short text_width = (@exact(final_width) - el.children.w).combine(el.text.width).min; - text_height = @exact((short)(el.text.area / text_width)).combine(el.text.height).min; + // if the direction is ROW then the text is placed horizontally with the children + if (el.dir == ROW) { + Size content_width = el.children.w + el.text.width; + Size width = el.w.combine(content_width); + short final_width = width.greater(); + + short text_height; + if (el.text.area != 0) { + short text_width = (@exact(final_width) - el.children.w).combine(el.text.width).min; + text_height = @exact((short)(el.text.area / text_width)).combine(el.text.height).min; + } + + Size content_height = el.children.h.comb_max(@exact(text_height)); + Size height = el.h.combine(content_height); + short final_height = height.greater(); + + Point dim = { + .x = final_width + el.content_offset.x + el.content_offset.w, + .y = final_height + el.content_offset.y + el.content_offset.h, + }; + return dim; + } else { + // if the direction is COLUMN the text and children are one on top of the other + Size content_width = el.children.w.comb_max(el.text.width); + Size width = el.w.combine(content_width); + short final_width = width.greater(); + + short text_height; + if (el.text.area != 0) { + short text_width = @exact(final_width).combine(el.text.width).min; + text_height = @exact((short)(el.text.area / text_width)).combine(el.text.height).min; + } + + Size content_height = el.children.h + @exact(text_height); + Size height = el.h.combine(content_height); + short final_height = height.greater(); + + Point dim = { + .x = final_width + el.content_offset.x + el.content_offset.w, + .y = final_height + el.content_offset.y + el.content_offset.h, + }; + return dim; } - - Size content_height = el.children.h + @exact(text_height); - Size height = el.h.combine(content_height); - short final_height = height.greater(); - - Point dim = { - .x = final_width + el.content_offset.x + el.content_offset.w, - .y = final_height + el.content_offset.y + el.content_offset.h, - }; - return dim; } // The content space of the element diff --git a/lib/ugui.c3l/src/ugui_sprite.c3 b/lib/ugui.c3l/src/ugui_sprite.c3 index edd3b8f..ab4127b 100644 --- a/lib/ugui.c3l/src/ugui_sprite.c3 +++ b/lib/ugui.c3l/src/ugui_sprite.c3 @@ -30,10 +30,6 @@ struct SpriteAtlas { bool should_update; } -struct ElemSprite { - Id id; -} - // name: some examples are "icons" or "images" fn void? SpriteAtlas.init(&this, String name, AtlasType type, ushort width, ushort height) { @@ -109,47 +105,3 @@ fn void? Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType t ctx.sprite_atlas.insert(name, type, pixels, (ushort)desc.width, (ushort)desc.height, (ushort)desc.width)!; } - - -/* -macro Ctx.sprite(&ctx, String name, Point off = {0,0}, ...) - => ctx.sprite_id(@compute_id($vasplat), name, off); -fn void? Ctx.sprite_id(&ctx, Id id, String name, Point off) -{ - id = ctx.gen_id(id)!; - - Elem *parent = ctx.get_parent()!; - Elem *elem = ctx.get_elem(id, ETYPE_SPRITE)!; - - Style* style = ctx.styles.get_style(@str_hash("sprite")); - Sprite* sprite = ctx.sprite_atlas.get(name)!; - - Rect uv = { sprite.u, sprite.v, sprite.w, sprite.h }; - Rect bounds = { 0, 0, sprite.w, sprite.h }; - - elem.bounds = ctx.layout_element(parent, bounds.off(off), style); - elem.sprite.id = ctx.get_sprite_atlas_id(name); - - // 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; - - Id tex_id = ctx.sprite_atlas.id; - - return ctx.push_sprite(elem.bounds, uv, tex_id, parent.div.z_index)!; -} - -fn void? Ctx.draw_sprite_raw(&ctx, String name, Rect bounds, bool center = false) -{ - Elem *parent = ctx.get_parent()!; - Sprite* sprite = ctx.sprite_atlas.get(name)!; - Id tex_id = ctx.sprite_atlas.id; - - if (center) { - Point off = {.x = (bounds.w - sprite.w) / 2, .y = (bounds.h - sprite.h) / 2}; - bounds = bounds.off(off); - } - - return ctx.push_sprite(bounds, sprite.uv(), tex_id, parent.div.z_index, type: sprite.type)!; -} -*/ \ No newline at end of file diff --git a/lib/ugui.c3l/src/ugui_button.c3 b/lib/ugui.c3l/src/widgets/ugui_button.c3 similarity index 64% rename from lib/ugui.c3l/src/ugui_button.c3 rename to lib/ugui.c3l/src/widgets/ugui_button.c3 index 00b2b5c..7c69379 100644 --- a/lib/ugui.c3l/src/ugui_button.c3 +++ b/lib/ugui.c3l/src/widgets/ugui_button.c3 @@ -18,9 +18,6 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon) Style* style = ctx.styles.get_style(@str_hash("button")); Sprite* sprite = icon != "" ? ctx.sprite_atlas.get(icon)! : &&(Sprite){}; - - // TODO: get min size by style - TextSize text_size = ctx.measure_string(label)!; Rect icon_size = sprite.rect(); ushort min_size = style.size; @@ -44,7 +41,7 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon) elem.layout.h = @fit(min_size); elem.layout.children.w = @exact(content_size.x); elem.layout.children.h = @exact(content_size.y); - elem.layout.text = text_size; + elem.layout.text = ctx.measure_string(label)!; elem.layout.content_offset = style.margin + style.border + style.padding; update_parent_grow(elem, parent); @@ -86,47 +83,82 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon) return elem.events; } -/* -// FIXME: this should be inside the style -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) +macro Ctx.checkbox(&ctx, String desc, bool* active, String tick_sprite = "", ...) + => ctx.checkbox_id(@compute_id($vasplat), desc, active, tick_sprite); +fn void? Ctx.checkbox_id(&ctx, Id id, String description, 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, style.size, style.size}; - elem.bounds = ctx.layout_element(parent, size, style); + short inner_pad = description != "" ? style.size/2 : 0; + /* + * |< >| style.size/2 + * +---------------------|---|-----------+ + * | | .-----. ---|-- + * | +-----------------+ ' ### ' | ^ + * | | description | | ##### | | style.size + * | +-----------------+ . ### . | v + * | '-----' ---|-- + * +-------------------------|-------|---+ + * |<----->| style.size + */ + + elem.layout.w = @fit(style.size); + elem.layout.h = @fit(style.size); + elem.layout.children.w = @exact(style.size + inner_pad); + elem.layout.children.h = @exact(style.size); + elem.layout.text = ctx.measure_string(description)!; + elem.layout.content_offset = style.margin + style.border + style.padding; - // 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; + update_parent_grow(elem, parent); + update_parent_size(elem, parent); elem.events = ctx.get_elem_events(elem); if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active); + + + Rect content_bounds = elem.bounds.pad(elem.layout.content_offset); + Rect text_bounds = { + .x = content_bounds.x, + .y = content_bounds.y, + .w = content_bounds.w - inner_pad - style.size, + .h = content_bounds.h + }; + Rect check_bounds = { + .x = content_bounds.x + text_bounds.w + inner_pad, + .y = content_bounds.y + (content_bounds.h - style.size)/2, + .w = style.size, + .h = style.size, + }; + + Style s; + s.bg = style.bg; + s.secondary = style.secondary; + s.border = style.border; + s.radius = style.radius; - if (tick_sprite != {}) { - ctx.push_rect(elem.bounds, parent.div.z_index, style)!; + ctx.layout_string(description, text_bounds, CENTER, parent.div.z_index, style.fg)!; + if (tick_sprite != "") { + ctx.push_rect(check_bounds, parent.div.z_index, &s)!; if (*active) { - ctx.draw_sprite_raw(tick_sprite, elem.bounds, center: true)!; + Sprite* sprite = ctx.sprite_atlas.get(tick_sprite)!; + Id tex_id = ctx.sprite_atlas.id; + ctx.push_sprite(sprite.rect().center_to(check_bounds), sprite.uv(), tex_id, parent.div.z_index, type: sprite.type)!; } } else { - ctx.push_rect(elem.bounds, parent.div.z_index, style)!; if (*active) { - ushort x = style.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)!; + s.bg = style.primary; + ctx.push_rect(check_bounds, parent.div.z_index, &s)!; + } else { + ctx.push_rect(check_bounds, parent.div.z_index, &s)!; } } } +/* // FIXME: this should be inside the style macro Ctx.toggle(&ctx, String desc, Point off, bool* active) => ctx.toggle_id(@compute_id($vasplat), desc, off, active); @@ -156,5 +188,4 @@ fn void? Ctx.toggle_id(&ctx, Id id, String description, Point off, bool* active) s.bg = s.primary; s.margin = s.border = s.padding = {}; ctx.push_rect(t, parent.div.z_index, &s)!; -} -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/lib/ugui.c3l/src/ugui_div.c3 b/lib/ugui.c3l/src/widgets/ugui_div.c3 similarity index 98% rename from lib/ugui.c3l/src/ugui_div.c3 rename to lib/ugui.c3l/src/widgets/ugui_div.c3 index a53cd0c..c5667d2 100644 --- a/lib/ugui.c3l/src/ugui_div.c3 +++ b/lib/ugui.c3l/src/widgets/ugui_div.c3 @@ -161,7 +161,7 @@ fn void? Ctx.div_end(&ctx) ctx.active_div = ctx.tree.parentof(ctx.active_div)!; Elem* parent = ctx.get_parent()!; ctx.div_scissor = parent.bounds; - ctx.push_scissor(parent.bounds, elem.div.z_index)!; + ctx.reset_scissor(elem.div.z_index)!; update_parent_size(elem, parent); } diff --git a/lib/ugui.c3l/src/ugui_slider.c3 b/lib/ugui.c3l/src/widgets/ugui_slider.c3 similarity index 100% rename from lib/ugui.c3l/src/ugui_slider.c3 rename to lib/ugui.c3l/src/widgets/ugui_slider.c3 diff --git a/lib/ugui.c3l/src/widgets/ugui_sprite.c3 b/lib/ugui.c3l/src/widgets/ugui_sprite.c3 new file mode 100644 index 0000000..f0c8661 --- /dev/null +++ b/lib/ugui.c3l/src/widgets/ugui_sprite.c3 @@ -0,0 +1,27 @@ +module ugui; + +struct ElemSprite { + Id id; +} + +macro Ctx.sprite(&ctx, String name, ...) + => ctx.sprite_id(@compute_id($vasplat), name); +fn void? Ctx.sprite_id(&ctx, Id id, String name) +{ + id = ctx.gen_id(id)!; + Elem *parent = ctx.get_parent()!; + Elem *elem = ctx.get_elem(id, ETYPE_SPRITE)!; + + Style* style = ctx.styles.get_style(@str_hash("sprite")); + Sprite* sprite = ctx.sprite_atlas.get(name)!; + elem.sprite.id = ctx.get_sprite_atlas_id(name); + + elem.layout.w = elem.layout.children.w = @exact(sprite.w); + elem.layout.h = elem.layout.children.h = @exact(sprite.h); + + update_parent_grow(elem, parent); + update_parent_size(elem, parent); + + Id tex_id = ctx.sprite_atlas.id; + return ctx.push_sprite(elem.bounds, sprite.uv(), tex_id, parent.div.z_index, type: sprite.type)!; +} diff --git a/lib/ugui.c3l/src/ugui_text.c3 b/lib/ugui.c3l/src/widgets/ugui_text.c3 similarity index 100% rename from lib/ugui.c3l/src/ugui_text.c3 rename to lib/ugui.c3l/src/widgets/ugui_text.c3 diff --git a/resources/style.css b/resources/style.css index 4564354..37f17fc 100644 --- a/resources/style.css +++ b/resources/style.css @@ -28,7 +28,7 @@ checkbox { border: 2; padding: 1; radius: 10; - size: 16; + size: 20; bg: #3c3836ff; fg: #fbf1c7ff; primary: #cc241dff; diff --git a/src/main.c3 b/src/main.c3 index f92778e..3d497e2 100644 --- a/src/main.c3 +++ b/src/main.c3 @@ -360,7 +360,7 @@ fn void calculator(ugui::Ctx* ui) case "c": len = 0; case "d": - len--; + if (len > 0) len--; } // ui input/output @@ -389,9 +389,7 @@ fn void calculator(ugui::Ctx* ui) ui.button("3")!!.mouse_press ? buffer[len++] = '3' : 0; ui.button("(")!!.mouse_press ? buffer[len++] = '(' : 0; }!!; - ui.@div(ugui::@exact(10), ugui::@exact(10)) {}!!; - ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) { ui.button("x")!!.mouse_press ? buffer[len++] = '*' : 0; ui.button("/")!!.mouse_press ? buffer[len++] = '/' : 0; @@ -400,7 +398,7 @@ fn void calculator(ugui::Ctx* ui) }!!; ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) { ui.button("C")!!.mouse_press ? len = 0 : 0; - ui.button("D")!!.mouse_press ? len-- : 0; + ui.button("D")!!.mouse_press ? len > 0 ? len-- : 0 : 0; ui.button("-")!!.mouse_press ? buffer[len++] = '-' : 0; // eval the expression with 'bc' @@ -413,5 +411,12 @@ fn void calculator(ugui::Ctx* ui) } }!!; }!!; + + ui.@div(ugui::@grow(), ugui::@grow(), anchor: CENTER) { + static bool state; + ui.checkbox("boolean", &state, "tick")!!; + ui.sprite("tux")!!; + }!!; + }!!; }!!; }