From b7cfa4db2d4dff70cbd8aa02f7a8eee2f0a3b63e Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 22 Jul 2025 15:55:05 -0400 Subject: [PATCH 01/13] Icons now scale via aspect ratio, but it's still not as robust as it should be. I also think that Sprite should just work this way or stretch if an option is given, which would make Icon kind of pointless. --- src/guecs/sfml/components.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index 064cc3a..52c86cd 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -45,19 +45,16 @@ namespace guecs { void Sprite::init(lel::Cell &cell) { auto sprite_texture = BACKEND->get_sprite(name); + auto bounds = sprite_texture.frame_size; - sf::IntRect rect{{0,0},sprite_texture.frame_size}; + sf::IntRect rect{{0,0}, bounds}; sprite = make_shared(*sprite_texture.texture, rect); - sprite->setPosition({ - float(cell.x + padding), - float(cell.y + padding)}); - - auto bounds = sprite->getLocalBounds(); + sprite->setPosition({float(cell.x + padding), float(cell.y + padding)}); sprite->setScale({ - float(cell.w - padding * 2) / bounds.size.x, - float(cell.h - padding * 2) / bounds.size.y}); + float(cell.w - padding * 2) / float(bounds.x), + float(cell.h - padding * 2) / float(bounds.y)}); } void Sprite::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { @@ -75,21 +72,25 @@ namespace guecs { void Icon::init(lel::Cell &cell) { auto sprite_texture = BACKEND->get_icon(name); + auto bounds = sprite_texture.frame_size; - sf::IntRect rect{{0,0},sprite_texture.frame_size}; - fmt::println("ICON SIZE: {},{}; {},{}", - rect.position.x, rect.position.y, - rect.size.x, rect.size.y); + sf::IntRect rect{{0,0}, bounds}; sprite = make_shared(*sprite_texture.texture, rect); sprite->setPosition({ float(cell.x + padding), float(cell.y + padding)}); + + float a_ratio = float(bounds.x) / float(bounds.y); + float x_scale = float(cell.w - padding * 2) / float(bounds.x); + float y_new_size = (float(bounds.x) * x_scale) / a_ratio; + float y_scale = float(cell.h - padding * 2) / y_new_size; + + sprite->setScale({x_scale, y_scale}); } void Icon::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { window.draw(*sprite, shader_ptr); } - 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); From a22342cd7ee96b0a6b6047e85c36e0f5d9338a45 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 22 Jul 2025 16:22:05 -0400 Subject: [PATCH 02/13] Now Sprite can do either aspect_ratio scaling or stretching and Icon is just a subclass. --- demos/clicker_game.cpp | 2 +- include/guecs/sfml/components.hpp | 11 ++------ src/guecs/sfml/components.cpp | 43 +++++++------------------------ 3 files changed, 13 insertions(+), 43 deletions(-) diff --git a/demos/clicker_game.cpp b/demos/clicker_game.cpp index bb6cda7..d932de6 100644 --- a/demos/clicker_game.cpp +++ b/demos/clicker_game.cpp @@ -101,7 +101,7 @@ struct ClickerUI { } $clicker = $gui.entity("clicker"); - $gui.set($clicker, {"clicker_the_dog"}); + $gui.set($clicker, {"clicker_the_dog", 0, true}); $gui.set($clicker, {"clicker_bark"}); $gui.set($clicker, { [&](auto, auto) { handle_button(Event::CLICKER); } diff --git a/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index a323371..49ade35 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -40,6 +40,7 @@ namespace guecs { struct Sprite { string name; int padding = THEME.PADDING; + bool stretch = false; std::shared_ptr sprite = nullptr; void init(lel::Cell &cell); @@ -47,15 +48,7 @@ namespace guecs { void render(sf::RenderWindow& window, sf::Shader *shader_ptr); }; - struct Icon { - string name; - int padding = THEME.PADDING; - std::shared_ptr sprite = nullptr; - - void init(lel::Cell &cell); - void update(const string& new_name); - void render(sf::RenderWindow& window, sf::Shader *shader_ptr); - }; + struct Icon : public Sprite { }; struct Rectangle { int padding = THEME.PADDING; diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index 52c86cd..329a069 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -52,42 +52,19 @@ namespace guecs { sprite->setPosition({float(cell.x + padding), float(cell.y + padding)}); - sprite->setScale({ - float(cell.w - padding * 2) / float(bounds.x), - float(cell.h - padding * 2) / float(bounds.y)}); - } - - void Sprite::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { - window.draw(*sprite, shader_ptr); - } - - void Icon::update(const string& new_name) { - if(new_name != name) { - name = new_name; - auto sprite_texture = BACKEND->get_icon(name); - sprite->setTexture(*sprite_texture.texture); - sprite->setTextureRect({{0,0},sprite_texture.frame_size}); + if(stretch) { + sprite->setScale({ + float(cell.w - padding * 2) / float(bounds.x), + float(cell.h - padding * 2) / float(bounds.y)}); + } else { + float box_width = float(cell.w - padding * 2); + float box_height = float(cell.h - padding * 2); + float scale = std::min(box_width / float(bounds.x), box_height / float(bounds.y)); + sprite->setScale({scale, scale}); } } - void Icon::init(lel::Cell &cell) { - auto sprite_texture = BACKEND->get_icon(name); - auto bounds = sprite_texture.frame_size; - - sf::IntRect rect{{0,0}, bounds}; - sprite = make_shared(*sprite_texture.texture, rect); - - sprite->setPosition({ float(cell.x + padding), float(cell.y + padding)}); - - float a_ratio = float(bounds.x) / float(bounds.y); - float x_scale = float(cell.w - padding * 2) / float(bounds.x); - float y_new_size = (float(bounds.x) * x_scale) / a_ratio; - float y_scale = float(cell.h - padding * 2) / y_new_size; - - sprite->setScale({x_scale, y_scale}); - } - - void Icon::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { + void Sprite::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { window.draw(*sprite, shader_ptr); } From 3752522597999d08233f2c56292e954e42364aa3 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 22 Jul 2025 17:11:02 -0400 Subject: [PATCH 03/13] Fixed Icon vs. Sprite and now Icon is just a subclass of Sprite. Closes #12. --- demos/clicker_game.cpp | 4 ++-- include/guecs/sfml/components.hpp | 17 +++++++++++++++-- src/guecs/sfml/components.cpp | 10 +++++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/demos/clicker_game.cpp b/demos/clicker_game.cpp index d932de6..6e271ec 100644 --- a/demos/clicker_game.cpp +++ b/demos/clicker_game.cpp @@ -93,7 +93,7 @@ struct ClickerUI { if(name != "clicker") { $gui.set(id, {}); $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); } }); @@ -101,7 +101,7 @@ struct ClickerUI { } $clicker = $gui.entity("clicker"); - $gui.set($clicker, {"clicker_the_dog", 0, true}); + $gui.set($clicker, {"clicker_the_dog"}); $gui.set($clicker, {"clicker_bark"}); $gui.set($clicker, { [&](auto, auto) { handle_button(Event::CLICKER); } diff --git a/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index 49ade35..c7d989f 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -40,7 +40,8 @@ namespace guecs { struct Sprite { string name; int padding = THEME.PADDING; - bool stretch = false; + bool stretch = true; + bool is_icon = false; std::shared_ptr sprite = nullptr; void init(lel::Cell &cell); @@ -48,7 +49,19 @@ namespace guecs { void render(sf::RenderWindow& window, sf::Shader *shader_ptr); }; - struct Icon : public Sprite { }; + struct Icon : public Sprite { + template + Icon(Args... args) : Sprite(args...) + { + stretch = false; + is_icon = true; + } + + Icon() { + // BUG: why do I have to do this again? + stretch = false; + }; + }; struct Rectangle { int padding = THEME.PADDING; diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index 329a069..db46ad6 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -43,8 +43,16 @@ namespace guecs { } } + inline SpriteTexture load_texture(const string& name, bool is_icon) { + if(is_icon) { + return BACKEND->get_icon(name); + } else { + return BACKEND->get_sprite(name); + } + } + void Sprite::init(lel::Cell &cell) { - auto sprite_texture = BACKEND->get_sprite(name); + auto sprite_texture = load_texture(name, is_icon); auto bounds = sprite_texture.frame_size; sf::IntRect rect{{0,0}, bounds}; From 2c22da022f1eaf420af050b7ce6ca33e7d6021e6 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 23 Jul 2025 00:25:00 -0400 Subject: [PATCH 04/13] Text can be centered wither with the centered attribute on a class or with = in the spec. --- demos/clicker_game.cpp | 17 ++++++++--------- src/guecs/sfml/components.cpp | 3 ++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/demos/clicker_game.cpp b/demos/clicker_game.cpp index 6e271ec..7651ee1 100644 --- a/demos/clicker_game.cpp +++ b/demos/clicker_game.cpp @@ -73,7 +73,7 @@ struct ClickerUI { ClickerUI() { $gui.position(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); $gui.layout( - "[_|*%(300,400)clicker|_|_|_]" + "[_|=*%(300,400)clicker|_|_|_]" "[_|_ |_|_|_]" "[_|_ |_|_|_]" "[_|_ |_|_|_]" @@ -135,15 +135,14 @@ struct ClickerUI { using enum Event; switch(ev) { case CLICKER: { - auto& shaker = $gui.get($clicker); - auto& sprite = $gui.get($clicker); - shaker.play(sprite); - fmt::println("CLICKER LOVES YOU!"); - } break; + auto& shaker = $gui.get($clicker); + auto& sprite = $gui.get($clicker); + shaker.play(sprite); + fmt::println("CLICKER LOVES YOU!"); + } break; case A_BUTTON: - fmt::println("a button clicked"); - break; - + fmt::println("a button clicked"); + break; default: assert(false && "invalid event"); } diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index db46ad6..06c2f0f 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -13,7 +13,7 @@ namespace guecs { if(text == nullptr) text = make_shared(*font, content, size); text->setFillColor(color); - if(centered) { + if(centered || cell.center) { auto bounds = text->getLocalBounds(); auto text_cell = lel::center(bounds.size.x, bounds.size.y, cell); // this stupid / 2 is because SFML renders from baseline rather than from the claimed bounding box @@ -57,6 +57,7 @@ namespace guecs { sf::IntRect rect{{0,0}, bounds}; sprite = make_shared(*sprite_texture.texture, rect); + fmt::println("SPRITE centered: {}", cell.center); sprite->setPosition({float(cell.x + padding), float(cell.y + padding)}); From 9e9b9620c9314b776b200e06995f80f77ed85ce9 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 23 Jul 2025 12:48:29 -0400 Subject: [PATCH 05/13] Major refactoring but now centering text and sprites works. See Issue #16 for why only those and not anything else yet. --- demos/calc.cpp | 18 ++++++------- demos/clicker_game.cpp | 6 ++--- include/guecs/sfml/components.hpp | 29 +-------------------- src/guecs/lel.cpp | 1 + src/guecs/sfml/components.cpp | 43 ++++++++++++++++++++----------- src/guecs/ui.cpp | 42 ++++-------------------------- 6 files changed, 47 insertions(+), 92 deletions(-) diff --git a/demos/calc.cpp b/demos/calc.cpp index c8f69ab..c96ffea 100644 --- a/demos/calc.cpp +++ b/demos/calc.cpp @@ -141,12 +141,12 @@ struct CalculatorUI { $gui.layout( "[*%(400)stack |_|_|_]" "[*%(400)readout|_|_|_]" - "[push|pop|clear|eq]" - "[add |sub|mul |div]" - "[btn7|btn8|btn9]" - "[btn4|btn5|btn6]" - "[btn1|btn2|btn3]" - "[neg |btn0|del]"); + "[=push|=pop|=clear|=eq]" + "[=add |=sub|=mul |=div]" + "[=btn7|=btn8|=btn9]" + "[=btn4|=btn5|=btn6]" + "[=btn1|=btn2|=btn3]" + "[=neg |=btn0|=del]"); } void init() { @@ -161,12 +161,12 @@ struct CalculatorUI { $gui.set(id, {}); if(name == "readout") { - $gui.set(id, {L"", 40}); + $gui.set(id, {L"", 40}); } else if(name == "stack") { - $gui.set(id, {L"", 20}); + $gui.set(id, {L"", 20}); } else { $gui.set(id, {}); - $gui.set(id, { label }); + $gui.set(id, { label }); wchar_t op = label[0]; $gui.set(id, { [&, op](auto, auto) { handle_button(op); } diff --git a/demos/clicker_game.cpp b/demos/clicker_game.cpp index 7651ee1..7f41742 100644 --- a/demos/clicker_game.cpp +++ b/demos/clicker_game.cpp @@ -77,7 +77,7 @@ struct ClickerUI { "[_|_ |_|_|_]" "[_|_ |_|_|_]" "[_|_ |_|_|_]" - "[a1|a2|a3|a4|a5]"); + "[(30,30)a1|=(30,30)a2|a3|=a4|a5]"); } void init() { @@ -91,9 +91,9 @@ struct ClickerUI { for(auto& [name, cell] : $gui.cells()) { auto id = $gui.entity(name); if(name != "clicker") { - $gui.set(id, {}); + $gui.set(id, {0, {255, 0, 0, 255}}); $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 c7d989f..dc82033 100644 --- a/include/guecs/sfml/components.hpp +++ b/include/guecs/sfml/components.hpp @@ -10,7 +10,7 @@ namespace guecs { using std::shared_ptr, std::wstring, std::string; - struct Textual { + struct Text { std::wstring content; unsigned int size = THEME.TEXT_SIZE; sf::Color color = THEME.TEXT_COLOR; @@ -24,19 +24,6 @@ namespace guecs { void render(sf::RenderWindow& window, sf::Shader *shader_ptr); }; - struct Label : public Textual { - template - Label(Args... args) : Textual(args...) - { - centered = true; - size = THEME.LABEL_SIZE; - } - - Label() { - centered = true; - }; - }; - struct Sprite { string name; int padding = THEME.PADDING; @@ -49,20 +36,6 @@ 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() { - // BUG: why do I have to do this again? - stretch = false; - }; - }; - struct Rectangle { int padding = THEME.PADDING; sf::Color color = THEME.FILL_COLOR; diff --git a/src/guecs/lel.cpp b/src/guecs/lel.cpp index b363293..c8efccc 100644 --- a/src/guecs/lel.cpp +++ b/src/guecs/lel.cpp @@ -50,6 +50,7 @@ namespace lel { int cell_height = grid_h / rows; for(auto& row : grid) { + // BUG: see issue #16 size_t columns = row.size(); int cell_width = grid_w / columns; assert(cell_width > 0 && "invalid cell width calc"); diff --git a/src/guecs/sfml/components.cpp b/src/guecs/sfml/components.cpp index 06c2f0f..6dbb221 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -7,7 +7,28 @@ namespace guecs { using std::make_shared; - void Textual::init(lel::Cell &cell, shared_ptr font_ptr) { + template + void sfml_center_helper(T& 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); + } + + inline SpriteTexture load_texture(const string& name, bool is_icon) { + if(is_icon) { + return BACKEND->get_icon(name); + } else { + return BACKEND->get_sprite(name); + } + } + + void Text::init(lel::Cell &cell, shared_ptr font_ptr) { assert(font_ptr != nullptr && "you failed to initialize this WideText"); if(font == nullptr) font = font_ptr; if(text == nullptr) text = make_shared(*font, content, size); @@ -25,11 +46,11 @@ namespace guecs { text->setCharacterSize(size); } - void Textual::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { + void Text::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { window.draw(*text, shader_ptr); } - void Textual::update(const wstring& new_content) { + void Text::update(const wstring& new_content) { content = new_content; text->setString(content); } @@ -43,23 +64,14 @@ namespace guecs { } } - inline SpriteTexture load_texture(const string& name, bool is_icon) { - if(is_icon) { - return BACKEND->get_icon(name); - } else { - return BACKEND->get_sprite(name); - } - } - void Sprite::init(lel::Cell &cell) { + if(cell.center) stretch = false; + auto sprite_texture = load_texture(name, is_icon); auto bounds = sprite_texture.frame_size; sf::IntRect rect{{0,0}, bounds}; sprite = make_shared(*sprite_texture.texture, rect); - fmt::println("SPRITE centered: {}", cell.center); - - sprite->setPosition({float(cell.x + padding), float(cell.y + padding)}); if(stretch) { sprite->setScale({ @@ -71,6 +83,8 @@ namespace guecs { float scale = std::min(box_width / float(bounds.x), box_height / float(bounds.y)); sprite->setScale({scale, scale}); } + + sfml_center_helper(sprite, cell, padding); } void Sprite::render(sf::RenderWindow& window, sf::Shader *shader_ptr) { @@ -80,7 +94,6 @@ 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->setPosition({float(cell.x + padding), float(cell.y + padding)}); shape->setFillColor(color); shape->setOutlineColor(border_color); shape->setOutlineThickness(border_px); diff --git a/src/guecs/ui.cpp b/src/guecs/ui.cpp index b1824c6..5e8d498 100644 --- a/src/guecs/ui.cpp +++ b/src/guecs/ui.cpp @@ -82,21 +82,13 @@ namespace guecs { } }); - query([this](auto, auto& cell, auto& text) { - text.init(cell, $font); - }); - - query([this](auto, auto& cell, auto& text) { + query([this](auto, auto& cell, auto& text) { text.init(cell, $font); }); 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) { @@ -142,17 +134,7 @@ 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