From 79a2d66880bea156e4d81c5bde2d2b4d6b11f368 Mon Sep 17 00:00:00 2001 From: Alessandro Mauri Date: Mon, 5 May 2025 16:23:26 +0200 Subject: [PATCH] update project to c3 0.7.1 --- TODO | 34 ++++++++++++++-- lib/schrift.c3l/libschrift.c3 | 6 +-- src/cache.c3 | 31 ++++++++------- src/fifo.c3 | 17 ++++---- src/main.c3 | 74 ++++++++++++++++++----------------- src/ugui_atlas.c3 | 43 ++++++++++++++++---- src/ugui_button.c3 | 65 ++++++++++++++++++++++++------ src/ugui_cmd.c3 | 12 +++--- src/ugui_core.c3 | 37 ++++++++---------- src/ugui_div.c3 | 12 +++--- src/ugui_font.c3 | 43 ++++++++++---------- src/ugui_input.c3 | 8 ++-- src/ugui_layout.c3 | 32 +++++++-------- src/ugui_shapes.c3 | 34 ++++++++-------- src/ugui_slider.c3 | 8 ++-- src/ugui_sprite.c3 | 40 ++++++++++--------- src/ugui_text.c3 | 2 +- src/vtree.c3 | 64 ++++++++++++++---------------- 18 files changed, 324 insertions(+), 238 deletions(-) diff --git a/TODO b/TODO index 9b412a2..1817dd9 100644 --- a/TODO +++ b/TODO @@ -13,6 +13,8 @@ to maintain focus until mouse release (fix scroll bars) [x] Clip element bounds to parent div, specifically text [ ] Resizeable divs [ ] Implement a z index and sort command buffer based on that + [ ] Ctx.set_z_index() + [ ] Sort command buffer on insertion [ ] Standardize element handling, for example all buttons do almost the same thing, so write a lot of boiler plate and reuse it [x] The id combination in gen_id() uses an intger division, which is costly, use another combination function that is non-linear and doesn't use division [ ] Animations, somehow @@ -33,12 +35,19 @@ to maintain focus until mouse release (fix scroll bars) [ ] Subdivide modules into ugui::ug for exported functions and ugui::core for internal use functions (used to create widgets) [ ] The render loop RAPES the gpu, valve pls fix +[ ] The way the element structures are implemented wastes a lot of memory since + each struct Elem, struct Cmd, etc. is as big as the largest element. It would + be better to use a different allcation strategy. +[ ] Add a way to handle time events like double clicks ## Layout [ ] Text reflow [x] Flexbox [ ] Center elements to the row/column +[ ] Text wrapping +[ ] Consider a multi-pass recursive approach to layout (like https://github.com/nicbarker/clay) + instead of the curren multi-frame approach. ## Input @@ -51,15 +60,16 @@ to maintain focus until mouse release (fix scroll bars) ## Commands [x] rect commads should have: -_ border width -_ border radius + - border width + - border radius [x] add a command to update an atlas [ ] New window command, useful for popups -## Atlases +## Atlas [ ] Add an interface to create, destroy, update and get atlases based on their ids [ ] Implement multiple font atlases +[ ] Pixel format conversion ## Fonts @@ -79,3 +89,21 @@ _ border radius [ ] Icon Buttons [x] Switch [x] Checkbox +[ ] Selectable text box + +## Main / exaple + +[ ] Create maps from ids to textures and images instead of hardcoding them + + +## API + +[ ] Introduce a Layout structure that specifies the positioning of elements inside + a Div element. This would allow specifying alignment, maximum and minimum sizing + margins between children, etc. + This is different from style which is applied per-element. +[ ] Remove Ids for element that don't need them. Such elements are button, toggles, + and all elements which do not have internal data that has to be cached and/or + queried by the user for later use. This allows for smaller caches and in general + reduces some load, since most of the stuff is recomputed for every frame. + diff --git a/lib/schrift.c3l/libschrift.c3 b/lib/schrift.c3l/libschrift.c3 index dde82a5..699d296 100644 --- a/lib/schrift.c3l/libschrift.c3 +++ b/lib/schrift.c3l/libschrift.c3 @@ -1,8 +1,8 @@ module schrift; -def SftFont = void*; -def SftUChar = uint; -def SftGlyph = uint; +alias SftFont = void*; +alias SftUChar = uint; +alias SftGlyph = uint; const int SFT_DOWNWARD_Y = 0x01; diff --git a/src/cache.c3 b/src/cache.c3 index fac5cf2..130ffd1 100644 --- a/src/cache.c3 +++ b/src/cache.c3 @@ -1,4 +1,4 @@ -module cache(); +module cache{Key, Value, SIZE}; /* LRU Cache * The cache uses a pool (array) to store all the elements, each element has @@ -14,12 +14,13 @@ module cache(); // happens at the same time import std::core::mem; +import std::core::mem::allocator; import std::collections::bitset; import std::collections::map; -def BitArr = bitset::BitSet() @private; -def IdTable = map::HashMap() @private; -def IdTableEntry = map::Entry() @private; +alias BitArr = bitset::BitSet{SIZE}; +alias IdTable = map::HashMap{Key, usz}; +alias IdTableEntry = map::Entry{Key, usz}; const usz CACHE_NCYCLES = (usz)(SIZE * 2.0/3.0); @@ -42,9 +43,9 @@ macro Cache.cycle(&cache) @private { } } -fn void! Cache.init(&cache) +fn void? Cache.init(&cache) { - cache.table.new_init(capacity: SIZE); + cache.table.init(allocator::heap(), capacity: SIZE); // FIXME: this shit is SLOW foreach (idx, bit : cache.used) { cache.used[idx] = false; } foreach (idx, bit : cache.present) { cache.present[idx] = false; } @@ -57,7 +58,7 @@ fn void Cache.free(&cache) (void)mem::free(cache.pool); } -fn Value*! Cache.search(&cache, Key id) +fn Value*? Cache.search(&cache, Key id) { // get_entry() faults on miss IdTableEntry* entry = cache.table.get_entry(id)!; @@ -65,14 +66,14 @@ fn Value*! Cache.search(&cache, Key id) /* MISS, wrong key */ if (entry.key != id) { cache.table.remove(id)!; - return SearchResult.MISSING?; + return NOT_FOUND?; } /* MISS, the data is not valid (not present) */ if (!cache.present[entry.value]) { // if the data is not present but it is still in the table, remove it cache.table.remove(id)!; - return SearchResult.MISSING?; + return NOT_FOUND?; } /* HIT, set as recently used */ @@ -82,7 +83,7 @@ fn Value*! Cache.search(&cache, Key id) fn void Cache.remove(&cache, Key id) { - IdTableEntry*! entry = cache.table.get_entry(id); + IdTableEntry*? entry = cache.table.get_entry(id); if (catch entry) { return; } @@ -105,7 +106,7 @@ fn usz Cache.get_free_spot(&cache) @private 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; @@ -122,17 +123,17 @@ fn Value*! Cache.insert_at(&cache, Value *g, Key id, usz index) @private } // Insert an element in the cache, returns the index -fn Value*! Cache.insert_new(&cache, Value* g, Key id) +fn Value*? Cache.insert_new(&cache, Value* g, Key id) { usz index = cache.get_free_spot(); return cache.insert_at(g, id, index); } -fn Value*! Cache.get_or_insert(&cache, Value* g, Key id, bool *is_new = null) +fn Value*? Cache.get_or_insert(&cache, Value* g, Key id, bool *is_new = null) { - Value*! c = cache.search(id); + Value*? c = cache.search(id); if (catch e = c) { - if (e != SearchResult.MISSING) { + if (e != NOT_FOUND) { return e?; } else { // if the element is new (inserted) set the is_new flag diff --git a/src/fifo.c3 b/src/fifo.c3 index dbc50f0..b4c49d4 100644 --- a/src/fifo.c3 +++ b/src/fifo.c3 @@ -1,11 +1,8 @@ -module fifo(); +module fifo{Type}; import std::core::mem; -fault FifoErr { - FULL, - EMPTY, -} +faultdef FULL, EMPTY; // TODO: specify the allocator @@ -15,7 +12,7 @@ struct Fifo { usz count; } -fn void! Fifo.init(&fifo, usz size) +fn void? Fifo.init(&fifo, usz size) { fifo.arr = mem::new_array(Type, size); fifo.out = 0; @@ -27,20 +24,20 @@ fn void Fifo.free(&fifo) (void)mem::free(fifo.arr); } -fn void! Fifo.enqueue(&fifo, Type *elem) +fn void? Fifo.enqueue(&fifo, Type *elem) { if (fifo.count >= fifo.arr.len) { - return FifoErr.FULL?; + return FULL?; } usz in = (fifo.out + fifo.count) % fifo.arr.len; fifo.arr[in] = *elem; fifo.count++; } -fn Type*! Fifo.dequeue(&fifo) +fn Type*? Fifo.dequeue(&fifo) { if (fifo.count == 0) { - return FifoErr.EMPTY?; + return EMPTY?; } Type *ret = &fifo.arr[fifo.out]; fifo.count--; diff --git a/src/main.c3 b/src/main.c3 index 7b710fd..e0c1737 100644 --- a/src/main.c3 +++ b/src/main.c3 @@ -7,7 +7,7 @@ import std::time; import std::collections::ringbuffer; import std::core::string; -def Times = ringbuffer::RingBuffer(); +alias Times = ringbuffer::RingBuffer{time::NanoDuration[128]}; fn void Times.print_stats(×) { @@ -40,7 +40,7 @@ fn TimeStats Times.get_stats(×) } avg = (NanoDuration)((ulong)avg/128.0); - return TimeStats{.min = min, .max = max, .avg = avg}; + return {.min = min, .max = max, .avg = avg}; } @@ -115,12 +115,12 @@ void main() macro rl::Color ugui::Color.conv(color) { - return rl::Color{.r = color.r, .g = color.g, .b = color.b, .a = color.a}; + return {.r = color.r, .g = color.g, .b = color.b, .a = color.a}; } macro rl::Rectangle Rect.conv(rect) { - return rl::Rectangle{.x = rect.x, .y = rect.y, .width = rect.w, .height = rect.h}; + return {.x = rect.x, .y = rect.y, .width = rect.w, .height = rect.h}; } fn int main(String[] args) @@ -169,11 +169,13 @@ fn int main(String[] args) mod.lctrl = rl::isKeyDown(rl::KEY_LEFT_CONTROL); ui.input_mod_keys(mod); + /* for (rl::KeyboardKey key; (key = (KeyboardKey)rl::getKeyPressed()) != 0;) { ZString kname = rl::getKeyName(key); if (kname == null) continue; ui.input_text_unicode(kname.str_view()); } + */ /* Start Input Handling */ @@ -203,29 +205,29 @@ fn int main(String[] args) if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) break; - ui.div_begin("main", Rect{.w=-100})!!; - {| + ui.div_begin("main", {.w=-100})!!; + { ui.layout_set_column()!!; - if (ui.button("button0", Rect{0,0,30,30}, toggle)!!.mouse_press) { + if (ui.button("button0", {0,0,30,30}, toggle)!!.mouse_press) { io::printn("press button0"); toggle = !toggle; } //ui.layout_next_column()!!; - if (ui.button("button1", Rect{0,0,30,30})!!.mouse_press) { + if (ui.button("button1", {0,0,30,30})!!.mouse_press) { io::printn("press button1"); } //ui.layout_next_column()!!; - if (ui.button("button2", Rect{0,0,30,30})!!.mouse_release) { + if (ui.button("button2", {0,0,30,30})!!.mouse_release) { io::printn("release button2"); } ui.layout_set_row()!!; ui.layout_next_row()!!; static float rf, gf, bf, af; - ui.slider_ver("slider_r", Rect{0,0,30,100}, &rf)!!; - ui.slider_ver("slider_g", Rect{0,0,30,100}, &gf)!!; - ui.slider_ver("slider_b", Rect{0,0,30,100}, &bf)!!; - ui.slider_ver("slider_a", Rect{0,0,30,100}, &af)!!; + ui.slider_ver("slider_r", {0,0,30,100}, &rf)!!; + ui.slider_ver("slider_g", {0,0,30,100}, &gf)!!; + ui.slider_ver("slider_b", {0,0,30,100}, &bf)!!; + ui.slider_ver("slider_a", {0,0,30,100}, &af)!!; ui.layout_next_column()!!; ui.text_unbounded("text1", "Ciao Mamma\nAbilità ⚡\n'\udb80\udd2c'")!!; @@ -235,8 +237,8 @@ fn int main(String[] args) ui.layout_next_row()!!; static bool check; - ui.checkbox("check1", "", Point{}, &check, "tick")!!; - ui.toggle("toggle1", "", Point{}, &toggle)!!; + ui.checkbox("check1", "", {}, &check, "tick")!!; + ui.toggle("toggle1", "", {}, &toggle)!!; /* ui.layout_set_column()!!; @@ -248,33 +250,33 @@ fn int main(String[] args) ui.layout_next_row()!!; ui.button_label(" E ")!!; */ - |}; + }; ui.draw_sprite("sprite1", "tux")!!; ui.div_end()!!; ui.div_begin("second", ugui::DIV_FILL, scroll_x: true, scroll_y: true)!!; - {| + { ui.layout_set_column()!!; static float slider2 = 0.5; - if (ui.slider_ver("slider", Rect{0,0,30,100}, &slider2)!!.update) { + if (ui.slider_ver("slider", {0,0,30,100}, &slider2)!!.update) { io::printfn("other slider: %f", slider2); } - ui.button("button0", Rect{0,0,50,50})!!; - ui.button("button1", Rect{0,0,50,50})!!; - ui.button("button2", Rect{0,0,50,50})!!; - ui.button("button3", Rect{0,0,50,50})!!; + ui.button("button0", {0,0,50,50})!!; + ui.button("button1", {0,0,50,50})!!; + ui.button("button2", {0,0,50,50})!!; + ui.button("button3", {0,0,50,50})!!; if (toggle) { - ui.button("button4", Rect{0,0,50,50})!!; - ui.button("button5", Rect{0,0,50,50})!!; - ui.button("button6", Rect{0,0,50,50})!!; - ui.button("button7", Rect{0,0,50,50})!!; + ui.button("button4", {0,0,50,50})!!; + ui.button("button5", {0,0,50,50})!!; + ui.button("button6", {0,0,50,50})!!; + ui.button("button7", {0,0,50,50})!!; } ui.layout_next_column()!!; ui.layout_set_row()!!; static float f1, f2; - ui.slider_hor("hs1", Rect{0,0,100,30}, &f1)!!; - ui.slider_hor("hs2", Rect{0,0,100,30}, &f2)!!; - |}; + ui.slider_hor("hs1", {0,0,100,30}, &f1)!!; + ui.slider_hor("hs2", {0,0,100,30}, &f2)!!; + }; ui.div_end()!!; // Timings counter @@ -282,12 +284,12 @@ fn int main(String[] args) TimeStats uts = ui_times.get_stats(); ui.layout_set_floating()!!; - ui.div_begin("fps", Rect{0, ui.height-100, -300, 100})!!; - {| + ui.div_begin("fps", {0, ui.height-100, -300, 100})!!; + { ui.layout_set_column()!!; ui.text_unbounded("draw times", string::tformat("ui avg: %s\ndraw avg: %s\nTOT: %s", uts.avg, dts.avg, uts.avg+dts.avg))!!; ui.text_unbounded("ui text input", (String)ui.input.keyboard.text[..])!!; - |}; + }; ui.div_end()!!; ui.frame_end()!!; @@ -341,15 +343,15 @@ fn int main(String[] args) rl::endShaderMode(); } else if (cmd.sprite.texture_id == sprite_id) { // FIXME: THIS CODE IS SHIT, REAL DOO DOO - {| + { if (cmd.sprite.type == SpriteType.SPRITE_MSDF) { rl::beginShaderMode(msdf_shader); defer rl::endShaderMode(); - rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), rl::Vector2{0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv()); + rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), {0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv()); } else { - rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), rl::Vector2{0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv()); + rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), {0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv()); } - |}; + }; } else { io::printfn("unknown texture id: %d", cmd.sprite.texture_id); } diff --git a/src/ugui_atlas.c3 b/src/ugui_atlas.c3 index 8745fd7..7f3a40b 100644 --- a/src/ugui_atlas.c3 +++ b/src/ugui_atlas.c3 @@ -2,10 +2,7 @@ module ugui; import std::io; -fault UgAtlasError { - CANNOT_PLACE, - INVALID_TYPE, -} +faultdef CANNOT_PLACE, INVALID_TYPE; enum AtlasType { ATLAS_GRAYSCALE, @@ -33,7 +30,39 @@ macro usz AtlasType.bpp(type) } } -fn void! Atlas.new(&atlas, Id id, AtlasType type, ushort width, ushort height) +macro typeid AtlasType.underlying(type) +{ + switch (type) { + case ATLAS_GRAYSCALE: return char; + case ATLAS_R8G8B8A8: return uint; + } +} + +/* +// FIXME: in and out types are not always known at compile time +macro @pixel_convert(p, AtlasType $in, AtlasType $out) +{ + $if $in == $out: + return p; + $else + $switch + $case $in == ATLAS_R8G8B8A8 && $out == ATLAS_GRAYSCALE: + var r = ((p >> 0) & 0xff); + var g = ((p >> 8) & 0xff); + var b = ((p >> 16) & 0xff); + var a = ((p >> 24) & 0xff); + if (a == 0) return (char)0; + return (ATLAS_GRAYSCALE.underlying())(((float)r+g+b) / 3.0f); + $case $in == ATLAS_GRAYSCALE && $out == ATLAS_R8G8B8A8: + var x = (char)(p/3.0); + return (ATLAS_R8G8B8A8.underlying())(x|(x<<8)|(x<<16)|(255<<24)); + $default: $error "Unimplemented pixel format conversion"; + $endswitch + $endif +} +*/ + +fn void? Atlas.new(&atlas, Id id, AtlasType type, ushort width, ushort height) { atlas.id = id; atlas.type = type; @@ -66,7 +95,7 @@ fn void Atlas.free(&atlas) // place a rect inside the atlas // uses a row first algorithm // TODO: use a skyline algorithm https://jvernay.fr/en/blog/skyline-2d-packer/implementation/ -fn Point! Atlas.place(&atlas, char[] pixels, ushort w, ushort h, ushort stride) +fn Point? Atlas.place(&atlas, char[] pixels, ushort w, ushort h, ushort stride) { Point p; @@ -79,7 +108,7 @@ fn Point! Atlas.place(&atlas, char[] pixels, ushort w, ushort h, ushort stride) if (atlas.row.x + w <= atlas.width && atlas.row.y + h <= atlas.height) { p = atlas.row; } else { - return UgAtlasError.CANNOT_PLACE?; + return CANNOT_PLACE?; } } diff --git a/src/ugui_button.c3 b/src/ugui_button.c3 index cd76206..f9582bc 100644 --- a/src/ugui_button.c3 +++ b/src/ugui_button.c3 @@ -10,7 +10,7 @@ struct ElemButton { // draw a button, return the events on that button // FIXME: "state" should be renamed "active" to toggle between an usable button and // an inactive (greyed-out) button -fn ElemEvents! Ctx.button(&ctx, String label, Rect size, bool state = false) +fn ElemEvents? Ctx.button(&ctx, String label, Rect size, bool state = false) { Id id = ctx.gen_id(label)!; @@ -22,14 +22,14 @@ fn ElemEvents! Ctx.button(&ctx, String label, Rect size, bool state = false) if (elem.flags.is_new) { elem.type = ETYPE_BUTTON; } else if (elem.type != ETYPE_BUTTON) { - return UgError.WRONG_ELEMENT_TYPE?; + return WRONG_ELEMENT_TYPE?; } elem.bounds = ctx.position_element(parent, size, true); // 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 ElemEvents{}; } + if (elem.bounds.is_null()) { return {}; } Color col = 0x0000ffffu.to_rgba(); elem.events = ctx.get_elem_events(elem); @@ -45,7 +45,7 @@ fn ElemEvents! Ctx.button(&ctx, String label, Rect size, bool state = false) return elem.events; } -fn ElemEvents! Ctx.button_label(&ctx, String label, Rect size = Rect{0,0,short.max,short.max}, bool state = false) +fn ElemEvents? Ctx.button_label(&ctx, String label, Rect size = {0,0,short.max,short.max}, bool state = false) { Id id = ctx.gen_id(label)!; @@ -60,11 +60,11 @@ fn ElemEvents! Ctx.button_label(&ctx, String label, Rect size = Rect{0,0,short.m 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(Rect{0,0,10,10}); + Rect btn_size = text_size.add({0,0,10,10}); // 2. Layout elem.bounds = ctx.position_element(parent, btn_size, true); - if (elem.bounds.is_null()) { return ElemEvents{}; } + if (elem.bounds.is_null()) { return {}; } Color col = 0x0000ffffu.to_rgba(); elem.events = ctx.get_elem_events(elem); @@ -86,9 +86,50 @@ fn ElemEvents! Ctx.button_label(&ctx, String label, Rect size = Rect{0,0,short.m return elem.events; } +fn ElemEvents? Ctx.button_icon(&ctx, String label, String icon, String on_icon = "", bool state = false) +{ + 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)!; + + if (elem.flags.is_new) { + elem.type = ETYPE_BUTTON; + } else if (elem.type != ETYPE_BUTTON) { + return WRONG_ELEMENT_TYPE?; + } + + 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()); + + elem.bounds = ctx.position_element(parent, max_size, true); + + // 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 {}; } + + Color col = 0x0000ffffu.to_rgba(); + elem.events = ctx.get_elem_events(elem); + + Id tex_id = ctx.sprite_atlas.id; + if (state && on_icon != "") { + ctx.push_sprite(elem.bounds, on_sprite.uv(), tex_id, type: on_sprite.type)!; + } else { + ctx.push_sprite(elem.bounds, def_sprite.uv(), tex_id, type: def_sprite.type)!; + } + + // Draw the button + ctx.push_rect(elem.bounds, col, do_border: true, do_radius: true)!; + + return elem.events; +} + // FIXME: this should be inside the style const ushort DEFAULT_CHECKBOX_SIZE = 16; -fn void! Ctx.checkbox(&ctx, String label, String description, Point off, bool* state, String tick_sprite = {}) +fn void? Ctx.checkbox(&ctx, String label, String description, Point off, bool* state, String tick_sprite = {}) { Id id = ctx.gen_id(label)!; @@ -102,7 +143,7 @@ fn void! Ctx.checkbox(&ctx, String label, String description, Point off, bool* s if (elem.flags.is_new) { elem.type = ETYPE_BUTTON; } else if (elem.type != ETYPE_BUTTON) { - return UgError.WRONG_ELEMENT_TYPE?; + return WRONG_ELEMENT_TYPE?; } Rect size = {off.x, off.y, DEFAULT_CHECKBOX_SIZE, DEFAULT_CHECKBOX_SIZE}; @@ -116,7 +157,7 @@ fn void! Ctx.checkbox(&ctx, String label, String description, Point off, bool* s if (elem.events.mouse_hover && elem.events.mouse_release) *state = !(*state); Color col; - if (tick_sprite != String{}) { + if (tick_sprite != {}) { col = ctx.style.bgcolor; ctx.push_rect(elem.bounds, col, do_border: true, do_radius: true)!; if (*state) { @@ -135,7 +176,7 @@ fn void! Ctx.checkbox(&ctx, String label, String description, Point off, bool* s // FIXME: this should be inside the style const short DEFAULT_SWITCH_SIZE = 16; -fn void! Ctx.toggle(&ctx, String label, String description, Point off, bool* state) +fn void? Ctx.toggle(&ctx, String label, String description, Point off, bool* state) { Id id = ctx.gen_id(label)!; @@ -149,7 +190,7 @@ fn void! Ctx.toggle(&ctx, String label, String description, Point off, bool* sta if (elem.flags.is_new) { elem.type = ETYPE_BUTTON; } else if (elem.type != ETYPE_BUTTON) { - return UgError.WRONG_ELEMENT_TYPE?; + return WRONG_ELEMENT_TYPE?; } Rect size = {off.x, off.y, DEFAULT_SWITCH_SIZE*2, DEFAULT_SWITCH_SIZE}; @@ -171,6 +212,6 @@ fn void! Ctx.toggle(&ctx, String label, String description, Point off, bool* sta // Draw the button // FIXME: THIS IS SHIT ctx.push_rect(elem.bounds, ctx.style.bgcolor, do_border: true, do_radius: true)!; - Rect t = elem.bounds.add(Rect{*state ? (DEFAULT_SWITCH_SIZE+3) : +3, +3, -DEFAULT_SWITCH_SIZE-6, -6}); + Rect t = elem.bounds.add({*state ? (DEFAULT_SWITCH_SIZE+3) : +3, +3, -DEFAULT_SWITCH_SIZE-6, -6}); ctx.push_rect(t, col, do_border: false, do_radius: true)!; } diff --git a/src/ugui_cmd.c3 b/src/ugui_cmd.c3 index 9524dc3..eee78d4 100644 --- a/src/ugui_cmd.c3 +++ b/src/ugui_cmd.c3 @@ -68,7 +68,7 @@ macro Ctx.push_cmd(&ctx, Cmd *cmd) // FIXME: is this really the best solution? // "rect" is the bounding box of the element, which includes the border and the padding (so not just the content) -fn void! Ctx.push_rect(&ctx, Rect rect, Color color, bool do_border = false, bool do_padding = false, bool do_radius = false) +fn void? Ctx.push_rect(&ctx, Rect rect, Color color, bool do_border = false, bool do_padding = false, bool do_radius = false) { Rect border = ctx.style.border; Rect padding = ctx.style.padding; @@ -101,7 +101,7 @@ fn void! Ctx.push_rect(&ctx, Rect rect, Color color, bool do_border = false, boo } // TODO: add texture id -fn void! Ctx.push_sprite(&ctx, Rect bounds, Rect texture, Id texture_id, Color hue = 0xffffffffu.to_rgba(), SpriteType type = SPRITE_NORMAL) +fn void? Ctx.push_sprite(&ctx, Rect bounds, Rect texture, Id texture_id, Color hue = 0xffffffffu.to_rgba(), SpriteType type = SPRITE_NORMAL) { Cmd cmd = { .type = CMD_SPRITE, @@ -114,7 +114,7 @@ fn void! Ctx.push_sprite(&ctx, Rect bounds, Rect texture, Id texture_id, Color h ctx.push_cmd(&cmd)!; } -fn void! Ctx.push_string(&ctx, Rect bounds, String text, Color hue = 0xffffffffu.to_rgba()) +fn void? Ctx.push_string(&ctx, Rect bounds, String text, Color hue = 0xffffffffu.to_rgba()) { if (text.len == 0) { return; @@ -163,10 +163,10 @@ fn void! Ctx.push_string(&ctx, Rect bounds, String text, Color hue = 0xffffffffu } // FIXME: we never get here if an error was thrown before - ctx.push_scissor(Rect{})!; + ctx.push_scissor({})!; } -fn void! Ctx.push_update_atlas(&ctx, Atlas* atlas) +fn void? Ctx.push_update_atlas(&ctx, Atlas* atlas) { Cmd up = { .type = CMD_UPDATE_ATLAS, @@ -181,7 +181,7 @@ fn void! Ctx.push_update_atlas(&ctx, Atlas* atlas) ctx.push_cmd(&up)!; } -fn void! Ctx.push_scissor(&ctx, Rect rect) +fn void? Ctx.push_scissor(&ctx, Rect rect) { Cmd sc = { .type = CMD_SCISSOR, diff --git a/src/ugui_core.c3 b/src/ugui_core.c3 index c330efe..8c4cf75 100644 --- a/src/ugui_core.c3 +++ b/src/ugui_core.c3 @@ -9,7 +9,7 @@ import std::core::string; // element ids are just long ints -def Id = usz; +alias Id = usz; enum ElemType { ETYPE_NONE, @@ -54,20 +54,15 @@ struct Elem { // relationships between elements are stored in a tree, it stores just the ids -def IdTree = vtree::VTree() @private; +alias IdTree = vtree::VTree{Id}; // elements themselves are kept in a cache const uint MAX_ELEMENTS = 256; -def ElemCache = cache::Cache() @private; +alias ElemCache = cache::Cache{Id, Elem, MAX_ELEMENTS}; -def CmdQueue = fifo::Fifo(); +alias CmdQueue = fifo::Fifo{Cmd}; -fault UgError { - INVALID_SIZE, - EVENT_UNSUPPORTED, - UNEXPECTED_ELEMENT, - WRONG_ELEMENT_TYPE, -} +faultdef INVALID_SIZE, EVENT_UNSUPPORTED, UNEXPECTED_ELEMENT, WRONG_ELEMENT_TYPE; const Rect DIV_FILL = { .x = 0, .y = 0, .w = 0, .h = 0 }; @@ -128,7 +123,7 @@ struct Ctx { } // return a pointer to the parent of the current active div -fn Elem*! Ctx.get_parent(&ctx) +fn Elem*? Ctx.get_parent(&ctx) { Id parent_id = ctx.tree.get(ctx.active_div)!; return ctx.cache.search(parent_id); @@ -140,7 +135,7 @@ const uint GOLDEN_RATIO = 0x9E3779B9; // generate an id combining the hashes of the parent id and the label // with the Cantor pairing function -macro Id! Ctx.gen_id(&ctx, String label) +macro Id? Ctx.gen_id(&ctx, String label) { Id id1 = ctx.tree.get(ctx.active_div)!; Id id2 = label.hash(); @@ -153,7 +148,7 @@ macro Id! Ctx.gen_id(&ctx, String label) // get or push an element from the cache, return a pointer to it // resets all flags except is_new which is set accordingly -fn Elem*! Ctx.get_elem(&ctx, Id id) +fn Elem*? Ctx.get_elem(&ctx, Id id) { Elem empty_elem; bool is_new; @@ -170,10 +165,10 @@ fn Elem*! Ctx.get_elem(&ctx, Id id) // THIS HAS TO BE A MACRO SINCE IT RETURNS A POINTER TO A TEMPORARY VALUE macro Elem* Ctx.find_elem(&ctx, Id id) { - Elem*! elem; + Elem*? elem; elem = ctx.cache.search(id); if (catch elem) { - return &&Elem{}; + return &&(Elem){}; } return elem; } @@ -194,7 +189,7 @@ macro Ctx.get_elem_by_tree_idx(&ctx, isz idx) @private return ctx.cache.search(id); } -fn void! Ctx.init(&ctx) +fn void? Ctx.init(&ctx) { ctx.tree.init(MAX_ELEMENTS)!; defer catch { (void)ctx.tree.free(); } @@ -208,9 +203,9 @@ fn void! Ctx.init(&ctx) ctx.active_div = 0; // TODO: add style config - ctx.style.margin = Rect{2, 2, 2, 2}; - ctx.style.border = Rect{2, 2, 2, 2}; - ctx.style.padding = Rect{1, 1, 1, 1}; + ctx.style.margin = {2, 2, 2, 2}; + ctx.style.border = {2, 2, 2, 2}; + ctx.style.padding = {1, 1, 1, 1}; ctx.style.radius = 5; ctx.style.bgcolor = 0x282828ffu.to_rgba(); ctx.style.fgcolor = 0xfbf1c7ffu.to_rgba(); @@ -226,7 +221,7 @@ fn void Ctx.free(&ctx) (void)ctx.sprite_atlas.free(); } -fn void! Ctx.frame_begin(&ctx) +fn void? Ctx.frame_begin(&ctx) { // 2. Get the root element from the cache and update it @@ -268,7 +263,7 @@ fn void! Ctx.frame_begin(&ctx) // TODO: add a background color taken from a theme or config } -fn void! Ctx.frame_end(&ctx) +fn void? Ctx.frame_end(&ctx) { Elem* root = ctx.get_elem_by_tree_idx(0)!; root.div.layout = LAYOUT_ROW; diff --git a/src/ugui_div.c3 b/src/ugui_div.c3 index da175cf..bff69a3 100644 --- a/src/ugui_div.c3 +++ b/src/ugui_div.c3 @@ -28,7 +28,7 @@ struct ElemDiv { // if the width or height are negative the width or height will be calculated based on the children size // sort similar to a flexbox, and the minimum size is set by the negative of the width or height // FIXME: there is a bug if the size.w or size.h == -0 -fn void! Ctx.div_begin(&ctx, String label, Rect size, bool scroll_x = false, bool scroll_y = false) +fn void? Ctx.div_begin(&ctx, String label, Rect size, bool scroll_x = false, bool scroll_y = false) { Id id = ctx.gen_id(label)!; @@ -42,7 +42,7 @@ fn void! Ctx.div_begin(&ctx, String label, Rect size, bool scroll_x = false, boo if (elem.flags.is_new) { elem.type = ETYPE_DIV; } else if (elem.type != ETYPE_DIV) { - return UgError.WRONG_ELEMENT_TYPE?; + return WRONG_ELEMENT_TYPE?; } elem.div.scroll_x.enabled = scroll_x; elem.div.scroll_y.enabled = scroll_y; @@ -55,16 +55,16 @@ fn void! Ctx.div_begin(&ctx, String label, Rect size, bool scroll_x = false, boo .h = size.h < 0 ? max(elem.div.pcb.h, (short)-size.h) : size.h, }; elem.bounds = ctx.position_element(parent, wanted_size); - elem.div.children_bounds = Rect{}; + elem.div.children_bounds = {}; // update the ctx scissor ctx.div_scissor = elem.bounds; ctx.push_scissor(elem.bounds)!; // 4. Fill the div fields - elem.div.origin_c = Point{ + elem.div.origin_c = { .x = elem.bounds.x, - .y = elem.bounds.y, + .y = elem.bounds.y }; elem.div.origin_r = elem.div.origin_c; elem.div.layout = parent.div.layout; @@ -79,7 +79,7 @@ fn void! Ctx.div_begin(&ctx, String label, Rect size, bool scroll_x = false, boo // TODO: check resizeable } -fn void! Ctx.div_end(&ctx) +fn void? Ctx.div_end(&ctx) { // swap the children bounds Elem* parent = ctx.get_parent()!; diff --git a/src/ugui_font.c3 b/src/ugui_font.c3 index 535432d..5afe033 100644 --- a/src/ugui_font.c3 +++ b/src/ugui_font.c3 @@ -4,13 +4,15 @@ import schrift; import grapheme; import std::collections::map; import std::core::mem; +import std::core::mem::allocator; import std::io; import std::ascii; // unicode code point, different type for a different hash -def Codepoint = uint; +alias Codepoint = uint; +//macro uint Codepoint.hash(self) => ((uint)self).hash(); /* width and height of a glyph contain the kering advance * (u,v) @@ -40,14 +42,9 @@ struct Glyph { } const uint FONT_CACHED = 255; -def GlyphTable = map::HashMap() @private; +alias GlyphTable = map::HashMap{Codepoint, Glyph}; -fault UgFontError { - TTF_LOAD_FAILED, - MISSING_GLYPH, - BAD_GLYPH_METRICS, - RENDER_ERROR, -} +faultdef TTF_LOAD_FAILED, MISSING_GLYPH, BAD_GLYPH_METRICS, RENDER_ERROR; struct Font { schrift::Sft sft; @@ -61,14 +58,14 @@ struct Font { bool should_update; // should send update_atlas command, resets at frame_end() } -fn void! Font.load(&font, String name, ZString path, uint height, float scale) +fn void? Font.load(&font, String name, ZString path, uint height, float scale) { - font.table.new_init(capacity: FONT_CACHED); + font.table.init(allocator::heap(), capacity: FONT_CACHED); font.id = name.hash(); font.size = height*scale; - font.sft = schrift::Sft{ + font.sft = { .xScale = (double)font.size, .yScale = (double)font.size, .flags = schrift::SFT_DOWNWARD_Y, @@ -77,7 +74,7 @@ fn void! Font.load(&font, String name, ZString path, uint height, float scale) font.sft.font = schrift::loadfile(path); if (font.sft.font == null) { font.table.free(); - return UgFontError.TTF_LOAD_FAILED?; + return TTF_LOAD_FAILED?; } schrift::SftLMetrics lmetrics; @@ -98,13 +95,13 @@ fn void! Font.load(&font, String name, ZString path, uint height, float scale) } } -fn Glyph*! Font.get_glyph(&font, Codepoint code) +fn Glyph*? Font.get_glyph(&font, Codepoint code) { - Glyph*! gp; + Glyph*? gp; gp = font.table.get_ref(code); if (catch excuse = gp) { - if (excuse != SearchResult.MISSING) { + if (excuse != NOT_FOUND) { return excuse?; } } else { @@ -117,12 +114,12 @@ fn Glyph*! Font.get_glyph(&font, Codepoint code) schrift::SftGlyph gid; schrift::SftGMetrics gmtx; - if (schrift::lookup(&font.sft, code, &gid) < 0) { - return UgFontError.MISSING_GLYPH?; + if (schrift::lookup(&font.sft, (SftUChar)code, &gid) < 0) { + return MISSING_GLYPH?; } if (schrift::gmetrics(&font.sft, gid, &gmtx) < 0) { - return UgFontError.BAD_GLYPH_METRICS?; + return BAD_GLYPH_METRICS?; } schrift::SftImage img = { @@ -132,7 +129,7 @@ fn Glyph*! Font.get_glyph(&font, Codepoint code) char[] pixels = mem::new_array(char, (usz)img.width * img.height); img.pixels = pixels; if (schrift::render(&font.sft, gid, img) < 0) { - return UgFontError.RENDER_ERROR?; + return RENDER_ERROR?; } glyph.code = code; @@ -163,7 +160,7 @@ fn void Font.free(&font) schrift::freefont(font.sft.font); } -fn void! Ctx.load_font(&ctx, String name, ZString path, uint height, float scale = 1.0) +fn void? Ctx.load_font(&ctx, String name, ZString path, uint height, float scale = 1.0) { return ctx.font.load(name, path, height, scale); } @@ -174,7 +171,7 @@ fn void! Ctx.load_font(&ctx, String name, ZString path, uint height, float scale fn Codepoint str_to_codepoint(char[] str, usz* off) { Codepoint cp; - isz b = grapheme::decode_utf8(str, str.len, &cp); + isz b = grapheme::decode_utf8(str, str.len, (uint*)&cp); if (b == 0 || b > str.len) { return 0; } @@ -182,7 +179,7 @@ fn Codepoint str_to_codepoint(char[] str, usz* off) return cp; } -fn Rect! Ctx.get_text_bounds(&ctx, String text) +fn Rect? Ctx.get_text_bounds(&ctx, String text) { Rect text_bounds; short line_height = (short)ctx.font.ascender - (short)ctx.font.descender; @@ -219,7 +216,7 @@ fn Point Ctx.center_text(&ctx, Rect text_bounds, Rect bounds) short dw = bounds.w - text_bounds.w; short dh = bounds.h - text_bounds.h; - return Point{.x = dw/2, .y = dh/2}; + return {.x = dw/2, .y = dh/2}; } // TODO: check if the font is present in the context diff --git a/src/ugui_input.c3 b/src/ugui_input.c3 index 1911d9f..abad51c 100644 --- a/src/ugui_input.c3 +++ b/src/ugui_input.c3 @@ -45,10 +45,10 @@ 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_NONE = ModKeys{}; +const ModKeys KMOD_NONE = {}; const ModKeys KMOD_ANY = (ModKeys)(ModKeys.inner.max); -const MouseButtons BTN_NONE = MouseButtons{}; +const MouseButtons BTN_NONE = {}; const MouseButtons BTN_ANY = (MouseButtons)(MouseButtons.inner.max); const MouseButtons BTN_LEFT = {.btn_left = true}; const MouseButtons BTN_MIDDLE = {.btn_middle = true}; @@ -72,10 +72,10 @@ fn bool Ctx.check_key_combo(&ctx, ModKeys mod, String keys) } // Window size was changed -fn void! Ctx.input_window_size(&ctx, short width, short height) +fn void? Ctx.input_window_size(&ctx, short width, short height) { if (width <= 0 || height <= 0) { - return UgError.INVALID_SIZE?; + return INVALID_SIZE?; } ctx.input.events.resize = ctx.width != width || ctx.height != height; ctx.width = width; diff --git a/src/ugui_layout.c3 b/src/ugui_layout.c3 index 3a2ac9a..feba610 100644 --- a/src/ugui_layout.c3 +++ b/src/ugui_layout.c3 @@ -7,71 +7,71 @@ enum Layout { LAYOUT_ABSOLUTE, } -fn void! Ctx.layout_set_row(&ctx) +fn void? Ctx.layout_set_row(&ctx) { Id parent_id = ctx.tree.get(ctx.active_div)!; Elem *parent = ctx.cache.search(parent_id)!; if (parent.type != ETYPE_DIV) { // what? - return UgError.UNEXPECTED_ELEMENT?; + return UNEXPECTED_ELEMENT?; } parent.div.layout = LAYOUT_ROW; } -fn void! Ctx.layout_set_column(&ctx) +fn void? Ctx.layout_set_column(&ctx) { Id parent_id = ctx.tree.get(ctx.active_div)!; Elem *parent = ctx.cache.search(parent_id)!; if (parent.type != ETYPE_DIV) { // what? - return UgError.UNEXPECTED_ELEMENT?; + return UNEXPECTED_ELEMENT?; } parent.div.layout = LAYOUT_COLUMN; } -fn void! Ctx.layout_set_floating(&ctx) +fn void? Ctx.layout_set_floating(&ctx) { Id parent_id = ctx.tree.get(ctx.active_div)!; Elem *parent = ctx.cache.search(parent_id)!; if (parent.type != ETYPE_DIV) { // what? - return UgError.UNEXPECTED_ELEMENT?; + return UNEXPECTED_ELEMENT?; } parent.div.layout = LAYOUT_FLOATING; } -fn void! Ctx.layout_next_row(&ctx) +fn void? Ctx.layout_next_row(&ctx) { Id parent_id = ctx.tree.get(ctx.active_div)!; Elem *parent = ctx.cache.search(parent_id)!; if (parent.type != ETYPE_DIV) { - return UgError.UNEXPECTED_ELEMENT?; + return UNEXPECTED_ELEMENT?; } - parent.div.origin_r = Point{ + parent.div.origin_r = { .x = parent.bounds.x, .y = parent.div.children_bounds.bottom_right().y, }; parent.div.origin_c = parent.div.origin_r; } -fn void! Ctx.layout_next_column(&ctx) +fn void? Ctx.layout_next_column(&ctx) { Id parent_id = ctx.tree.get(ctx.active_div)!; Elem *parent = ctx.cache.search(parent_id)!; if (parent.type != ETYPE_DIV) { - return UgError.UNEXPECTED_ELEMENT?; + return UNEXPECTED_ELEMENT?; } - parent.div.origin_c = Point{ + parent.div.origin_c = { .x = parent.div.children_bounds.bottom_right().x, .y = parent.bounds.y, }; @@ -124,7 +124,7 @@ fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false) case LAYOUT_ABSOLUTE: // absolute position, this is a no-op, return the rect return rect; default: // error - return Rect{}; + return {}; } // 2. Compute the parent's view @@ -166,11 +166,11 @@ fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false) child_occupied = child_occupied.grow(rect.size()); // 4. Update the parent's origin - div.origin_r = Point{ + div.origin_r = { .x = child_occupied.bottom_right().x, .y = origin.y, }; - div.origin_c = Point{ + div.origin_c = { .x = origin.x, .y = child_occupied.bottom_right().y, }; @@ -191,6 +191,6 @@ fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false) if (child_placement.collides(parent_view)) { return child_placement.off(parent.get_view_off().neg()); } else { - return Rect{}; + return {}; } } diff --git a/src/ugui_shapes.c3 b/src/ugui_shapes.c3 index 39a782c..ebef525 100644 --- a/src/ugui_shapes.c3 +++ b/src/ugui_shapes.c3 @@ -19,7 +19,7 @@ macro bool Rect.contains(Rect a, Rect b) // returns the intersection of a and b macro Rect Rect.intersection(Rect a, Rect b) { - return Rect{ + return { .x = (short)max(a.x, b.x), .y = (short)max(a.y, b.y), .w = (short)min(a.x+a.w, b.x+b.w) - (short)max(a.x, b.x), @@ -39,7 +39,7 @@ macro bool Rect.is_null(Rect r) => r.x == 0 && r.y == 0 && r.x == 0 && r.w == 0; // returns the element-wise addition of r1 and r2 macro Rect Rect.add(Rect r1, Rect r2) { - return Rect{ + return { .x = r1.x + r2.x, .y = r1.y + r2.y, .w = r1.w + r2.w, @@ -50,7 +50,7 @@ macro Rect Rect.add(Rect r1, Rect r2) // returns the element-wise subtraction of r1 and r2 macro Rect Rect.sub(Rect r1, Rect r2) { - return Rect{ + return { .x = r1.x - r2.x, .y = r1.y - r2.y, .w = r1.w - r2.w, @@ -61,7 +61,7 @@ macro Rect Rect.sub(Rect r1, Rect r2) // returns the element-wise multiplication of r1 and r2 macro Rect Rect.mul(Rect r1, Rect r2) { - return Rect{ + return { .x = r1.x * r2.x, .y = r1.y * r2.y, .w = r1.w * r2.w, @@ -71,7 +71,7 @@ macro Rect Rect.mul(Rect r1, Rect r2) macro Point Rect.position(Rect r) { - return Point{ + return { .x = r.x, .y = r.y, }; @@ -79,7 +79,7 @@ macro Point Rect.position(Rect r) macro Point Rect.size(Rect r) { - return Point{ + return { .x = r.w, .y = r.h, }; @@ -87,7 +87,7 @@ macro Point Rect.size(Rect r) macro Rect Rect.max(Rect a, Rect b) { - return Rect{ + return { .x = max(a.x, b.x), .y = max(a.y, b.y), .w = max(a.w, b.w), @@ -97,7 +97,7 @@ macro Rect Rect.max(Rect a, Rect b) macro Rect Rect.min(Rect a, Rect b) { - return Rect{ + return { .x = min(a.x, b.x), .y = min(a.y, b.y), .w = min(a.w, b.w), @@ -108,7 +108,7 @@ macro Rect Rect.min(Rect a, Rect b) // Offset a rect by a point macro Rect Rect.off(Rect r, Point p) { - return Rect{ + return { .x = r.x + p.x, .y = r.y + p.y, .w = r.w, @@ -119,7 +119,7 @@ macro Rect Rect.off(Rect r, Point p) // Resize a rect width and height macro Rect Rect.grow(Rect r, Point p) { - return Rect{ + return { .x = r.x, .y = r.y, .w = r.w + p.x, @@ -130,7 +130,7 @@ macro Rect Rect.grow(Rect r, Point p) // Return the bottom-right corner of a rectangle macro Point Rect.bottom_right(Rect r) { - return Point{ + return { .x = r.x + r.w, .y = r.y + r.h, }; @@ -153,7 +153,7 @@ macro bool Point.in_rect(Point p, Rect r) macro Point Point.add(Point a, Point b) { - return Point{ + return { .x = a.x + b.x, .y = a.y + b.y, }; @@ -161,17 +161,17 @@ macro Point Point.add(Point a, Point b) macro Point Point.sub(Point a, Point b) { - return Point{ + return { .x = a.x - b.x, .y = a.y - b.y, }; } -macro Point Point.neg(Point p) => Point{-p.x, -p.y}; +macro Point Point.neg(Point p) => {-p.x, -p.y}; macro Point Point.max(Point a, Point b) { - return Point{ + return { .x = max(a.x, b.x), .y = max(a.y, b.y), }; @@ -179,7 +179,7 @@ macro Point Point.max(Point a, Point b) macro Point Point.min(Point a, Point b) { - return Point{ + return { .x = min(a.x, b.x), .y = min(a.y, b.y), }; @@ -195,7 +195,7 @@ struct Color{ macro Color uint.to_rgba(uint u) { - return Color{ + return { .r = (char)((u >> 24) & 0xff), .g = (char)((u >> 16) & 0xff), .b = (char)((u >> 8) & 0xff), diff --git a/src/ugui_slider.c3 b/src/ugui_slider.c3 index 1d76376..cdcccf8 100644 --- a/src/ugui_slider.c3 +++ b/src/ugui_slider.c3 @@ -16,7 +16,7 @@ struct ElemSlider { <* @require value != null *> -fn ElemEvents! Ctx.slider_hor(&ctx, +fn ElemEvents? Ctx.slider_hor(&ctx, String label, Rect size, float* value, @@ -35,7 +35,7 @@ fn ElemEvents! Ctx.slider_hor(&ctx, if (elem.flags.is_new) { elem.type = ETYPE_SLIDER; } else if (elem.type != ETYPE_SLIDER) { - return UgError.WRONG_ELEMENT_TYPE?; + return WRONG_ELEMENT_TYPE?; } // 2. Layout @@ -79,7 +79,7 @@ fn ElemEvents! Ctx.slider_hor(&ctx, * | | * +-+ */ -fn ElemEvents! Ctx.slider_ver(&ctx, +fn ElemEvents? Ctx.slider_ver(&ctx, String label, Rect size, float* value, @@ -98,7 +98,7 @@ fn ElemEvents! Ctx.slider_ver(&ctx, if (elem.flags.is_new) { elem.type = ETYPE_SLIDER; } else if (elem.type != ETYPE_SLIDER) { - return UgError.WRONG_ELEMENT_TYPE?; + return WRONG_ELEMENT_TYPE?; } // 2. Layout diff --git a/src/ugui_sprite.c3 b/src/ugui_sprite.c3 index b4a83f2..1f94b27 100644 --- a/src/ugui_sprite.c3 +++ b/src/ugui_sprite.c3 @@ -1,5 +1,6 @@ module ugui; +import std::core::mem::allocator; import std::collections::map; import std::io; import mqoi; @@ -20,7 +21,7 @@ struct Sprite { ushort w, h; } -def SpriteMap = map::HashMap(); +alias SpriteMap = map::HashMap{Id, Sprite}; struct SpriteAtlas { Id id; @@ -34,20 +35,20 @@ struct ElemSprite { } // name: some examples are "icons" or "images" -fn void! SpriteAtlas.init(&this, String name, AtlasType type, ushort width, ushort height) +fn void? SpriteAtlas.init(&this, String name, AtlasType type, ushort width, ushort height) { // FIXME: for now only R8G8B8A8 format is supported if (type != ATLAS_R8G8B8A8) { - return UgAtlasError.INVALID_TYPE?; + return INVALID_TYPE?; } this.id = name.hash(); this.atlas.new(this.id, AtlasType.ATLAS_R8G8B8A8, width, height)!; - this.sprites.new_init(capacity: SRITES_PER_ATLAS); + this.sprites.init(allocator::heap(), capacity: SRITES_PER_ATLAS); this.should_update = false; } -fn void! SpriteAtlas.free(&this) +fn void? SpriteAtlas.free(&this) { this.atlas.free(); this.sprites.free(); @@ -55,7 +56,7 @@ fn void! SpriteAtlas.free(&this) // FIXME: this should throw an error when a different pixel format than the atlas' is used // or convert from the source's pixel format to the atlas' -fn Sprite*! SpriteAtlas.insert(&this, String name, SpriteType type, char[] pixels, ushort w, ushort h, ushort stride) +fn Sprite*? SpriteAtlas.insert(&this, String name, SpriteType type, char[] pixels, ushort w, ushort h, ushort stride) { Sprite s; s.id = name.hash(); @@ -70,18 +71,21 @@ fn Sprite*! SpriteAtlas.insert(&this, String name, SpriteType type, char[] pixel return this.sprites.get_ref(s.id); } -fn Sprite*! SpriteAtlas.get(&this, String name) +fn Sprite*? SpriteAtlas.get(&this, String name) { Id id = name.hash(); return this.sprites.get_ref(id); } -fn Sprite*! SpriteAtlas.get_by_id(&this, Id id) +fn Sprite*? SpriteAtlas.get_by_id(&this, Id id) { return this.sprites.get_ref(id); } -fn void! Ctx.sprite_atlas_create(&ctx, String name, AtlasType type, ushort w, ushort h) +macro Rect Sprite.rect(s) => {0,0,s.w,s.h}; +macro Rect Sprite.uv(s) => {s.u,s.v,s.w,s.h}; + +fn void? Ctx.sprite_atlas_create(&ctx, String name, AtlasType type, ushort w, ushort h) { ctx.sprite_atlas.init(name, type, w, h)!; } @@ -91,12 +95,12 @@ fn Id Ctx.get_sprite_atlas_id(&ctx, String name) return name.hash(); } -fn void! Ctx.import_sprite_memory(&ctx, String name, char[] pixels, ushort w, ushort h, ushort stride, SpriteType type = SPRITE_NORMAL) +fn void? Ctx.import_sprite_memory(&ctx, String name, char[] pixels, ushort w, ushort h, ushort stride, SpriteType type = SPRITE_NORMAL) { ctx.sprite_atlas.insert(name, type, pixels, w, h, stride)!; } -fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType type = SPRITE_NORMAL) +fn void? Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType type = SPRITE_NORMAL) { mqoi::Desc image_desc; uint w, h; @@ -108,7 +112,7 @@ fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType t mqoi::desc_push(&image_desc, file.read_byte()!); } if (mqoi::desc_verify(&image_desc, &w, &h) != 0) { - return IoError.FILE_NOT_VALID?; + return io::FILE_NOT_VALID?; } mqoi::Dec dec; @@ -123,7 +127,7 @@ fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType t mqoi::dec_push(&dec, file.read_byte()!); while ((px = mqoi::dec_pop(&dec)) != null) { - pixels[idx..idx+3] = px.value; + pixels[idx..idx+3] = px.value[..]; idx += 4; } } @@ -131,7 +135,7 @@ fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType t ctx.sprite_atlas.insert(name, type, pixels, (ushort)w, (ushort)h, (ushort)w)!; } -fn void! Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0}) +fn void? Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0}) { Id id = ctx.gen_id(label)!; @@ -143,7 +147,7 @@ fn void! Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0}) if (elem.flags.is_new) { elem.type = ETYPE_SPRITE; } else if (elem.type != ETYPE_SPRITE) { - return UgError.WRONG_ELEMENT_TYPE?; + return WRONG_ELEMENT_TYPE?; } Sprite* sprite = ctx.sprite_atlas.get(name)!; @@ -163,11 +167,9 @@ fn void! Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0}) return ctx.push_sprite(elem.bounds, uv, tex_id)!; } -fn void! Ctx.draw_sprite_raw(&ctx, String name, Rect bounds) +fn void? Ctx.draw_sprite_raw(&ctx, String name, Rect bounds) { Sprite* sprite = ctx.sprite_atlas.get(name)!; - Rect uv = { sprite.u, sprite.v, sprite.w, sprite.h }; - //Rect bounds = Rect{ 0, 0, sprite.w, sprite.h }.off(off); Id tex_id = ctx.sprite_atlas.id; - return ctx.push_sprite(bounds, uv, tex_id, type: sprite.type)!; + return ctx.push_sprite(bounds, sprite.uv(), tex_id, type: sprite.type)!; } diff --git a/src/ugui_text.c3 b/src/ugui_text.c3 index 18628e0..dea4562 100644 --- a/src/ugui_text.c3 +++ b/src/ugui_text.c3 @@ -6,7 +6,7 @@ struct ElemText { char* str; } -fn void! Ctx.text_unbounded(&ctx, String label, String text) +fn void? Ctx.text_unbounded(&ctx, String label, String text) { Id id = ctx.gen_id(label)!; diff --git a/src/vtree.c3 b/src/vtree.c3 index 8cdcff7..0a1e851 100644 --- a/src/vtree.c3 +++ b/src/vtree.c3 @@ -1,4 +1,4 @@ -module vtree(); +module vtree{ElemType}; import std::core::mem; import std::io; @@ -9,13 +9,7 @@ struct VTree { isz[] refs, ordered_refs; } -fault VTreeError { - CANNOT_SHRINK, - INVALID_REFERENCE, - TREE_FULL, - REFERENCE_NOT_PRESENT, - INVALID_ARGUMENT, -} +faultdef CANNOT_SHRINK, INVALID_REFERENCE, TREE_FULL, REFERENCE_NOT_PRESENT, INVALID_ARGUMENT; macro VTree.ref_is_valid(&tree, isz ref) { return (ref >= 0 && ref < tree.refs.len); } macro VTree.ref_is_present(&tree, isz ref) { return tree.refs[ref] >= 0; } @@ -27,11 +21,11 @@ macro @zero() $if $assignable(0, ElemType): return 0; $else - return ElemType{}; + return {}; $endif } -fn void! VTree.init(&tree, usz size) +fn void? VTree.init(&tree, usz size) { tree.vector = mem::new_array(ElemType, size); defer catch { (void)mem::free(tree.vector); } @@ -74,7 +68,7 @@ fn void VTree.pack(&tree) tree.vector[free_spot] = tree.vector[i]; tree.refs[free_spot] = tree.refs[i]; - tree.vector[i] = @zero(); + tree.vector[i] = {}; tree.refs[i] = -1; // and move all references @@ -90,18 +84,18 @@ fn void VTree.pack(&tree) } } -fn void! VTree.resize(&tree, usz newsize) +fn void? VTree.resize(&tree, usz newsize) { // return error when shrinking with too many elements if (newsize < tree.elements) { - return VTreeError.CANNOT_SHRINK?; + return CANNOT_SHRINK?; } // pack the vector when shrinking to avoid data loss if ((int)newsize < tree.size()) { // FIXME: packing destroys all references to elements of vec // so shrinking may cause dangling pointers - return VTreeError.CANNOT_SHRINK?; + return CANNOT_SHRINK?; } usz old_size = tree.size(); @@ -122,22 +116,22 @@ fn void! VTree.resize(&tree, usz newsize) } // add an element to the tree, return it's ref -fn isz! VTree.add(&tree, ElemType elem, isz parent) +fn isz? VTree.add(&tree, ElemType elem, isz parent) { // invalid parent if (!tree.ref_is_valid(parent)) { - return VTreeError.INVALID_REFERENCE?; + return INVALID_REFERENCE?; } // no space left if (tree.elements >= tree.size()) { - return VTreeError.TREE_FULL?; + return TREE_FULL?; } // check if the parent exists // if there are no elements in the tree the first add will set the root if (!tree.ref_is_present(parent) && tree.elements != 0) { - return VTreeError.REFERENCE_NOT_PRESENT?; + return REFERENCE_NOT_PRESENT?; } // get the first free spot @@ -149,7 +143,7 @@ fn isz! VTree.add(&tree, ElemType elem, isz parent) } } if (free_spot < 0) { - return VTreeError.TREE_FULL?; + return TREE_FULL?; } // finally add the element @@ -162,10 +156,10 @@ fn isz! VTree.add(&tree, ElemType elem, isz parent) // prune the tree starting from the ref // returns the number of pruned elements -fn usz! VTree.prune(&tree, isz ref) +fn usz? VTree.prune(&tree, isz ref) { if (!tree.ref_is_valid(ref)) { - return VTreeError.INVALID_REFERENCE?; + return INVALID_REFERENCE?; } if (!tree.ref_is_present(ref)) { @@ -196,10 +190,10 @@ fn usz VTree.nuke(&tree) } // find the size of the subtree starting from ref -fn usz! VTree.subtree_size(&tree, isz ref) +fn usz? VTree.subtree_size(&tree, isz ref) { if (!tree.ref_is_valid(ref)) { - return VTreeError.INVALID_REFERENCE?; + return INVALID_REFERENCE?; } if (!tree.ref_is_present(ref)) { @@ -218,20 +212,20 @@ fn usz! VTree.subtree_size(&tree, isz ref) } // iterate through the first level children, use a cursor like strtok_r -fn isz! VTree.children_it(&tree, isz parent, isz *cursor) +fn isz? VTree.children_it(&tree, isz parent, isz *cursor) { if (cursor == null) { - return VTreeError.INVALID_ARGUMENT?; + return INVALID_ARGUMENT?; } // if the cursor is out of bounds then we are done for sure if (!tree.ref_is_valid(*cursor)) { - return VTreeError.INVALID_REFERENCE?; + return INVALID_REFERENCE?; } // same for the parent, if it's invalid it can't have children if (!tree.ref_is_valid(parent) || !tree.ref_is_present(parent)) { - return VTreeError.INVALID_REFERENCE?; + return INVALID_REFERENCE?; } // find the first child, update the cursor and return the ref @@ -256,10 +250,10 @@ fn isz! VTree.children_it(&tree, isz parent, isz *cursor) * / \ [6] * [4] [5] */ -fn isz! VTree.level_order_it(&tree, isz ref, isz *cursor) +fn isz? VTree.level_order_it(&tree, isz ref, isz *cursor) { if (cursor == null) { - return VTreeError.INVALID_ARGUMENT?; + return INVALID_ARGUMENT?; } isz[] queue = tree.ordered_refs; @@ -303,27 +297,27 @@ fn isz! VTree.level_order_it(&tree, isz ref, isz *cursor) return -1; } -fn isz! VTree.parentof(&tree, isz ref) +fn isz? VTree.parentof(&tree, isz ref) { if (!tree.ref_is_valid(ref)) { - return VTreeError.INVALID_REFERENCE?; + return INVALID_REFERENCE?; } if (!tree.ref_is_present(ref)) { - return VTreeError.REFERENCE_NOT_PRESENT?; + return REFERENCE_NOT_PRESENT?; } return tree.refs[ref]; } -fn ElemType! VTree.get(&tree, isz ref) +fn ElemType? VTree.get(&tree, isz ref) { if (!tree.ref_is_valid(ref)) { - return VTreeError.INVALID_REFERENCE?; + return INVALID_REFERENCE?; } if (!tree.ref_is_present(ref)) { - return VTreeError.REFERENCE_NOT_PRESENT?; + return REFERENCE_NOT_PRESENT?; } return tree.vector[ref];