#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> #include "shaders.hpp" 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); void update(std::wstring& new_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); }; 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); }; struct Meter { float percent = 1.0f; Rectangle bar; void init(lel::Cell& cell); }; struct ActionData { std::any data; }; struct CellName { std::string name; }; struct Effect { float duration = 0.1f; std::string name{"ui_shader"}; float $u_time_end = 0.0; bool $active = false; std::shared_ptr<sf::Clock> $clock = nullptr; std::shared_ptr<sf::Shader> $shader = nullptr; int $shader_version = 0; void init(lel::Cell &cell); void run(); void step(); shared_ptr<sf::Shader> checkout_ptr(); }; struct Sound { std::string on_click{"ui_click"}; void play(bool hover); }; 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(); }; 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 init_entity(std::string name); 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, bool hover); 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); } template <typename Comp> void do_if(DinkyECS::Entity ent, std::function<void(Comp &)> cb) { if($world.has<Comp>(ent)) { cb($world.get<Comp>(ent)); } } lel::Cell& cell_for(DinkyECS::Entity ent) { return $world.get<lel::Cell>(ent); } lel::Cell& cell_for(std::string name) { DinkyECS::Entity ent = entity(name); return $world.get<lel::Cell>(ent); } 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); } template<typename T> void render_helper(sf::RenderWindow& window, DinkyECS::Entity ent, bool is_shape, T& target) { sf::Shader *shader_ptr = nullptr; if($world.has<Effect>(ent)) { auto& shader = $world.get<Effect>(ent); if(shader.$active) { auto ptr = shader.checkout_ptr(); ptr->setUniform("is_shape", is_shape); // NOTE: this is needed because SFML doesn't handle shared_ptr shader_ptr = ptr.get(); } } window.draw(*target, shader_ptr); } 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); }