diff --git a/demos/calc.cpp b/demos/calc.cpp index c8f69ab..8f38517 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,16 +161,16 @@ 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); } - }); + [&, 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 bb6cda7..c5e68b6 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|_|_|_]" "[_|_ |_|_|_]" "[_|_ |_|_|_]" "[_|_ |_|_|_]" @@ -91,11 +91,10 @@ struct ClickerUI { for(auto& [name, cell] : $gui.cells()) { auto id = $gui.entity(name); if(name != "clicker") { - $gui.set(id, {}); $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); } }); } } @@ -104,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 @@ -127,23 +126,22 @@ 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) { 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/include/guecs/sfml/components.hpp b/include/guecs/sfml/components.hpp index a323371..74d12bc 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,22 +24,11 @@ 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; + bool stretch = true; + bool is_icon = false; std::shared_ptr sprite = nullptr; void init(lel::Cell &cell); @@ -47,14 +36,18 @@ namespace guecs { void render(sf::RenderWindow& window, sf::Shader *shader_ptr); }; - struct Icon { - string name; - int padding = THEME.PADDING; - std::shared_ptr sprite = nullptr; + struct Icon : public Sprite { + template + Icon(Args... args) : Sprite(args...) + { + stretch = false; + is_icon = true; + } - void init(lel::Cell &cell); - void update(const string& new_name); - void render(sf::RenderWindow& window, sf::Shader *shader_ptr); + Icon() { + stretch = false; + is_icon = true; + }; }; struct Rectangle { diff --git a/include/guecs/ui.hpp b/include/guecs/ui.hpp index d66b063..bb1f7b7 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,30 @@ namespace guecs { std::queue free_indices; }; + namespace ModBit { + enum { + NONE=0, + hover=1, + left=2, + right=3 + }; + } + + using Modifiers = std::bitset<16>; + + constexpr const Modifiers NO_MODS{0}; + 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 +78,9 @@ 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(const std::string& name, Modifiers mods=NO_MODS); + void click_on(Entity slot_id, Modifiers mods=NO_MODS); void debug_layout(sf::RenderWindow& window); Entity entity() { return ++entity_count; } @@ -218,6 +227,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/meson.build b/meson.build index 830a626..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.4.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/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 064cc3a..e52097f 100644 --- a/src/guecs/sfml/components.cpp +++ b/src/guecs/sfml/components.cpp @@ -7,13 +7,34 @@ 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); 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 @@ -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); } @@ -44,59 +65,40 @@ namespace guecs { } void Sprite::init(lel::Cell &cell) { - auto sprite_texture = BACKEND->get_sprite(name); + if(cell.center) stretch = false; - sf::IntRect rect{{0,0},sprite_texture.frame_size}; + 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); - sprite->setPosition({ - float(cell.x + padding), - float(cell.y + padding)}); + 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}); + } - auto bounds = sprite->getLocalBounds(); - - sprite->setScale({ - float(cell.w - padding * 2) / bounds.size.x, - float(cell.h - padding * 2) / bounds.size.y}); + sfml_center_helper(sprite, cell, padding); } 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}); - } - } - - void Icon::init(lel::Cell &cell) { - auto sprite_texture = BACKEND->get_icon(name); - - 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); - sprite = make_shared(*sprite_texture.texture, rect); - - sprite->setPosition({ float(cell.x + padding), float(cell.y + padding)}); - } - - 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); - shape->setPosition({float(cell.x + padding), float(cell.y + padding)}); 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) { diff --git a/src/guecs/ui.cpp b/src/guecs/ui.cpp index b1824c6..1cbaffd 100644 --- a/src/guecs/ui.cpp +++ b/src/guecs/ui.cpp @@ -82,11 +82,7 @@ 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); }); @@ -147,20 +143,15 @@ namespace guecs { icon.render(window, shader_ptr); }); - query