From 7e64879f7875d4496708a4124f2d093c06c8a9b1 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 23 Jul 2025 13:30:42 -0400 Subject: [PATCH 01/25] Forgot to set the position on rectangle so just added the center helper. --- src/guecs/sfml/components.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index 6dbb221..e52097f 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -97,6 +97,8 @@ namespace guecs { shape->setFillColor(color); shape->setOutlineColor(border_color); shape->setOutlineThickness(border_px); + + sfml_center_helper(shape, cell, padding); } void Rectangle::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { From 4c019048d0c5232d23b174873acbba833512cf82 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 23 Jul 2025 13:49:16 -0400 Subject: [PATCH 02/25] Making Icons a thing again since it's convenient. --- demos/clicker_game.cpp | 2 +- include/guecs/sfml/components.hpp | 14 ++++++++++++++ include/guecs/ui.hpp | 1 + src/guecs/ui.cpp | 19 +++++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/demos/clicker_game.cpp b/demos/clicker_game.cpp index 17ffbb1..c76080a 100644 --- a/demos/clicker_game.cpp +++ b/demos/clicker_game.cpp @@ -92,7 +92,7 @@ struct ClickerUI { auto id = $gui.entity(name); if(name != "clicker") { $gui.set(id, {}); - $gui.set(id, { "clicker_treat_bone" }); + $gui.set(id, { "clicker_treat_bone" }); $gui.set(id, { [&](auto, auto) { handle_button(Event::A_BUTTON); } }); diff --git a/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index dc82033..74d12bc 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -36,6 +36,20 @@ namespace guecs { void render(sf::RenderWindow& window, sf::Shader *shader_ptr); }; + struct Icon : public Sprite { + template + Icon(Args... args) : Sprite(args...) + { + stretch = false; + is_icon = true; + } + + Icon() { + stretch = false; + is_icon = true; + }; + }; + struct Rectangle { int padding = THEME.PADDING; sf::Color color = THEME.FILL_COLOR; diff --git a/include/guecs/ui.hpp b/include/guecs/ui.hpp index d66b063..1b309b5 100644 --- a/include/guecs/ui.hpp +++ b/include/guecs/ui.hpp @@ -218,6 +218,7 @@ namespace guecs { sf::Shader* find_shader(Entity ent, bool is_shape); void show_sprite(const string& region, const string& sprite_name); + void show_icon(const string& region, const string& sprite_name); void show_text(const string& region, const wstring& content); void show_label(const string& region, const wstring& content); }; diff --git a/src/guecs/ui.cpp b/src/guecs/ui.cpp index 5e8d498..396a277 100644 --- a/src/guecs/ui.cpp +++ b/src/guecs/ui.cpp @@ -89,6 +89,10 @@ namespace guecs { query([&](auto, auto &cell, auto &sprite) { sprite.init(cell); }); + + query([&](auto, auto &cell, auto &icon) { + icon.init(cell); + }); } void UI::debug_layout(sf::RenderWindow& window) { @@ -134,6 +138,11 @@ namespace guecs { sprite.render(window, shader_ptr); }); + query([&](auto ent, auto& icon) { + auto shader_ptr = find_shader(ent, false); + icon.render(window, shader_ptr); + }); + query([&](auto ent, auto& text) { auto shader_ptr = find_shader(ent, false); text.render(window, shader_ptr); @@ -188,6 +197,16 @@ namespace guecs { } } + void UI::show_icon(const string& region, const string& sprite_name) { + auto ent = entity(region); + + if(auto sprite = get_if(ent)) { + sprite->update(sprite_name); + } else { + set_init(ent, {sprite_name}); + } + } + void UI::show_text(const string& region, const wstring& content) { auto ent = entity(region); From 5d0d8b16fc0716bdf96a96238c98a18559e97696 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Thu, 14 Aug 2025 12:35:25 -0400 Subject: [PATCH 03/25] Removed hover on guecs::UI::mouse and now use a generic 16 bit modifier bitset. Also finally fixed Clickable so it just a simple callback with only the modifiers. --- demos/calc.cpp | 8 ++++---- demos/clicker_game.cpp | 8 ++++---- include/guecs/ui.hpp | 21 ++++++++++++--------- meson.build | 2 +- src/guecs/ui.cpp | 31 ++++++++----------------------- 5 files changed, 29 insertions(+), 41 deletions(-) diff --git a/demos/calc.cpp b/demos/calc.cpp index c96ffea..8f38517 100644 --- a/demos/calc.cpp +++ b/demos/calc.cpp @@ -169,8 +169,8 @@ struct CalculatorUI { $gui.set(id, { label }); wchar_t op = label[0]; $gui.set(id, { - [&, op](auto, auto) { handle_button(op); } - }); + [&, op](auto) { handle_button(op); } + }); } } @@ -182,8 +182,8 @@ struct CalculatorUI { // $gui.debug_layout(window); } - void mouse(float x, float y, bool hover) { - $gui.mouse(x, y, hover); + void mouse(float x, float y, guecs::Modifiers mods) { + $gui.mouse(x, y, mods); } void handle_button(wchar_t op) { diff --git a/demos/clicker_game.cpp b/demos/clicker_game.cpp index c76080a..c5e68b6 100644 --- a/demos/clicker_game.cpp +++ b/demos/clicker_game.cpp @@ -94,7 +94,7 @@ struct ClickerUI { $gui.set(id, {}); $gui.set(id, { "clicker_treat_bone" }); $gui.set(id, { - [&](auto, auto) { handle_button(Event::A_BUTTON); } + [&](auto) { handle_button(Event::A_BUTTON); } }); } } @@ -103,7 +103,7 @@ struct ClickerUI { $gui.set($clicker, {"clicker_the_dog"}); $gui.set($clicker, {"clicker_bark"}); $gui.set($clicker, { - [&](auto, auto) { handle_button(Event::CLICKER); } + [&](auto) { handle_button(Event::CLICKER); } }); // custom components need to be initialized manually @@ -126,8 +126,8 @@ struct ClickerUI { // $gui.debug_layout(window); } - void mouse(float x, float y, bool hover) { - $gui.mouse(x, y, hover); + void mouse(float x, float y, guecs::Modifiers mods) { + $gui.mouse(x, y, mods); } void handle_button(Event ev) { diff --git a/include/guecs/ui.hpp b/include/guecs/ui.hpp index 1b309b5..2d30731 100644 --- a/include/guecs/ui.hpp +++ b/include/guecs/ui.hpp @@ -10,6 +10,7 @@ #include "guecs/theme.hpp" #include "guecs/sfml/components.hpp" #include +#include namespace guecs { using std::shared_ptr, std::wstring, std::string; @@ -24,22 +25,25 @@ namespace guecs { std::queue free_indices; }; + enum class ModBit { + hover=0, + left=1, + right=2 + }; + + using Modifiers = std::bitset<16>; + struct Clickable { /* This is actually called by UI::mouse and passed the entity ID of the * button pressed so you can interact with it in the event handler. */ - std::function action; - }; - - struct ActionData { - std::any data; + std::function action; }; struct CellName { string name; }; - class UI { public: Entity MAIN = 0; @@ -69,9 +73,8 @@ namespace guecs { void init(); void render(sf::RenderWindow& window); - bool mouse(float x, float y, bool hover); - void click_on(const string& name, bool required=false); - void click_on(Entity slot_id); + bool mouse(float x, float y, Modifiers mods); + void click_on(Entity slot_id, Modifiers mods); void debug_layout(sf::RenderWindow& window); Entity entity() { return ++entity_count; } diff --git a/meson.build b/meson.build index a9f4b00..57f07ec 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,7 @@ # HEY BUG: when you have a . spec in a LEL it doesn't work on text project('lel-guecs', 'cpp', - version: '0.5.0', + version: '0.6.0', default_options: [ 'cpp_std=c++20', 'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1', diff --git a/src/guecs/ui.cpp b/src/guecs/ui.cpp index 396a277..2d6c4d3 100644 --- a/src/guecs/ui.cpp +++ b/src/guecs/ui.cpp @@ -149,9 +149,9 @@ namespace guecs { }); } - bool UI::mouse(float x, float y, bool hover) { + bool UI::mouse(float x, float y, Modifiers mods) { int action_count = 0; - // BUG: Is Parser::hit useful? + bool hover = mods.test(size_t(ModBit::hover)); query([&](auto ent, auto& cell) { if((x >= cell.x && x <= cell.x + cell.w) && @@ -169,11 +169,11 @@ namespace guecs { if(hover) return; - click_on(ent); + click_on(ent, mods); action_count++; } else { - do_if(ent, [hover](auto& effect) { + do_if(ent, [](auto& effect) { effect.stop(); }); @@ -220,25 +220,10 @@ namespace guecs { } } - void UI::click_on(const string& name, bool required) { - auto ent = entity(name); - - if(required) { - assert(has(ent) && - "click_on required '{}' to exist but it doesn't"); - } - - click_on(ent); - } - - void UI::click_on(Entity ent) { - if(auto clicked = get_if(ent)) { - if(auto action_data = get_if(ent)) { - clicked->action(ent, action_data->data); - } else { - clicked->action(ent, {}); - } - } + void UI::click_on(Entity gui_id, Modifiers mods) { + if(auto to_click = get_if(gui_id)) { + to_click->action(mods); + }; } wstring to_wstring(const string& str) { From 4b4992185fa2c27d5d93898001c35c08c057c0ea Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Thu, 14 Aug 2025 12:54:16 -0400 Subject: [PATCH 04/25] Needed a default of no Modifiers to click_on since that's a very common operation. Also a constant to represent that. --- include/guecs/ui.hpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/include/guecs/ui.hpp b/include/guecs/ui.hpp index 2d30731..af61965 100644 --- a/include/guecs/ui.hpp +++ b/include/guecs/ui.hpp @@ -26,13 +26,16 @@ namespace guecs { }; enum class ModBit { - hover=0, - left=1, - right=2 + NONE=0, + hover=1, + left=2, + right=3 }; using Modifiers = std::bitset<16>; + constexpr const Modifiers NO_MODS{size_t(ModBit::NONE)}; + struct Clickable { /* This is actually called by UI::mouse and passed the entity ID of the * button pressed so you can interact with it in the event handler. @@ -74,7 +77,7 @@ namespace guecs { void init(); void render(sf::RenderWindow& window); bool mouse(float x, float y, Modifiers mods); - void click_on(Entity slot_id, Modifiers mods); + void click_on(Entity slot_id, Modifiers mods=NO_MODS); void debug_layout(sf::RenderWindow& window); Entity entity() { return ++entity_count; } From 41d568ab256e34e4dabeac2518970f78639e4fdf Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Thu, 14 Aug 2025 13:01:27 -0400 Subject: [PATCH 05/25] Bring back the convenience method to click on a cell by name. --- include/guecs/ui.hpp | 1 + src/guecs/ui.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/guecs/ui.hpp b/include/guecs/ui.hpp index af61965..faf8327 100644 --- a/include/guecs/ui.hpp +++ b/include/guecs/ui.hpp @@ -77,6 +77,7 @@ namespace guecs { void init(); void render(sf::RenderWindow& window); bool mouse(float x, float y, Modifiers mods); + void click_on(const std::string& name, Modifiers mods=NO_MODS); void click_on(Entity slot_id, Modifiers mods=NO_MODS); void debug_layout(sf::RenderWindow& window); diff --git a/src/guecs/ui.cpp b/src/guecs/ui.cpp index 2d6c4d3..1cbaffd 100644 --- a/src/guecs/ui.cpp +++ b/src/guecs/ui.cpp @@ -220,6 +220,10 @@ namespace guecs { } } + void UI::click_on(const std::string& name, Modifiers mods) { + click_on(entity(name), mods); + } + void UI::click_on(Entity gui_id, Modifiers mods) { if(auto to_click = get_if(gui_id)) { to_click->action(mods); From 070244269e44cdf4591af410e9d5e831575c6d86 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Thu, 14 Aug 2025 13:59:36 -0400 Subject: [PATCH 06/25] class enums are just jank when trying to work with a bitset. --- include/guecs/ui.hpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/include/guecs/ui.hpp b/include/guecs/ui.hpp index faf8327..bb1f7b7 100644 --- a/include/guecs/ui.hpp +++ b/include/guecs/ui.hpp @@ -25,16 +25,18 @@ namespace guecs { std::queue free_indices; }; - enum class ModBit { - NONE=0, - hover=1, - left=2, - right=3 - }; + namespace ModBit { + enum { + NONE=0, + hover=1, + left=2, + right=3 + }; + } using Modifiers = std::bitset<16>; - constexpr const Modifiers NO_MODS{size_t(ModBit::NONE)}; + constexpr const Modifiers NO_MODS{0}; struct Clickable { /* This is actually called by UI::mouse and passed the entity ID of the From e8c178db6f09f8b3be75da93fcbde48b7d823378 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Fri, 31 Oct 2025 11:05:29 -0400 Subject: [PATCH 07/25] Changing from RenderWindow to RenderTarget so the GUI can be rendered into a texture for zooming/saving. --- include/guecs/sfml/components.hpp | 10 +++++----- include/guecs/ui.hpp | 6 +++--- meson.build | 2 +- src/guecs/sfml/components.cpp | 10 +++++----- src/guecs/ui.cpp | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index 74d12bc..34022f3 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -21,7 +21,7 @@ namespace guecs { void init(lel::Cell &cell, shared_ptr font_ptr); void update(const std::wstring& new_content); - void render(sf::RenderWindow& window, sf::Shader *shader_ptr); + void render(sf::RenderTarget& window, sf::Shader *shader_ptr); }; struct Sprite { @@ -33,7 +33,7 @@ namespace guecs { void init(lel::Cell &cell); void update(const string& new_name); - void render(sf::RenderWindow& window, sf::Shader *shader_ptr); + void render(sf::RenderTarget& window, sf::Shader *shader_ptr); }; struct Icon : public Sprite { @@ -58,7 +58,7 @@ namespace guecs { shared_ptr shape = nullptr; void init(lel::Cell& cell); - void render(sf::RenderWindow& window, sf::Shader *shader_ptr); + void render(sf::RenderTarget& window, sf::Shader *shader_ptr); }; struct Meter { @@ -68,7 +68,7 @@ namespace guecs { void init(lel::Cell& cell); void init(lel::Cell& cell, Rectangle& bg); - void render(lel::Cell& cell, sf::RenderWindow& window, sf::Shader *shader_ptr); + void render(lel::Cell& cell, sf::RenderTarget& window, sf::Shader *shader_ptr); }; struct Effect { @@ -114,7 +114,7 @@ namespace guecs { Background() {} void init(); - void render(sf::RenderWindow& window); + void render(sf::RenderTarget& window); void set_color(sf::Color c); void set_sprite(const std::string& name, bool stretch=false); diff --git a/include/guecs/ui.hpp b/include/guecs/ui.hpp index bb1f7b7..b25d940 100644 --- a/include/guecs/ui.hpp +++ b/include/guecs/ui.hpp @@ -77,11 +77,11 @@ namespace guecs { } void init(); - void render(sf::RenderWindow& window); + void render(sf::RenderTarget& window); bool mouse(float x, float y, Modifiers mods); void click_on(const std::string& name, Modifiers mods=NO_MODS); void click_on(Entity slot_id, Modifiers mods=NO_MODS); - void debug_layout(sf::RenderWindow& window); + void debug_layout(sf::RenderTarget& window); Entity entity() { return ++entity_count; } @@ -220,7 +220,7 @@ namespace guecs { } template - void render_helper(sf::RenderWindow& window, T& target, sf::Shader *shader_ptr) { + void render_helper(sf::RenderTarget& window, T& target, sf::Shader *shader_ptr) { window.draw(*target, shader_ptr); } diff --git a/meson.build b/meson.build index 57f07ec..e05ce36 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,7 @@ # HEY BUG: when you have a . spec in a LEL it doesn't work on text project('lel-guecs', 'cpp', - version: '0.6.0', + version: '0.7.0', default_options: [ 'cpp_std=c++20', 'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1', diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index e52097f..45184dd 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -46,7 +46,7 @@ namespace guecs { text->setCharacterSize(size); } - void Text::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { + void Text::render(sf::RenderTarget& window, sf::Shader *shader_ptr) { window.draw(*text, shader_ptr); } @@ -87,7 +87,7 @@ namespace guecs { sfml_center_helper(sprite, cell, padding); } - void Sprite::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { + void Sprite::render(sf::RenderTarget& window, sf::Shader *shader_ptr) { window.draw(*sprite, shader_ptr); } @@ -101,7 +101,7 @@ namespace guecs { sfml_center_helper(shape, cell, padding); } - void Rectangle::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { + void Rectangle::render(sf::RenderTarget& window, sf::Shader *shader_ptr) { window.draw(*shape, shader_ptr); } @@ -114,7 +114,7 @@ namespace guecs { bar.init(cell); } - void Meter::render(lel::Cell& cell, sf::RenderWindow& window, sf::Shader *shader_ptr) { + void Meter::render(lel::Cell& cell, sf::RenderTarget& window, sf::Shader *shader_ptr) { float level = std::clamp(percent, 0.0f, 1.0f) * float(cell.w); // ZED: this 6 is a border width, make it a thing bar.shape->setSize({std::max(level, 0.0f), float(cell.h - 6)}); @@ -170,7 +170,7 @@ namespace guecs { } } - void Background::render(sf::RenderWindow& window) { + void Background::render(sf::RenderTarget& window) { if(shape != nullptr) { window.draw(*shape); } diff --git a/src/guecs/ui.cpp b/src/guecs/ui.cpp index 1cbaffd..75ff28a 100644 --- a/src/guecs/ui.cpp +++ b/src/guecs/ui.cpp @@ -95,7 +95,7 @@ namespace guecs { }); } - void UI::debug_layout(sf::RenderWindow& window) { + void UI::debug_layout(sf::RenderTarget& window) { query([&](const auto, auto &cell) { sf::RectangleShape rect{{float(cell.w), float(cell.h)}}; rect.setPosition({float(cell.x), float(cell.y)}); @@ -114,7 +114,7 @@ namespace guecs { } } - void UI::render(sf::RenderWindow& window) { + void UI::render(sf::RenderTarget& window) { if(auto bg = get_if(MAIN)) { bg->render(window); } From 3e9047177c4a677de2f1846d646a2e8c74e252b3 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Fri, 31 Oct 2025 11:06:54 -0400 Subject: [PATCH 08/25] Include the .wrap that I'm using. --- lel-guecs.wrap | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 lel-guecs.wrap diff --git a/lel-guecs.wrap b/lel-guecs.wrap new file mode 100644 index 0000000..8c799b5 --- /dev/null +++ b/lel-guecs.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory=lel-guecs-0.7.0 +url=https://git.learnjsthehardway.com/learn-code-the-hard-way/lel-guecs.git +revision=HEAD +depth=1 +method=meson + +[provide] +lel_guecs = lel_guecs_dep From 516f20124f2cf11b45a17bd34e5f569fd504f3fe Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 10 Mar 2026 23:01:15 -0400 Subject: [PATCH 09/25] Fix a few little bugs. --- include/guecs/sfml/backend.hpp | 1 + include/guecs/ui.hpp | 1 - src/guecs/sfml/components.cpp | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/guecs/sfml/backend.hpp b/include/guecs/sfml/backend.hpp index fc36f35..e4c2e9f 100644 --- a/include/guecs/sfml/backend.hpp +++ b/include/guecs/sfml/backend.hpp @@ -1,3 +1,4 @@ +#pragma once #include "guecs/ui.hpp" namespace sfml { diff --git a/include/guecs/ui.hpp b/include/guecs/ui.hpp index b25d940..c6c1fa0 100644 --- a/include/guecs/ui.hpp +++ b/include/guecs/ui.hpp @@ -229,7 +229,6 @@ namespace guecs { void show_sprite(const string& region, const string& sprite_name); void show_icon(const string& region, const string& sprite_name); void show_text(const string& region, const wstring& content); - void show_label(const string& region, const wstring& content); }; diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index 45184dd..6e04f14 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -137,7 +137,6 @@ namespace guecs { assert(w > 0.0f && "Background.w must be > 0.0f. Forgot $gui.$parser?"); assert(h > 0.0f && "Background.h must be > 0.0f. Forgot $gui.$parser?"); - std::cout << "x=" << x << " y=" << y << " w=" << w << " h=" << h << std::endl; sf::Vector2f size{float(w), float(h)}; if(shape == nullptr) { shape = make_shared(size); From e88179b78876b57adad8ba3c0187eb6ccc55e552 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 11 Mar 2026 07:39:50 -0400 Subject: [PATCH 10/25] Small mistake. Didn't maintain the texture pointer in the sprite so it could go out of scope and crash with use after free. --- include/guecs/sfml/components.hpp | 1 + meson.build | 4 +++- src/guecs/sfml/components.cpp | 6 ++++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index 34022f3..1b87999 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -30,6 +30,7 @@ namespace guecs { bool stretch = true; bool is_icon = false; std::shared_ptr sprite = nullptr; + std::shared_ptr texture = nullptr; void init(lel::Cell &cell); void update(const string& new_name); diff --git a/meson.build b/meson.build index e05ce36..d41a2ab 100644 --- a/meson.build +++ b/meson.build @@ -10,7 +10,9 @@ project('lel-guecs', 'cpp', ]) # use this for common options only for our executables -cpp_args=[] +cpp_args=[ + '-Wno-deprecated-declarations' + ] link_args=[] # these are passed as override_defaults exe_defaults = [ 'warning_level=2' ] diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index 6e04f14..7bb6f9c 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -59,7 +59,8 @@ namespace guecs { if(new_name != name) { name = new_name; auto sprite_texture = BACKEND->get_sprite(name); - sprite->setTexture(*sprite_texture.texture); + texture = sprite_texture.texture; + sprite->setTexture(*texture); sprite->setTextureRect({{0,0},sprite_texture.frame_size}); } } @@ -71,7 +72,8 @@ namespace guecs { auto bounds = sprite_texture.frame_size; sf::IntRect rect{{0,0}, bounds}; - sprite = make_shared(*sprite_texture.texture, rect); + texture = sprite_texture.texture; + sprite = make_shared(*texture, rect); if(stretch) { sprite->setScale({ From 31adf011efa062a52cf58cfad9190a9edd46d565 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 11 Mar 2026 07:56:05 -0400 Subject: [PATCH 11/25] Same problem with background. --- include/guecs/sfml/components.hpp | 1 + src/guecs/sfml/components.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index 1b87999..593f5f4 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -103,6 +103,7 @@ namespace guecs { sf::Color color=THEME.BG_COLOR; shared_ptr shape = nullptr; shared_ptr sprite = nullptr; + shared_ptr texture = nullptr; Background(lel::Parser& parser, sf::Color bg_color=THEME.BG_COLOR) : x(parser.grid_x), diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index 7bb6f9c..dba17b1 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -158,7 +158,8 @@ namespace guecs { auto sprite_texture = BACKEND->get_sprite(name); sf::IntRect rect{{0,0},sprite_texture.frame_size}; - sprite = make_shared(*sprite_texture.texture, rect); + texture = sprite_texture.texture; + sprite = make_shared(*texture, rect); sprite->setPosition({float(x), float(y)}); From aff0fb839caa928d57484fb24c787cb5f738970b Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 31 Mar 2026 13:04:11 -0400 Subject: [PATCH 12/25] Fix the build to work with new meson. --- meson.build | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index d41a2ab..9e3b5bc 100644 --- a/meson.build +++ b/meson.build @@ -3,9 +3,9 @@ # HEY BUG: when you have a . spec in a LEL it doesn't work on text project('lel-guecs', 'cpp', - version: '0.7.0', + version: '0.7.1', default_options: [ - 'cpp_std=c++20', + 'cpp_std=c++23', 'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1', ]) @@ -32,10 +32,6 @@ if build_machine.system() == 'windows' exe_defaults += ['werror=false'] elif build_machine.system() == 'darwin' - add_global_link_arguments( - language: 'cpp', - ) - opengl = dependency('OpenGL') corefoundation = dependency('CoreFoundation') carbon = dependency('Carbon') From 25e3935170e8cf71a93faa9d2cb4ba033bc38822 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sat, 4 Apr 2026 14:50:15 -0400 Subject: [PATCH 13/25] I need a way to ask if a cell exists for more dynamic layouts. --- include/guecs/ui.hpp | 1 + meson.build | 2 +- src/guecs/ui.cpp | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/guecs/ui.hpp b/include/guecs/ui.hpp index c6c1fa0..5d6ad73 100644 --- a/include/guecs/ui.hpp +++ b/include/guecs/ui.hpp @@ -68,6 +68,7 @@ namespace guecs { sf::Vector2f get_size(); void layout(const string& grid); Entity init_entity(const string& name); + bool contains(const string& name); Entity entity(const string& name); Entity entity(const string& name, int id); const std::string& name_for(Entity gui_id); diff --git a/meson.build b/meson.build index 9e3b5bc..4f0c701 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,7 @@ # HEY BUG: when you have a . spec in a LEL it doesn't work on text project('lel-guecs', 'cpp', - version: '0.7.1', + version: '0.7.2', default_options: [ 'cpp_std=c++23', 'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1', diff --git a/src/guecs/ui.cpp b/src/guecs/ui.cpp index 75ff28a..49810a9 100644 --- a/src/guecs/ui.cpp +++ b/src/guecs/ui.cpp @@ -51,6 +51,10 @@ namespace guecs { return $ents_name.at(gui_id); } + bool UI::contains(const string& name) { + return $name_ents.contains(name); + } + Entity UI::entity(const string& name) { assert($name_ents.contains(name) && "GUECS entity does not exist. Mispelled cell name?"); From 6aa1a877c9b080b2c47a3e68369731ff2e4ad9f9 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 8 Apr 2026 23:01:05 -0400 Subject: [PATCH 14/25] Brought the UIStack over from the previous game to use here. --- demos/multiscreen.cpp | 123 ++++++++++++++++++++++++++++++ include/guecs/sfml/components.hpp | 5 ++ include/guecs/ui.hpp | 3 + include/guecs/uistack.hpp | 97 +++++++++++++++++++++++ meson.build | 10 +++ src/guecs/sfml/components.cpp | 1 + src/guecs/ui.cpp | 2 + 7 files changed, 241 insertions(+) create mode 100644 demos/multiscreen.cpp create mode 100644 include/guecs/uistack.hpp diff --git a/demos/multiscreen.cpp b/demos/multiscreen.cpp new file mode 100644 index 0000000..8234d1c --- /dev/null +++ b/demos/multiscreen.cpp @@ -0,0 +1,123 @@ +#include "guecs/sfml/backend.hpp" +#include "guecs/sfml/components.hpp" +#include "guecs/uistack.hpp" +#include "guecs/ui.hpp" +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +struct TestScreen { + guecs::UI $gui{}; + + TestScreen(const std::string &layout) { + init(layout); + } + + void init(const std::string& layout) { + $gui.position(0, 0, 1280, 720); + $gui.layout(layout); + + for(auto& [name, cell] : $gui.cells()) { + auto gui_id = $gui.entity(name); + $gui.set(gui_id, {}); + $gui.set(gui_id, {}); + $gui.set(gui_id, {guecs::to_wstring(name)}); + $gui.set(gui_id, { + [&, gui_id, name](auto) { fmt::println("click! {}={}", name, gui_id); } + }); + } + + $gui.init(); + } + + void render(sf::RenderTarget& target) { + $gui.render(target); + } + + bool mouse(float x, float y, guecs::Modifiers mods) { + return $gui.mouse(x, y, mods); + } + + void update() { + // empty + } +}; + + +int main() { + try { + sfml::Backend backend; + guecs::init(&backend); + + sf::RenderWindow window(sf::VideoMode({1280, 720}), "Multi-Screen Tester"); + window.setVerticalSyncEnabled(true); + window.setFramerateLimit(60); + window.setPosition({0,0}); + + gui::UIStack stack; + auto test2 = std::make_shared("[test2|sonice]"); + auto test3 = std::make_shared("[test3|pointer]"); + + { + stack.add("test1", std::make_shared("[test1|hello]")); + stack.add("test2", test2); + stack.add("test3", test3); + } + + stack.set_active("test1"); + + while(window.isOpen()) { + while (const auto event = window.pollEvent()) { + if(event->is()) { + window.close(); + } + + if(const auto* mouse = event->getIf()) { + if(mouse->button == sf::Mouse::Button::Left) { + sf::Vector2f pos = window.mapPixelToCoords(mouse->position); + stack.mouse(pos.x, pos.y, false); + } + } else if(const auto* key = event->getIf()) { + using KEY = sf::Keyboard::Scan; + + switch(key->scancode) { + case KEY::Q: + return 0; + case KEY::N: { + bool good = stack.next(); + fmt::println("showing {} is {}", stack.active_name(), good); + } break; + case KEY::P: { + bool good = stack.prev(); + fmt::println("showing {} is {}", stack.active_name(), good); + } break; + case KEY::F: + stack.first(); + break; + case KEY::L: + stack.last(); + break; + default: + break; + } + } + } + + stack.update(); + stack.render(window); + window.display(); + + std::this_thread::sleep_for(100ms); + } + + } catch(const std::system_error& e) { + std::cout << "WARNING: On OSX you'll get this error on shutdown.\n"; + std::cout << "Caught system_error with code " + "[" << e.code() << "] meaning " + "[" << e.what() << "]\n"; + } +} diff --git a/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index 593f5f4..017e7dc 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -52,6 +52,7 @@ namespace guecs { }; struct Rectangle { + // BUG: confirm that padding is being used correctly here int padding = THEME.PADDING; sf::Color color = THEME.FILL_COLOR; sf::Color border_color = THEME.BORDER_COLOR; @@ -62,6 +63,9 @@ namespace guecs { void render(sf::RenderTarget& window, sf::Shader *shader_ptr); }; + // BUG: meter needs a backing rectangle of one color for the full bar, then + // another rectangle for the current setting. + // BUG: the meter will be past the end, probably a floating point error struct Meter { float percent = 1.0f; sf::Color color = THEME.BG_COLOR_DARK; @@ -95,6 +99,7 @@ namespace guecs { void stop(bool hover); }; + // BUG: it should be _way_ easier to set the background and change it struct Background { float x = 0.0f; float y = 0.0f; diff --git a/include/guecs/ui.hpp b/include/guecs/ui.hpp index 5d6ad73..164e2ba 100644 --- a/include/guecs/ui.hpp +++ b/include/guecs/ui.hpp @@ -60,6 +60,9 @@ namespace guecs { shared_ptr $font = nullptr; lel::Parser $parser; string $grid = ""; + // BUG: check for this to prevent bugs, or find a way to shape the API so you + // simply can't get it wrong + bool initialized = false; UI(); diff --git a/include/guecs/uistack.hpp b/include/guecs/uistack.hpp new file mode 100644 index 0000000..d451ea0 --- /dev/null +++ b/include/guecs/uistack.hpp @@ -0,0 +1,97 @@ +#include +#include +#include + +namespace gui { + template + struct UIStack { + using UIStackMap = std::flat_map>; + + UIStackMap screens{}; + bool visible = false; + + UIStackMap::iterator $current{screens.begin()}; + SCREEN_TYPE* $active = nullptr; + + void add(const std::string& name, std::shared_ptr screen) { + screens.insert_or_assign(name, screen); + update_active(screens.begin()); + } + + void update_active(const UIStackMap::iterator& itr) { + $current = itr; + $active = (*$current).second.get(); + } + + void set_active(const std::string& name) { + assert(screens.contains(name) && "no screen with that name"); + update_active(screens.find(name)); + } + + void set_visible(bool new_value) { + visible = new_value; + } + + bool is_visible() { + return visible; + } + + void render(sf::RenderTarget& target) { + assert($active != nullptr && "you didn't set active"); + $active->render(target); + } + + void update() { + assert($active != nullptr && "you didn't set active"); + $active->update(); + } + + bool mouse(float x, float y, guecs::Modifiers mods = guecs::NO_MODS) { + assert($active != nullptr && "you didn't set active"); + return $active->mouse(x, y, mods); + } + + bool move(const UIStackMap::iterator& itr, const UIStackMap::iterator& avoid, const UIStackMap::iterator& result) { + if(itr != avoid) { + update_active(result); + return true; + } else { + return false; + } + } + + bool next() { + return move($current + 1, screens.end(), $current + 1); + } + + bool prev() { + return move($current, screens.begin(), $current - 1); + } + + void first() { + update_active(screens.begin()); + } + + void last() { + update_active(screens.end() - 1); + } + + const std::string& active_name() { + assert($active != nullptr && "you didn't set active"); + return (*$current).first; + } + + std::shared_ptr active_ui() { + assert($active != nullptr && "you didn't set active"); + return (*$current).second; + } + + std::shared_ptr at(const std::string& name) { + return screens.at(name); + } + + const UIStackMap::key_container_type& names() { + return screens.keys(); + } + }; +} diff --git a/meson.build b/meson.build index 4f0c701..869aa8e 100644 --- a/meson.build +++ b/meson.build @@ -144,3 +144,13 @@ executable('calc', [ include_directories: lel_guecs_inc, link_with: [lel_guecs_lib, lel_guecs_sfml_lib], dependencies: dependencies) + +executable('multiscreen', [ + 'demos/multiscreen.cpp', + ], + cpp_args: cpp_args, + link_args: link_args, + override_options: exe_defaults, + include_directories: lel_guecs_inc, + link_with: [lel_guecs_lib, lel_guecs_sfml_lib], + dependencies: dependencies) diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index dba17b1..7172ee8 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -7,6 +7,7 @@ namespace guecs { using std::make_shared; + // BUG: this seems to center things wrong in lel layouts template void sfml_center_helper(T& obj, lel::Cell& cell, int padding) { sf::Vector2f position{float(cell.x + padding), float(cell.y + padding)}; diff --git a/src/guecs/ui.cpp b/src/guecs/ui.cpp index 49810a9..74ad78d 100644 --- a/src/guecs/ui.cpp +++ b/src/guecs/ui.cpp @@ -118,6 +118,8 @@ namespace guecs { } } + // BUG: either render detects that the things are initialized or there's + // another validator function I can call in debug modes to confirm they are void UI::render(sf::RenderTarget& window) { if(auto bg = get_if(MAIN)) { bg->render(window); From d6dd68bcefbb621c033af02ce2227704b5135c39 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Thu, 9 Apr 2026 00:16:32 -0400 Subject: [PATCH 15/25] Version bump. --- lel-guecs.wrap | 2 +- meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lel-guecs.wrap b/lel-guecs.wrap index 8c799b5..5ce8218 100644 --- a/lel-guecs.wrap +++ b/lel-guecs.wrap @@ -1,5 +1,5 @@ [wrap-git] -directory=lel-guecs-0.7.0 +directory=lel-guecs-0.7.3 url=https://git.learnjsthehardway.com/learn-code-the-hard-way/lel-guecs.git revision=HEAD depth=1 diff --git a/meson.build b/meson.build index 869aa8e..b0cc9cc 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,7 @@ # HEY BUG: when you have a . spec in a LEL it doesn't work on text project('lel-guecs', 'cpp', - version: '0.7.2', + version: '0.7.3', default_options: [ 'cpp_std=c++23', 'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1', From 3d44b7ccc5f9cb77a8e83c3f74312361f0c82472 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Fri, 17 Apr 2026 00:45:06 -0400 Subject: [PATCH 16/25] Experimental cross platform utf8 support. --- meson.build | 6 ++++-- src/guecs/ui.cpp | 12 ++++++++++-- wraps/utf8cpp.wrap | 9 +++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 wraps/utf8cpp.wrap diff --git a/meson.build b/meson.build index b0cc9cc..59e925e 100644 --- a/meson.build +++ b/meson.build @@ -11,7 +11,8 @@ project('lel-guecs', 'cpp', # use this for common options only for our executables cpp_args=[ - '-Wno-deprecated-declarations' + '-Wno-deprecated-declarations', + '-I../subprojects/utf8cpp-4.0.9/source', ] link_args=[] # these are passed as override_defaults @@ -50,6 +51,7 @@ catch2 = subproject('catch2').get_variable('catch2_with_main_dep') fmt = subproject('fmt').get_variable('fmt_dep') json = subproject('nlohmann_json').get_variable('nlohmann_json_dep') freetype2 = subproject('freetype2').get_variable('freetype_dep') +utf8cpp = subproject('utf8cpp').get_variable('utf8cpp_dep') flac = subproject('flac').get_variable('flac_dep') ogg = subproject('ogg').get_variable('libogg_dep') @@ -64,7 +66,7 @@ sfml_window = subproject('sfml').get_variable('sfml_window_dep') lib_depends = [ fmt, sfml_graphics, sfml_system, sfml_window ] lib_sfml_depends = [ - fmt, json, freetype2, flac, ogg, + fmt, json, freetype2, flac, ogg, utf8cpp, vorbis, vorbisfile, vorbisenc, sfml_audio, ] diff --git a/src/guecs/ui.cpp b/src/guecs/ui.cpp index 74ad78d..7fe3f58 100644 --- a/src/guecs/ui.cpp +++ b/src/guecs/ui.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace guecs { using std::make_shared; @@ -237,7 +238,14 @@ namespace guecs { } wstring to_wstring(const string& str) { - std::wstring_convert> $converter; - return $converter.from_bytes(str); + std::wstring result = L""; + + try { + utf8::utf8to32(str.begin(), str.end(), std::back_inserter(result)); + } catch(const utf8::exception& e) { + std::cerr << e.what() << std::endl; + } + + return result; } } diff --git a/wraps/utf8cpp.wrap b/wraps/utf8cpp.wrap new file mode 100644 index 0000000..c1ae900 --- /dev/null +++ b/wraps/utf8cpp.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory=utf8cpp-4.0.9 +url=https://github.com/nemtrif/utfcpp.git +revision=v4.0.9 +depth=1 +method=cmake + +[provide] +utf8cpp = utf8cpp_dep From 1950f45f46c35e3657190ab48790f83decc84393 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sun, 19 Apr 2026 17:59:04 -0400 Subject: [PATCH 17/25] Meter is improved but needs a few more things. --- demos/meters.cpp | 100 ++++++++++++++++++++++++++++++ include/guecs/sfml/components.hpp | 11 +++- meson.build | 16 ++++- src/guecs/sfml/components.cpp | 32 +++++----- src/guecs/ui.cpp | 4 +- 5 files changed, 141 insertions(+), 22 deletions(-) create mode 100644 demos/meters.cpp diff --git a/demos/meters.cpp b/demos/meters.cpp new file mode 100644 index 0000000..22a9911 --- /dev/null +++ b/demos/meters.cpp @@ -0,0 +1,100 @@ +#include "guecs/sfml/backend.hpp" +#include "guecs/sfml/components.hpp" +#include "guecs/uistack.hpp" +#include "guecs/ui.hpp" +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +struct TestMeters { + guecs::UI $gui{}; + + void init() { + $gui.position(0, 0, 1280, 720); + $gui.layout("[meter1][=meter2][meter3][meter4]"); + + for(auto& [name, cell] : $gui.cells()) { + auto gui_id = $gui.entity(name); + + $gui.set(gui_id, {4, {0, 255, 0, 255}}); + $gui.set(gui_id, {1.0f, {255, 0, 0, 255}, 4}); + $gui.set(gui_id, {}); + $gui.set(gui_id, {guecs::to_wstring(name)}); + $gui.set(gui_id, { + [&, gui_id, name](auto) { + auto& meter = $gui.get(gui_id); + meter.percent = meter.percent - 0.1f; + if(meter.percent < 0.0f) { + meter.percent = 1.0f; + } + } + }); + } + + $gui.init(); + } + + void render(sf::RenderTarget& target) { + $gui.render(target); + } + + bool mouse(float x, float y, guecs::Modifiers mods) { + return $gui.mouse(x, y, mods); + } + + void update() { + } +}; + +int main() { + try { + sfml::Backend backend; + guecs::init(&backend); + + sf::RenderWindow window(sf::VideoMode({1280, 720}), "Meter Tester"); + window.setVerticalSyncEnabled(true); + window.setFramerateLimit(60); + window.setPosition({0,0}); + TestMeters gui; + gui.init(); + + while(window.isOpen()) { + while (const auto event = window.pollEvent()) { + if(event->is()) { + window.close(); + } + + if(const auto* mouse = event->getIf()) { + if(mouse->button == sf::Mouse::Button::Left) { + sf::Vector2f pos = window.mapPixelToCoords(mouse->position); + gui.mouse(pos.x, pos.y, false); + } + } else if(const auto* key = event->getIf()) { + using KEY = sf::Keyboard::Scan; + + switch(key->scancode) { + case KEY::Q: + return 0; + default: + break; + } + } + } + + gui.update(); + gui.render(window); + window.display(); + + std::this_thread::sleep_for(100ms); + } + } catch(const std::system_error& e) { + std::cout << "WARNING: On OSX you'll get this error on shutdown.\n"; + std::cout << "Caught system_error with code " + "[" << e.code() << "] meaning " + "[" << e.what() << "]\n"; + } +} diff --git a/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index 017e7dc..234f1b4 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -69,11 +69,16 @@ namespace guecs { struct Meter { float percent = 1.0f; sf::Color color = THEME.BG_COLOR_DARK; - Rectangle bar; + int padding = THEME.PADDING; + Rectangle bar{padding, color}; + // this is set automatically if a Rectangle is configured for the cell + Rectangle backing_rect{padding, color}; + size_t $cell_w = 0; + size_t $cell_h = 0; + void init(lel::Cell& cell, guecs::Rectangle &bg); void init(lel::Cell& cell); - void init(lel::Cell& cell, Rectangle& bg); - void render(lel::Cell& cell, sf::RenderTarget& window, sf::Shader *shader_ptr); + void render(sf::RenderTarget& window, sf::Shader *shader_ptr); }; struct Effect { diff --git a/meson.build b/meson.build index 59e925e..15a920f 100644 --- a/meson.build +++ b/meson.build @@ -11,8 +11,12 @@ project('lel-guecs', 'cpp', # use this for common options only for our executables cpp_args=[ - '-Wno-deprecated-declarations', '-I../subprojects/utf8cpp-4.0.9/source', + '-Wno-unused-parameter', + '-Wno-unused-function', + '-Wno-unused-variable', + '-Wno-unused-but-set-variable', + '-Wno-deprecated-declarations', ] link_args=[] # these are passed as override_defaults @@ -156,3 +160,13 @@ executable('multiscreen', [ include_directories: lel_guecs_inc, link_with: [lel_guecs_lib, lel_guecs_sfml_lib], dependencies: dependencies) + +executable('meters', [ + 'demos/meters.cpp', + ], + cpp_args: cpp_args, + link_args: link_args, + override_options: exe_defaults, + include_directories: lel_guecs_inc, + link_with: [lel_guecs_lib, lel_guecs_sfml_lib], + dependencies: dependencies) diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index 7172ee8..6c1fadb 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -8,19 +8,18 @@ namespace guecs { using std::make_shared; // BUG: this seems to center things wrong in lel layouts - template - void sfml_center_helper(T& obj, lel::Cell& cell, int padding) { - sf::Vector2f position{float(cell.x + padding), float(cell.y + padding)}; + void sfml_center_helper(auto& obj, lel::Cell& cell, int padding) { + sf::Vector2f position{float(cell.x + padding), float(cell.y + padding)}; - if(cell.center) { - auto bounds = obj->getLocalBounds(); - position = {float(cell.mid_x), float(cell.mid_y)}; - obj->setOrigin({bounds.size.x/2, bounds.size.y/2}); - } - - obj->setPosition(position); + if(cell.center) { + auto bounds = obj->getLocalBounds(); + position = {float(cell.mid_x), float(cell.mid_y)}; + obj->setOrigin({bounds.size.x/2, bounds.size.y/2}); } + obj->setPosition(position); + } + inline SpriteTexture load_texture(const string& name, bool is_icon) { if(is_icon) { return BACKEND->get_icon(name); @@ -109,19 +108,20 @@ namespace guecs { } void Meter::init(lel::Cell& cell, Rectangle& bg) { - bg.shape->setFillColor(color); + backing_rect = bg; init(cell); } void Meter::init(lel::Cell& cell) { + $cell_w = cell.w; + $cell_h = cell.h; bar.init(cell); } - void Meter::render(lel::Cell& cell, sf::RenderTarget& window, sf::Shader *shader_ptr) { - float level = std::clamp(percent, 0.0f, 1.0f) * float(cell.w); - // ZED: this 6 is a border width, make it a thing - bar.shape->setSize({std::max(level, 0.0f), float(cell.h - 6)}); - window.draw(*bar.shape, shader_ptr); + void Meter::render(sf::RenderTarget& window, sf::Shader *shader_ptr) { + float level = std::clamp(percent, 0.0f, 1.0f) * float($cell_w); + bar.shape->setSize({std::max(level, 0.0f) - padding * 2, float($cell_h) - padding * 2}); + bar.render(window, shader_ptr); } void Sound::play(bool hover) { diff --git a/src/guecs/ui.cpp b/src/guecs/ui.cpp index 7fe3f58..84215f2 100644 --- a/src/guecs/ui.cpp +++ b/src/guecs/ui.cpp @@ -135,9 +135,9 @@ namespace guecs { rect.render(window, shader_ptr); }); - query([&](auto ent, auto& cell, auto &meter) { + query([&](auto ent, auto &meter) { auto shader_ptr = find_shader(ent, true); - meter.render(cell, window, shader_ptr); + meter.render(window, shader_ptr); }); query([&](auto ent, auto& sprite) { From c390ca5616fe2afdf81791144d43a80bdd392915 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sun, 19 Apr 2026 18:03:33 -0400 Subject: [PATCH 18/25] Make a update_percent function for meter. --- demos/meters.cpp | 2 +- include/guecs/sfml/components.hpp | 1 + src/guecs/sfml/components.cpp | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/demos/meters.cpp b/demos/meters.cpp index 22a9911..56ee58a 100644 --- a/demos/meters.cpp +++ b/demos/meters.cpp @@ -27,7 +27,7 @@ struct TestMeters { $gui.set(gui_id, { [&, gui_id, name](auto) { auto& meter = $gui.get(gui_id); - meter.percent = meter.percent - 0.1f; + meter.update_percent(meter.percent - 0.1f); if(meter.percent < 0.0f) { meter.percent = 1.0f; } diff --git a/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index 234f1b4..86f59f8 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -79,6 +79,7 @@ namespace guecs { void init(lel::Cell& cell, guecs::Rectangle &bg); void init(lel::Cell& cell); void render(sf::RenderTarget& window, sf::Shader *shader_ptr); + void update_percent(float pct); }; struct Effect { diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index 6c1fadb..c437b36 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -116,12 +116,17 @@ namespace guecs { $cell_w = cell.w; $cell_h = cell.h; bar.init(cell); + update_percent(percent); } void Meter::render(sf::RenderTarget& window, sf::Shader *shader_ptr) { + bar.render(window, shader_ptr); + } + + void Meter::update_percent(float pct) { + percent = pct; float level = std::clamp(percent, 0.0f, 1.0f) * float($cell_w); bar.shape->setSize({std::max(level, 0.0f) - padding * 2, float($cell_h) - padding * 2}); - bar.render(window, shader_ptr); } void Sound::play(bool hover) { From ad116b5515c4f3eb936e2068d00d866c2d95c5c6 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sun, 19 Apr 2026 23:15:15 -0400 Subject: [PATCH 19/25] Improve the demo a bit. --- demos/meters.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/demos/meters.cpp b/demos/meters.cpp index 56ee58a..7311b85 100644 --- a/demos/meters.cpp +++ b/demos/meters.cpp @@ -20,17 +20,14 @@ struct TestMeters { for(auto& [name, cell] : $gui.cells()) { auto gui_id = $gui.entity(name); - $gui.set(gui_id, {4, {0, 255, 0, 255}}); - $gui.set(gui_id, {1.0f, {255, 0, 0, 255}, 4}); + $gui.set(gui_id, {guecs::THEME.PADDING, {120, 25, 25, 255}}); + $gui.set(gui_id, {1.0f, guecs::THEME.DARK_LIGHT, guecs::THEME.PADDING}); $gui.set(gui_id, {}); $gui.set(gui_id, {guecs::to_wstring(name)}); $gui.set(gui_id, { [&, gui_id, name](auto) { auto& meter = $gui.get(gui_id); - meter.update_percent(meter.percent - 0.1f); - if(meter.percent < 0.0f) { - meter.percent = 1.0f; - } + meter.update_percent(1.0f); } }); } @@ -85,6 +82,14 @@ int main() { } } + gui.$gui.query([](auto ent, auto& meter) { + if(meter.percent <= 0.0f) { + meter.update_percent(1.0f); + } else { + meter.update_percent(meter.percent - (0.01f * float(ent))); + } + }); + gui.update(); gui.render(window); window.display(); From 48b672eec4fbf6b6c6252495bad0fb3c4ee5d312 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sun, 19 Apr 2026 23:47:14 -0400 Subject: [PATCH 20/25] Implement vertical meters. --- demos/meters.cpp | 14 ++++++++++++-- include/guecs/sfml/components.hpp | 3 +++ src/guecs/sfml/components.cpp | 13 +++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/demos/meters.cpp b/demos/meters.cpp index 7311b85..e23b6b9 100644 --- a/demos/meters.cpp +++ b/demos/meters.cpp @@ -15,13 +15,23 @@ struct TestMeters { void init() { $gui.position(0, 0, 1280, 720); - $gui.layout("[meter1][=meter2][meter3][meter4]"); + $gui.layout( + "[*%(300,100)meter1|_|_|*%(100,400)vert1|*%(100,400)vert2|*%(100,400)vert3]" + "[*%(300,100)meter2|_|_|_|_|_]" + "[*%(300,100)meter3|_|_|_|_|_]" + "[*%(300,100)meter4|_|_|_|_|_]"); for(auto& [name, cell] : $gui.cells()) { auto gui_id = $gui.entity(name); $gui.set(gui_id, {guecs::THEME.PADDING, {120, 25, 25, 255}}); - $gui.set(gui_id, {1.0f, guecs::THEME.DARK_LIGHT, guecs::THEME.PADDING}); + + if(name.starts_with("meter")) { + $gui.set(gui_id, {1.0f, guecs::THEME.DARK_LIGHT, guecs::THEME.PADDING}); + } else { + $gui.set(gui_id, {1.0f, guecs::THEME.DARK_LIGHT, guecs::THEME.PADDING, true}); + } + $gui.set(gui_id, {}); $gui.set(gui_id, {guecs::to_wstring(name)}); $gui.set(gui_id, { diff --git a/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index 86f59f8..ad30a45 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -70,9 +70,12 @@ namespace guecs { float percent = 1.0f; sf::Color color = THEME.BG_COLOR_DARK; int padding = THEME.PADDING; + bool vertical = false; Rectangle bar{padding, color}; // this is set automatically if a Rectangle is configured for the cell Rectangle backing_rect{padding, color}; + size_t $cell_x = 0; + size_t $cell_y = 0; size_t $cell_w = 0; size_t $cell_h = 0; diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index c437b36..07f188e 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -113,6 +113,8 @@ namespace guecs { } void Meter::init(lel::Cell& cell) { + $cell_x = cell.x; + $cell_y = cell.y; $cell_w = cell.w; $cell_h = cell.h; bar.init(cell); @@ -125,8 +127,15 @@ namespace guecs { void Meter::update_percent(float pct) { percent = pct; - float level = std::clamp(percent, 0.0f, 1.0f) * float($cell_w); - bar.shape->setSize({std::max(level, 0.0f) - padding * 2, float($cell_h) - padding * 2}); + if(vertical) { + float level = std::clamp(percent, 0.0f, 1.0f) * float($cell_h); + sf::Vector2f size{float($cell_w) - padding * 2, std::max(level, 0.0f) - padding}; + bar.shape->setSize(size); + bar.shape->setPosition({float($cell_x + padding), float($cell_y) + (float($cell_h) - size.y)}); + } else { + float level = std::clamp(percent, 0.0f, 1.0f) * float($cell_w); + bar.shape->setSize({std::max(level, 0.0f) - padding * 2, float($cell_h) - padding * 2}); + } } void Sound::play(bool hover) { From 2dbfac27c6740d086cd35aedf7684928dc669049 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Mon, 20 Apr 2026 16:02:13 -0400 Subject: [PATCH 21/25] Better design that has a render/update cycle for components. --- demos/meters.cpp | 15 ++++++++++++--- include/guecs/sfml/components.hpp | 6 +++--- include/guecs/ui.hpp | 1 + src/guecs/sfml/components.cpp | 21 +++++++++++---------- src/guecs/ui.cpp | 16 +++++++++++----- 5 files changed, 38 insertions(+), 21 deletions(-) diff --git a/demos/meters.cpp b/demos/meters.cpp index e23b6b9..10195ff 100644 --- a/demos/meters.cpp +++ b/demos/meters.cpp @@ -37,7 +37,15 @@ struct TestMeters { $gui.set(gui_id, { [&, gui_id, name](auto) { auto& meter = $gui.get(gui_id); - meter.update_percent(1.0f); + auto& bg = $gui.get(gui_id); + meter.percent = 1.0f; + if(meter.color.r == 255) { + meter.color = guecs::THEME.DARK_LIGHT; + bg.color = {120, 25, 25, 255}; + } else { + meter.color = {255, 0, 0, 255}; + bg.color = {0,255,0,255}; + } } }); } @@ -54,6 +62,7 @@ struct TestMeters { } void update() { + $gui.update(); } }; @@ -94,9 +103,9 @@ int main() { gui.$gui.query([](auto ent, auto& meter) { if(meter.percent <= 0.0f) { - meter.update_percent(1.0f); + meter.percent = 1.0f; } else { - meter.update_percent(meter.percent - (0.01f * float(ent))); + meter.percent -= 0.01f * float(ent); } }); diff --git a/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index ad30a45..d681ebc 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -61,6 +61,7 @@ namespace guecs { void init(lel::Cell& cell); void render(sf::RenderTarget& window, sf::Shader *shader_ptr); + void update(); }; // BUG: meter needs a backing rectangle of one color for the full bar, then @@ -72,17 +73,16 @@ namespace guecs { int padding = THEME.PADDING; bool vertical = false; Rectangle bar{padding, color}; + // this is set automatically if a Rectangle is configured for the cell - Rectangle backing_rect{padding, color}; size_t $cell_x = 0; size_t $cell_y = 0; size_t $cell_w = 0; size_t $cell_h = 0; - void init(lel::Cell& cell, guecs::Rectangle &bg); void init(lel::Cell& cell); void render(sf::RenderTarget& window, sf::Shader *shader_ptr); - void update_percent(float pct); + void update(); }; struct Effect { diff --git a/include/guecs/ui.hpp b/include/guecs/ui.hpp index 164e2ba..b797bc1 100644 --- a/include/guecs/ui.hpp +++ b/include/guecs/ui.hpp @@ -82,6 +82,7 @@ namespace guecs { void init(); void render(sf::RenderTarget& window); + void update(); bool mouse(float x, float y, Modifiers mods); void click_on(const std::string& name, Modifiers mods=NO_MODS); void click_on(Entity slot_id, Modifiers mods=NO_MODS); diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index 07f188e..2144a96 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -96,10 +96,7 @@ namespace guecs { void Rectangle::init(lel::Cell& cell) { sf::Vector2f size{float(cell.w) - padding * 2, float(cell.h) - padding * 2}; if(shape == nullptr) shape = make_shared(size); - shape->setFillColor(color); - shape->setOutlineColor(border_color); - shape->setOutlineThickness(border_px); - + update(); sfml_center_helper(shape, cell, padding); } @@ -107,9 +104,10 @@ namespace guecs { window.draw(*shape, shader_ptr); } - void Meter::init(lel::Cell& cell, Rectangle& bg) { - backing_rect = bg; - init(cell); + void Rectangle::update() { + shape->setFillColor(color); + shape->setOutlineColor(border_color); + shape->setOutlineThickness(border_px); } void Meter::init(lel::Cell& cell) { @@ -118,15 +116,18 @@ namespace guecs { $cell_w = cell.w; $cell_h = cell.h; bar.init(cell); - update_percent(percent); + update(); } void Meter::render(sf::RenderTarget& window, sf::Shader *shader_ptr) { bar.render(window, shader_ptr); } - void Meter::update_percent(float pct) { - percent = pct; + void Meter::update() { + bar.color = color; + bar.padding = padding; + bar.update(); + if(vertical) { float level = std::clamp(percent, 0.0f, 1.0f) * float($cell_h); sf::Vector2f size{float($cell_w) - padding * 2, std::max(level, 0.0f) - padding}; diff --git a/src/guecs/ui.cpp b/src/guecs/ui.cpp index 84215f2..87fa4c7 100644 --- a/src/guecs/ui.cpp +++ b/src/guecs/ui.cpp @@ -80,11 +80,7 @@ namespace guecs { }); query([this](auto ent, auto &cell, auto& meter) { - if(auto bg = get_if(ent)) { - meter.init(cell, *bg); - } else { - meter.init(cell); - } + meter.init(cell); }); query([this](auto, auto& cell, auto& text) { @@ -119,6 +115,16 @@ namespace guecs { } } + void UI::update() { + query([&](auto ent, auto& rect) { + rect.update(); + }); + + query([&](auto ent, auto &meter) { + meter.update(); + }); + } + // BUG: either render detects that the things are initialized or there's // another validator function I can call in debug modes to confirm they are void UI::render(sf::RenderTarget& window) { From 4f3fa597938e687b281d0874519bdff50abd1b6a Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 21 Apr 2026 14:24:52 -0400 Subject: [PATCH 22/25] Fix the linux reset. --- scripts/reset_build.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/reset_build.sh diff --git a/scripts/reset_build.sh b/scripts/reset_build.sh old mode 100644 new mode 100755 From 1d11c8b162929b51db96de3df21fb1e0b244b05f Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 16 Jun 2026 01:34:03 -0400 Subject: [PATCH 23/25] Now off catch2 and on my own testing fuc2. --- Makefile | 4 +- meson.build | 11 ++--- tests/guecs.cpp | 16 +++++-- tests/lel.cpp | 107 +++++++++++++++++++++++++-------------------- tests/main.cpp | 31 +++++++++++++ tests/shaders.cpp | 40 ++++++++++------- tests/sound.cpp | 21 ++++++--- tests/textures.cpp | 25 +++++++---- wraps/catch2.wrap | 11 ----- wraps/fuc2.wrap | 9 ++++ 10 files changed, 177 insertions(+), 98 deletions(-) create mode 100644 tests/main.cpp delete mode 100644 wraps/catch2.wrap create mode 100644 wraps/fuc2.wrap diff --git a/Makefile b/Makefile index 5e1b2b8..4069b4f 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ tracy_build: meson compile -j 10 -C builddir test: build - ./builddir/runtests + ./builddir/fuc2it run: build test ifeq '$(OS)' 'Windows_NT' @@ -49,7 +49,7 @@ clean: meson compile --clean -C builddir debug_test: build - gdb --nx -x .gdbinit --ex run --args builddir/runtests -e + gdb --nx -x .gdbinit --ex run --args builddir/fuc2it -e win_installer: powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp' diff --git a/meson.build b/meson.build index 15a920f..02f9c18 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,7 @@ # HEY BUG: when you have a . spec in a LEL it doesn't work on text project('lel-guecs', 'cpp', - version: '0.7.3', + version: '0.7.4', default_options: [ 'cpp_std=c++23', 'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1', @@ -51,7 +51,7 @@ elif build_machine.system() == 'darwin' ] endif -catch2 = subproject('catch2').get_variable('catch2_with_main_dep') +fuc2 = subproject('fuc2').get_variable('fuc2_dep') fmt = subproject('fmt').get_variable('fmt_dep') json = subproject('nlohmann_json').get_variable('nlohmann_json_dep') freetype2 = subproject('freetype2').get_variable('freetype_dep') @@ -117,19 +117,20 @@ lel_guecs_sfml_dep = declare_dependency( link_with: lel_guecs_sfml_lib, include_directories: lel_guecs_inc) -executable('runtests', [ +executable('fuc2it', [ + 'tests/guecs.cpp', 'tests/lel.cpp', + 'tests/main.cpp', 'tests/shaders.cpp', 'tests/sound.cpp', 'tests/textures.cpp', - 'tests/guecs.cpp', ], cpp_args: cpp_args, link_args: link_args, override_options: exe_defaults, include_directories: lel_guecs_inc, link_with: [lel_guecs_lib, lel_guecs_sfml_lib], - dependencies: dependencies + [catch2]) + dependencies: dependencies + [fuc2]) executable('clicker_game', [ 'demos/clicker_game.cpp', diff --git a/tests/guecs.cpp b/tests/guecs.cpp index 33d5765..82f558f 100644 --- a/tests/guecs.cpp +++ b/tests/guecs.cpp @@ -1,10 +1,20 @@ -#include #include #include "guecs/ui.hpp" #include +#include using namespace guecs; +using namespace fuc2; -TEST_CASE("prototype one gui", "[ecs-gui]") { - // accidentally gutted, will recover later +namespace guecs_tests { + void test_prototype() { + // accidentally gutted, will recover later + } + + fuc2::Set TESTS{ + .name="GUECS tests (empty!)", + .tests={ + {"test_prototype", test_prototype}, + } + }; } diff --git a/tests/lel.cpp b/tests/lel.cpp index 244e751..7928746 100644 --- a/tests/lel.cpp +++ b/tests/lel.cpp @@ -1,61 +1,72 @@ #include "guecs/lel.hpp" -#include #include #include +#include -TEST_CASE("test basic ops", "[lel]") { - lel::Parser parser(0, 0, 500, 500); +using namespace fuc2; - bool good = parser.parse( - "[ label_1 | label3 | test1]" - "[ *(300,300)text1 | %(150)people | ^test2]" - "[ >label2 | _ | .test3]" - "[ =message | buttons | test4]"); +namespace lel_tests { + void test_basic_ops() { + lel::Parser parser(0, 0, 500, 500); - REQUIRE(good); + bool good = parser.parse( + "[ label_1 | label3 | test1]" + "[ *(300,300)text1 | %(150)people | ^test2]" + "[ >label2 | _ | .test3]" + "[ =message | buttons | test4]"); - for(size_t rowcount = 0; rowcount < parser.grid.size(); rowcount++) { - auto& row = parser.grid[rowcount]; + CHECK(good); - for(size_t colcount = 0; colcount < row.size(); colcount++) { - auto &name = row[colcount]; - if(name == "_") { - REQUIRE(!parser.cells.contains(name)); - } else if(name == "_MAIN") { - // it's the main cell which covers the whole area - auto &cell = parser.cells.at(name); - REQUIRE(cell.row == 0); - REQUIRE(cell.col == 0); - REQUIRE(cell.x == parser.grid_x); - REQUIRE(cell.y == parser.grid_y); - REQUIRE(cell.w == parser.grid_w); - REQUIRE(cell.h == parser.grid_h); - } else { - auto &cell = parser.cells.at(name); - REQUIRE(cell.row == int(rowcount)); - REQUIRE(cell.col == int(colcount)); + for(size_t rowcount = 0; rowcount < parser.grid.size(); rowcount++) { + auto& row = parser.grid[rowcount]; + + for(size_t colcount = 0; colcount < row.size(); colcount++) { + auto &name = row[colcount]; + if(name == "_") { + CHECK(!parser.cells.contains(name)); + } else if(name == "_MAIN") { + // it's the main cell which covers the whole area + auto &cell = parser.cells.at(name); + CHECK(cell.row == 0); + CHECK(cell.col == 0); + CHECK(cell.x == parser.grid_x); + CHECK(cell.y == parser.grid_y); + CHECK(cell.w == parser.grid_w); + CHECK(cell.h == parser.grid_h); + } else { + auto &cell = parser.cells.at(name); + CHECK(cell.row == int(rowcount)); + CHECK(cell.col == int(colcount)); + } } } + + CHECK(parser.cells.size() == 11); + CHECK(parser.cells.at("label2").right == true); + CHECK(parser.cells.at("text1").expand == true); + CHECK(parser.cells.at("text1").w == 300); + CHECK(parser.cells.at("text1").h == 300); + CHECK(parser.cells.at("people").expand == false); + CHECK(parser.cells.at("message").expand == false); + CHECK(parser.cells.at("message").center == true); + + for(auto& [name, cell] : parser.cells) { + CHECK(cell.w > 0); + CHECK(cell.h > 0); + } + + auto hit = parser.hit(10, 10); + CHECK(*hit == "label_1"); + + auto nohit = parser.hit(1000, 1000); + CHECK(!nohit); + CHECK(nohit == std::nullopt); } - REQUIRE(parser.cells.size() == 11); - REQUIRE(parser.cells.at("label2").right == true); - REQUIRE(parser.cells.at("text1").expand == true); - REQUIRE(parser.cells.at("text1").w == 300); - REQUIRE(parser.cells.at("text1").h == 300); - REQUIRE(parser.cells.at("people").expand == false); - REQUIRE(parser.cells.at("message").expand == false); - REQUIRE(parser.cells.at("message").center == true); - - for(auto& [name, cell] : parser.cells) { - REQUIRE(cell.w > 0); - REQUIRE(cell.h > 0); - } - - auto hit = parser.hit(10, 10); - REQUIRE(*hit == "label_1"); - - auto nohit = parser.hit(1000, 1000); - REQUIRE(!nohit); - REQUIRE(nohit == std::nullopt); + fuc2::Set TESTS{ + .name="lel parser tests", + .tests={ + {"test_basic_ops", test_basic_ops}, + } + }; } diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..59b16c5 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,31 @@ +#include + +// you can also place these into a .hpp you include if +// they're too numerous to manage +namespace lel_tests { + extern fuc2::Set TESTS; +} + +namespace guecs_tests { + extern fuc2::Set TESTS; +} + +namespace shader_tests { + extern fuc2::Set TESTS; +} + +namespace sound_tests { + extern fuc2::Set TESTS; +} + +namespace texture_tests { + extern fuc2::Set TESTS; +} + +int main(int argc, char* argv[]) { + run(lel_tests::TESTS); + run(guecs_tests::TESTS); + run(shader_tests::TESTS); + run(sound_tests::TESTS); + run(texture_tests::TESTS); +} diff --git a/tests/shaders.cpp b/tests/shaders.cpp index 2b39e1f..6e6cf6b 100644 --- a/tests/shaders.cpp +++ b/tests/shaders.cpp @@ -1,26 +1,36 @@ -#include #include #include #include "guecs/sfml/shaders.hpp" +#include +using namespace fuc2; using namespace fmt; -TEST_CASE("shader loading/init works", "[shaders]") { - shaders::init(); - int version = shaders::version(); +namespace shader_tests { + void test_shader_loading() { + shaders::init(); + int version = shaders::version(); - std::shared_ptr ui_shader = shaders::get("ui_shader"); - auto other_test = shaders::get("ui_shader"); + std::shared_ptr ui_shader = shaders::get("ui_shader"); + auto other_test = shaders::get("ui_shader"); - REQUIRE(ui_shader != nullptr); - REQUIRE(ui_shader == other_test); - REQUIRE(shaders::updated(version) == false); + CHECK(ui_shader != nullptr); + CHECK(ui_shader == other_test); + CHECK(shaders::updated(version) == false); - int new_version = shaders::reload(); - REQUIRE(version != shaders::version()); - REQUIRE(version != new_version); - REQUIRE(shaders::version() == new_version); - REQUIRE(shaders::updated(version) == true); - version = new_version; + int new_version = shaders::reload(); + CHECK(version != shaders::version()); + CHECK(version != new_version); + CHECK(shaders::version() == new_version); + CHECK(shaders::updated(version) == true); + version = new_version; + } + + fuc2::Set TESTS{ + .name="shader tests", + .tests={ + {"test_shader_loading", test_shader_loading}, + } + }; } diff --git a/tests/sound.cpp b/tests/sound.cpp index 32dab05..6f22929 100644 --- a/tests/sound.cpp +++ b/tests/sound.cpp @@ -1,14 +1,23 @@ -#include #include #include #include "guecs/sfml/sound.hpp" +#include -using namespace fmt; +using namespace fuc2; -TEST_CASE("test sound manager", "[sound]") { - sound::init(); +namespace sound_tests { + void test_sound_manager() { + sound::init(); - sound::play("blank"); + sound::play("blank"); - sound::play_at("blank", 0.1, 0.1, 0.1); + sound::play_at("blank", 0.1, 0.1, 0.1); + } + + fuc2::Set TESTS{ + .name="sound manager tests", + .tests={ + {"test_sound_manager", test_sound_manager}, + } + }; } diff --git a/tests/textures.cpp b/tests/textures.cpp index 68ccee0..0d0dea2 100644 --- a/tests/textures.cpp +++ b/tests/textures.cpp @@ -1,15 +1,24 @@ -#include #include #include #include "guecs/sfml/textures.hpp" +#include -using namespace fmt; +using namespace fuc2; -TEST_CASE("test texture management", "[textures]") { - textures::init(); - auto spider = textures::get("textures_test"); - REQUIRE(spider.sprite != nullptr); - REQUIRE(spider.texture != nullptr); +namespace texture_tests { + void test_texture_management() { + textures::init(); + auto spider = textures::get("textures_test"); + CHECK(spider.sprite != nullptr); + CHECK(spider.texture != nullptr); - auto image = textures::load_image("assets/textures_test.png"); + auto image = textures::load_image("assets/textures_test.png"); + } + + fuc2::Set TESTS{ + .name="texture manager tests", + .tests={ + {"test_texture_management", test_texture_management}, + } + }; } diff --git a/wraps/catch2.wrap b/wraps/catch2.wrap deleted file mode 100644 index f9bf436..0000000 --- a/wraps/catch2.wrap +++ /dev/null @@ -1,11 +0,0 @@ -[wrap-file] -directory = Catch2-3.7.1 -source_url = https://github.com/catchorg/Catch2/archive/v3.7.1.tar.gz -source_filename = Catch2-3.7.1.tar.gz -source_hash = c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c -source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.7.1-1/Catch2-3.7.1.tar.gz -wrapdb_version = 3.7.1-1 - -[provide] -catch2 = catch2_dep -catch2-with-main = catch2_with_main_dep diff --git a/wraps/fuc2.wrap b/wraps/fuc2.wrap new file mode 100644 index 0000000..50fe0df --- /dev/null +++ b/wraps/fuc2.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory=fuc2-0.1.0 +url=https://lcthw.dev/cpp/fuc2.git +revision=HEAD +depth=1 +method=meson + +[provide] +fuc2 = fuc2_dep From aa153f8e8469c8aaaf7e87dd36441608ec3f6ded Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 16 Jun 2026 02:08:01 -0400 Subject: [PATCH 24/25] Use the newer way to wire up tests. --- tests/guecs.cpp | 2 +- tests/lel.cpp | 2 +- tests/main.cpp | 26 ++++++-------------------- tests/shaders.cpp | 2 +- tests/sound.cpp | 2 +- tests/textures.cpp | 2 +- 6 files changed, 11 insertions(+), 25 deletions(-) diff --git a/tests/guecs.cpp b/tests/guecs.cpp index 82f558f..ecb6ce7 100644 --- a/tests/guecs.cpp +++ b/tests/guecs.cpp @@ -14,7 +14,7 @@ namespace guecs_tests { fuc2::Set TESTS{ .name="GUECS tests (empty!)", .tests={ - {"test_prototype", test_prototype}, + TEST(test_prototype), } }; } diff --git a/tests/lel.cpp b/tests/lel.cpp index 7928746..fd0b183 100644 --- a/tests/lel.cpp +++ b/tests/lel.cpp @@ -66,7 +66,7 @@ namespace lel_tests { fuc2::Set TESTS{ .name="lel parser tests", .tests={ - {"test_basic_ops", test_basic_ops}, + TEST(test_basic_ops), } }; } diff --git a/tests/main.cpp b/tests/main.cpp index 59b16c5..02811e1 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,26 +1,12 @@ #include -// you can also place these into a .hpp you include if -// they're too numerous to manage -namespace lel_tests { - extern fuc2::Set TESTS; -} +using namespace fuc2; -namespace guecs_tests { - extern fuc2::Set TESTS; -} - -namespace shader_tests { - extern fuc2::Set TESTS; -} - -namespace sound_tests { - extern fuc2::Set TESTS; -} - -namespace texture_tests { - extern fuc2::Set TESTS; -} +TEST_SET(lel_tests); +TEST_SET(guecs_tests); +TEST_SET(shader_tests); +TEST_SET(sound_tests); +TEST_SET(texture_tests); int main(int argc, char* argv[]) { run(lel_tests::TESTS); diff --git a/tests/shaders.cpp b/tests/shaders.cpp index 6e6cf6b..c151d7b 100644 --- a/tests/shaders.cpp +++ b/tests/shaders.cpp @@ -30,7 +30,7 @@ namespace shader_tests { fuc2::Set TESTS{ .name="shader tests", .tests={ - {"test_shader_loading", test_shader_loading}, + TEST(test_shader_loading), } }; } diff --git a/tests/sound.cpp b/tests/sound.cpp index 6f22929..9d02fa8 100644 --- a/tests/sound.cpp +++ b/tests/sound.cpp @@ -17,7 +17,7 @@ namespace sound_tests { fuc2::Set TESTS{ .name="sound manager tests", .tests={ - {"test_sound_manager", test_sound_manager}, + TEST(test_sound_manager), } }; } diff --git a/tests/textures.cpp b/tests/textures.cpp index 0d0dea2..f85883c 100644 --- a/tests/textures.cpp +++ b/tests/textures.cpp @@ -18,7 +18,7 @@ namespace texture_tests { fuc2::Set TESTS{ .name="texture manager tests", .tests={ - {"test_texture_management", test_texture_management}, + TEST(test_texture_management), } }; } From 9fffa51729c303e3b00044a5b6364f097fb28e62 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 17 Jun 2026 01:55:09 -0400 Subject: [PATCH 25/25] Update to the newest fuc2. --- tests/guecs.cpp | 2 +- tests/lel.cpp | 2 +- tests/main.cpp | 14 +++++++++----- tests/shaders.cpp | 2 +- tests/sound.cpp | 2 +- tests/textures.cpp | 2 +- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/guecs.cpp b/tests/guecs.cpp index ecb6ce7..c4d3fb9 100644 --- a/tests/guecs.cpp +++ b/tests/guecs.cpp @@ -12,7 +12,7 @@ namespace guecs_tests { } fuc2::Set TESTS{ - .name="GUECS tests (empty!)", + .name="guecs", .tests={ TEST(test_prototype), } diff --git a/tests/lel.cpp b/tests/lel.cpp index fd0b183..273d988 100644 --- a/tests/lel.cpp +++ b/tests/lel.cpp @@ -64,7 +64,7 @@ namespace lel_tests { } fuc2::Set TESTS{ - .name="lel parser tests", + .name="lel", .tests={ TEST(test_basic_ops), } diff --git a/tests/main.cpp b/tests/main.cpp index 02811e1..1207005 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -9,9 +9,13 @@ TEST_SET(sound_tests); TEST_SET(texture_tests); int main(int argc, char* argv[]) { - run(lel_tests::TESTS); - run(guecs_tests::TESTS); - run(shader_tests::TESTS); - run(sound_tests::TESTS); - run(texture_tests::TESTS); + std::vector all_tests{ + lel_tests::TESTS, + guecs_tests::TESTS, + shader_tests::TESTS, + sound_tests::TESTS, + texture_tests::TESTS, + }; + + run_tests(all_tests, argc, argv); } diff --git a/tests/shaders.cpp b/tests/shaders.cpp index c151d7b..e2560d0 100644 --- a/tests/shaders.cpp +++ b/tests/shaders.cpp @@ -28,7 +28,7 @@ namespace shader_tests { } fuc2::Set TESTS{ - .name="shader tests", + .name="shader", .tests={ TEST(test_shader_loading), } diff --git a/tests/sound.cpp b/tests/sound.cpp index 9d02fa8..c6d0514 100644 --- a/tests/sound.cpp +++ b/tests/sound.cpp @@ -15,7 +15,7 @@ namespace sound_tests { } fuc2::Set TESTS{ - .name="sound manager tests", + .name="sound", .tests={ TEST(test_sound_manager), } diff --git a/tests/textures.cpp b/tests/textures.cpp index f85883c..65da69d 100644 --- a/tests/textures.cpp +++ b/tests/textures.cpp @@ -16,7 +16,7 @@ namespace texture_tests { } fuc2::Set TESTS{ - .name="texture manager tests", + .name="texture", .tests={ TEST(test_texture_management), }