#pragma once #include "color.hpp" #include "dinkyecs.hpp" #include "lel.hpp" #include <string> #include <memory> #include <SFML/Graphics.hpp> #include "textures.hpp" #include <functional> #include "events.hpp" #include "constants.hpp" #include "components.hpp" #include <any> namespace guecs { using std::shared_ptr, std::make_shared, std::wstring, std::string; struct Textual { std::wstring content; unsigned int size = GUECS_FONT_SIZE; sf::Color color = GUECS_TEXT_COLOR; int padding = GUECS_PADDING; bool centered = false; shared_ptr<sf::Font> font = nullptr; shared_ptr<sf::Text> text = nullptr; void init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) { dbc::check(font_ptr != nullptr, "you failed to initialize this WideText"); if(font == nullptr) font = font_ptr; if(text == nullptr) text = make_shared<sf::Text>(*font, content, size); text->setFillColor(color); if(centered) { dbc::log("TEXTUAL IS CENTERED"); 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 text->setPosition({float(text_cell.x), float(text_cell.y) - text_cell.h / 2}); } else { text->setPosition({float(cell.x + padding * 2), float(cell.y + padding * 2)}); } text->setCharacterSize(size); } void update(std::wstring& new_content) { content = new_content; text->setString(content); } }; struct Label : public Textual { template<typename... Args> Label(Args... args) : Textual(args...) { centered = true; } Label() { centered = true; }; }; 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<void(DinkyECS::Entity ent, std::any data)> action; }; struct Sprite { std::string name; int padding = GUECS_PADDING; std::shared_ptr<sf::Sprite> sprite = nullptr; std::shared_ptr<sf::Texture> texture = nullptr; void init(lel::Cell &cell) { auto sprite_texture = textures::get(name); texture = sprite_texture.texture; sprite = make_shared<sf::Sprite>(*texture); sprite->setPosition({ float(cell.x + padding), float(cell.y + padding)}); auto size = texture->getSize(); sprite->setScale({ float(cell.w - padding * 2) / size.x, float(cell.h - padding * 2) / size.y}); } }; struct Rectangle { int padding = GUECS_PADDING; sf::Color color = GUECS_FILL_COLOR; sf::Color border_color = GUECS_BORDER_COLOR; int border_px = GUECS_BORDER_PX; shared_ptr<sf::RectangleShape> shape = nullptr; void init(lel::Cell& cell) { sf::Vector2f size{float(cell.w) - padding * 2, float(cell.h) - padding * 2}; if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size); shape->setPosition({float(cell.x + padding), float(cell.y + padding)}); shape->setFillColor(color); shape->setOutlineColor(border_color); shape->setOutlineThickness(border_px); } }; struct Meter { float percent = 1.0f; Rectangle bar; void init(lel::Cell& cell) { bar.init(cell); } }; struct ActionData { std::any data; }; struct CellName { std::string name; }; struct Background { float x = 0.0f; float y = 0.0f; float w = 0.0f; float h = 0.0f; sf::Color color = GUECS_BG_COLOR; shared_ptr<sf::RectangleShape> shape = nullptr; Background(lel::Parser& parser, sf::Color bg_color=GUECS_BG_COLOR) : x(parser.grid_x), y(parser.grid_y), w(parser.grid_w), h(parser.grid_h), color(bg_color) {} Background() {} void init() { sf::Vector2f size{float(w), float(h)}; if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size); shape->setPosition({float(x), float(y)}); shape->setFillColor(color); } }; class UI { public: DinkyECS::World $world; std::unordered_map<std::string, DinkyECS::Entity> $name_ents; shared_ptr<sf::Font> $font = nullptr; lel::Parser $parser; std::string $grid = ""; UI(); void position(int x, int y, int width, int height); void layout(std::string grid); DinkyECS::Entity entity(std::string name); inline lel::CellMap& cells() { return $parser.cells; } inline DinkyECS::World& world() { return $world; } void init(); void render(sf::RenderWindow& window); bool mouse(float x, float y); void debug_layout(sf::RenderWindow& window); template <typename Comp> void set(DinkyECS::Entity ent, Comp val) { $world.set<Comp>(ent, val); } template <typename Comp> void set_init(DinkyECS::Entity ent, Comp val) { dbc::check(has<lel::Cell>(ent),"WRONG! slot is missing its cell?!"); auto& cell = get<lel::Cell>(ent); val.init(cell); $world.set<Comp>(ent, val); } lel::Cell& cell_for(DinkyECS::Entity entity) { return $world.get<lel::Cell>(entity); } template <typename Comp> Comp& get(DinkyECS::Entity entity) { return $world.get<Comp>(entity); } template <typename Comp> std::optional<Comp> get_if(DinkyECS::Entity entity) { return $world.get_if<Comp>(entity); } template <typename Comp> bool has(DinkyECS::Entity entity) { return $world.has<Comp>(entity); } template <typename Comp> void remove(DinkyECS::Entity ent) { $world.remove<Comp>(ent); } template <typename Comp> void close(string region) { auto ent = entity(region); remove<Comp>(ent); } void show_sprite(string region, string sprite_name); void show_text(string region, wstring content); void update_text(string region, wstring content); void update_label(string region, wstring content); void show_label(string region, wstring content); }; Clickable make_action(DinkyECS::World& target, Events::GUI event); }