From 6aa1a877c9b080b2c47a3e68369731ff2e4ad9f9 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 8 Apr 2026 23:01:05 -0400 Subject: [PATCH] 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);