Compare commits

...

32 commits
v0.4.0 ... main

Author SHA1 Message Date
Zed A. Shaw
9fffa51729 Update to the newest fuc2. 2026-06-17 01:55:09 -04:00
Zed A. Shaw
aa153f8e84 Use the newer way to wire up tests. 2026-06-16 02:08:01 -04:00
Zed A. Shaw
1d11c8b162 Now off catch2 and on my own testing fuc2. 2026-06-16 01:34:03 -04:00
Zed A. Shaw
4f3fa59793 Fix the linux reset. 2026-04-21 14:24:52 -04:00
Zed A. Shaw
2dbfac27c6 Better design that has a render/update cycle for components. 2026-04-20 16:02:13 -04:00
Zed A. Shaw
48b672eec4 Implement vertical meters. 2026-04-19 23:47:14 -04:00
Zed A. Shaw
ad116b5515 Improve the demo a bit. 2026-04-19 23:15:15 -04:00
Zed A. Shaw
c390ca5616 Make a update_percent function for meter. 2026-04-19 18:03:33 -04:00
Zed A. Shaw
1950f45f46 Meter is improved but needs a few more things. 2026-04-19 17:59:04 -04:00
Zed A. Shaw
3d44b7ccc5 Experimental cross platform utf8 support. 2026-04-17 00:45:06 -04:00
Zed A. Shaw
d6dd68bcef Version bump. 2026-04-09 00:16:32 -04:00
Zed A. Shaw
6aa1a877c9 Brought the UIStack over from the previous game to use here. 2026-04-08 23:01:05 -04:00
Zed A. Shaw
25e3935170 I need a way to ask if a cell exists for more dynamic layouts. 2026-04-04 14:50:15 -04:00
Zed A. Shaw
aff0fb839c Fix the build to work with new meson. 2026-03-31 13:04:11 -04:00
Zed A. Shaw
31adf011ef Same problem with background. 2026-03-11 07:56:05 -04:00
Zed A. Shaw
e88179b788 Small mistake. Didn't maintain the texture pointer in the sprite so it could go out of scope and crash with use after free. 2026-03-11 07:39:50 -04:00
Zed A. Shaw
516f20124f Fix a few little bugs. 2026-03-10 23:01:15 -04:00
Zed A. Shaw
3e9047177c Include the .wrap that I'm using. 2025-10-31 11:06:54 -04:00
Zed A. Shaw
e8c178db6f Changing from RenderWindow to RenderTarget so the GUI can be rendered into a texture for zooming/saving. 2025-10-31 11:05:29 -04:00
Zed A. Shaw
070244269e class enums are just jank when trying to work with a bitset. 2025-08-14 13:59:36 -04:00
Zed A. Shaw
41d568ab25 Bring back the convenience method to click on a cell by name. 2025-08-14 13:01:27 -04:00
Zed A. Shaw
4b4992185f Needed a default of no Modifiers to click_on since that's a very common operation. Also a constant to represent that. 2025-08-14 12:54:16 -04:00
Zed A. Shaw
5d0d8b16fc Removed hover on guecs::UI::mouse and now use a generic 16 bit modifier bitset. Also finally fixed Clickable so it just a simple callback with only the modifiers. 2025-08-14 12:35:25 -04:00
Zed A. Shaw
4c019048d0 Making Icons a thing again since it's convenient. 2025-07-23 13:49:16 -04:00
Zed A. Shaw
7e64879f78 Forgot to set the position on rectangle so just added the center helper. 2025-07-23 13:30:42 -04:00
Zed A. Shaw
0d91c554c6 Fix the clicker demo. 2025-07-23 12:51:51 -04:00
Zed A. Shaw
6837192583 Version bump. 2025-07-23 12:48:56 -04:00
Zed A. Shaw
9e9b9620c9 Major refactoring but now centering text and sprites works. See Issue #16 for why only those and not anything else yet. 2025-07-23 12:48:29 -04:00
Zed A. Shaw
2c22da022f Text can be centered wither with the centered attribute on a class or with = in the spec. 2025-07-23 00:25:00 -04:00
Zed A. Shaw
3752522597 Fixed Icon vs. Sprite and now Icon is just a subclass of Sprite. Closes #12. 2025-07-22 17:11:02 -04:00
Zed A. Shaw
a22342cd7e Now Sprite can do either aspect_ratio scaling or stretching and Icon is just a subclass. 2025-07-22 16:22:05 -04:00
Zed A. Shaw
b7cfa4db2d 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. 2025-07-22 15:55:05 -04:00
24 changed files with 787 additions and 293 deletions

View file

@ -26,7 +26,7 @@ tracy_build:
meson compile -j 10 -C builddir
test: build
./builddir/runtests
./builddir/fuc2it
run: build test
ifeq '$(OS)' 'Windows_NT'
@ -49,7 +49,7 @@ clean:
meson compile --clean -C builddir
debug_test: build
gdb --nx -x .gdbinit --ex run --args builddir/runtests -e
gdb --nx -x .gdbinit --ex run --args builddir/fuc2it -e
win_installer:
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp'

View file

@ -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<guecs::Rectangle>(id, {});
if(name == "readout") {
$gui.set<guecs::Textual>(id, {L"", 40});
$gui.set<guecs::Text>(id, {L"", 40});
} else if(name == "stack") {
$gui.set<guecs::Textual>(id, {L"", 20});
$gui.set<guecs::Text>(id, {L"", 20});
} else {
$gui.set<guecs::Effect>(id, {});
$gui.set<guecs::Label>(id, { label });
$gui.set<guecs::Text>(id, { label });
wchar_t op = label[0];
$gui.set<guecs::Clickable>(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) {

View file

@ -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<guecs::Rectangle>(id, {});
$gui.set<guecs::Effect>(id, {});
$gui.set<guecs::Icon>(id, { "clicker_treat_bone" });
$gui.set<guecs::Clickable>(id, {
[&](auto, auto) { handle_button(Event::A_BUTTON); }
[&](auto) { handle_button(Event::A_BUTTON); }
});
}
}
@ -104,7 +103,7 @@ struct ClickerUI {
$gui.set<guecs::Sprite>($clicker, {"clicker_the_dog"});
$gui.set<guecs::Sound>($clicker, {"clicker_bark"});
$gui.set<guecs::Clickable>($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<Shake>($clicker);
auto& sprite = $gui.get<guecs::Sprite>($clicker);
shaker.play(sprite);
fmt::println("CLICKER LOVES YOU!");
} break;
auto& shaker = $gui.get<Shake>($clicker);
auto& sprite = $gui.get<guecs::Sprite>($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");
}

124
demos/meters.cpp Normal file
View file

@ -0,0 +1,124 @@
#include "guecs/sfml/backend.hpp"
#include "guecs/sfml/components.hpp"
#include "guecs/uistack.hpp"
#include "guecs/ui.hpp"
#include <iostream>
#include <chrono>
#include <thread>
#include <flat_map>
#include <fmt/core.h>
using namespace std::chrono_literals;
struct TestMeters {
guecs::UI $gui{};
void init() {
$gui.position(0, 0, 1280, 720);
$gui.layout(
"[*%(300,100)meter1|_|_|*%(100,400)vert1|*%(100,400)vert2|*%(100,400)vert3]"
"[*%(300,100)meter2|_|_|_|_|_]"
"[*%(300,100)meter3|_|_|_|_|_]"
"[*%(300,100)meter4|_|_|_|_|_]");
for(auto& [name, cell] : $gui.cells()) {
auto gui_id = $gui.entity(name);
$gui.set<guecs::Rectangle>(gui_id, {guecs::THEME.PADDING, {120, 25, 25, 255}});
if(name.starts_with("meter")) {
$gui.set<guecs::Meter>(gui_id, {1.0f, guecs::THEME.DARK_LIGHT, guecs::THEME.PADDING});
} else {
$gui.set<guecs::Meter>(gui_id, {1.0f, guecs::THEME.DARK_LIGHT, guecs::THEME.PADDING, true});
}
$gui.set<guecs::Effect>(gui_id, {});
$gui.set<guecs::Text>(gui_id, {guecs::to_wstring(name)});
$gui.set<guecs::Clickable>(gui_id, {
[&, gui_id, name](auto) {
auto& meter = $gui.get<guecs::Meter>(gui_id);
auto& bg = $gui.get<guecs::Rectangle>(gui_id);
meter.percent = 1.0f;
if(meter.color.r == 255) {
meter.color = guecs::THEME.DARK_LIGHT;
bg.color = {120, 25, 25, 255};
} else {
meter.color = {255, 0, 0, 255};
bg.color = {0,255,0,255};
}
}
});
}
$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() {
$gui.update();
}
};
int main() {
try {
sfml::Backend backend;
guecs::init(&backend);
sf::RenderWindow window(sf::VideoMode({1280, 720}), "Meter Tester");
window.setVerticalSyncEnabled(true);
window.setFramerateLimit(60);
window.setPosition({0,0});
TestMeters gui;
gui.init();
while(window.isOpen()) {
while (const auto event = window.pollEvent()) {
if(event->is<sf::Event::Closed>()) {
window.close();
}
if(const auto* mouse = event->getIf<sf::Event::MouseButtonPressed>()) {
if(mouse->button == sf::Mouse::Button::Left) {
sf::Vector2f pos = window.mapPixelToCoords(mouse->position);
gui.mouse(pos.x, pos.y, false);
}
} else if(const auto* key = event->getIf<sf::Event::KeyPressed>()) {
using KEY = sf::Keyboard::Scan;
switch(key->scancode) {
case KEY::Q:
return 0;
default:
break;
}
}
}
gui.$gui.query<guecs::Meter>([](auto ent, auto& meter) {
if(meter.percent <= 0.0f) {
meter.percent = 1.0f;
} else {
meter.percent -= 0.01f * float(ent);
}
});
gui.update();
gui.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";
}
}

123
demos/multiscreen.cpp Normal file
View file

@ -0,0 +1,123 @@
#include "guecs/sfml/backend.hpp"
#include "guecs/sfml/components.hpp"
#include "guecs/uistack.hpp"
#include "guecs/ui.hpp"
#include <iostream>
#include <chrono>
#include <thread>
#include <flat_map>
#include <fmt/core.h>
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<guecs::Rectangle>(gui_id, {});
$gui.set<guecs::Effect>(gui_id, {});
$gui.set<guecs::Text>(gui_id, {guecs::to_wstring(name)});
$gui.set<guecs::Clickable>(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<TestScreen> stack;
auto test2 = std::make_shared<TestScreen>("[test2|sonice]");
auto test3 = std::make_shared<TestScreen>("[test3|pointer]");
{
stack.add("test1", std::make_shared<TestScreen>("[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<sf::Event::Closed>()) {
window.close();
}
if(const auto* mouse = event->getIf<sf::Event::MouseButtonPressed>()) {
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<sf::Event::KeyPressed>()) {
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";
}
}

View file

@ -1,3 +1,4 @@
#pragma once
#include "guecs/ui.hpp"
namespace sfml {

View file

@ -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;
@ -21,43 +21,38 @@ namespace guecs {
void init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr);
void update(const std::wstring& new_content);
void render(sf::RenderWindow& window, sf::Shader *shader_ptr);
};
struct Label : public Textual {
template<typename... Args>
Label(Args... args) : Textual(args...)
{
centered = true;
size = THEME.LABEL_SIZE;
}
Label() {
centered = true;
};
void render(sf::RenderTarget& window, sf::Shader *shader_ptr);
};
struct Sprite {
string name;
int padding = THEME.PADDING;
bool stretch = true;
bool is_icon = false;
std::shared_ptr<sf::Sprite> sprite = nullptr;
std::shared_ptr<sf::Texture> texture = nullptr;
void init(lel::Cell &cell);
void update(const string& new_name);
void render(sf::RenderWindow& window, sf::Shader *shader_ptr);
void render(sf::RenderTarget& window, sf::Shader *shader_ptr);
};
struct Icon {
string name;
int padding = THEME.PADDING;
std::shared_ptr<sf::Sprite> sprite = nullptr;
struct Icon : public Sprite {
template<typename... Args>
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 {
// 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;
@ -65,17 +60,29 @@ namespace guecs {
shared_ptr<sf::RectangleShape> shape = nullptr;
void init(lel::Cell& cell);
void render(sf::RenderWindow& window, sf::Shader *shader_ptr);
void render(sf::RenderTarget& window, sf::Shader *shader_ptr);
void update();
};
// 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;
Rectangle bar;
int padding = THEME.PADDING;
bool vertical = false;
Rectangle bar{padding, color};
// this is set automatically if a Rectangle is configured for the cell
size_t $cell_x = 0;
size_t $cell_y = 0;
size_t $cell_w = 0;
size_t $cell_h = 0;
void init(lel::Cell& cell);
void init(lel::Cell& cell, Rectangle& bg);
void render(lel::Cell& cell, sf::RenderWindow& window, sf::Shader *shader_ptr);
void render(sf::RenderTarget& window, sf::Shader *shader_ptr);
void update();
};
struct Effect {
@ -101,6 +108,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;
@ -109,6 +117,7 @@ namespace guecs {
sf::Color color=THEME.BG_COLOR;
shared_ptr<sf::RectangleShape> shape = nullptr;
shared_ptr<sf::Sprite> sprite = nullptr;
shared_ptr<sf::Texture> texture = nullptr;
Background(lel::Parser& parser, sf::Color bg_color=THEME.BG_COLOR) :
x(parser.grid_x),
@ -121,7 +130,7 @@ namespace guecs {
Background() {}
void init();
void render(sf::RenderWindow& window);
void render(sf::RenderTarget& window);
void set_color(sf::Color c);
void set_sprite(const std::string& name, bool stretch=false);

View file

@ -10,6 +10,7 @@
#include "guecs/theme.hpp"
#include "guecs/sfml/components.hpp"
#include <cassert>
#include <bitset>
namespace guecs {
using std::shared_ptr, std::wstring, std::string;
@ -24,22 +25,30 @@ namespace guecs {
std::queue<size_t> 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<void(Entity ent, std::any data)> action;
};
struct ActionData {
std::any data;
std::function<void(Modifiers mods)> action;
};
struct CellName {
string name;
};
class UI {
public:
Entity MAIN = 0;
@ -51,6 +60,9 @@ namespace guecs {
shared_ptr<sf::Font> $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();
@ -59,6 +71,7 @@ namespace guecs {
sf::Vector2f get_size();
void layout(const string& grid);
Entity init_entity(const string& name);
bool contains(const string& name);
Entity entity(const string& name);
Entity entity(const string& name, int id);
const std::string& name_for(Entity gui_id);
@ -68,11 +81,12 @@ 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);
void debug_layout(sf::RenderWindow& window);
void render(sf::RenderTarget& window);
void update();
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::RenderTarget& window);
Entity entity() { return ++entity_count; }
@ -211,15 +225,15 @@ namespace guecs {
}
template<typename T>
void render_helper(sf::RenderWindow& window, T& target, sf::Shader *shader_ptr) {
void render_helper(sf::RenderTarget& window, T& target, sf::Shader *shader_ptr) {
window.draw(*target, shader_ptr);
}
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);
};

97
include/guecs/uistack.hpp Normal file
View file

@ -0,0 +1,97 @@
#include <guecs/ui.hpp>
#include <flat_map>
#include <cassert>
namespace gui {
template<typename SCREEN_TYPE>
struct UIStack {
using UIStackMap = std::flat_map<std::string, std::shared_ptr<SCREEN_TYPE>>;
UIStackMap screens{};
bool visible = false;
UIStackMap::iterator $current{screens.begin()};
SCREEN_TYPE* $active = nullptr;
void add(const std::string& name, std::shared_ptr<SCREEN_TYPE> 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<SCREEN_TYPE> active_ui() {
assert($active != nullptr && "you didn't set active");
return (*$current).second;
}
std::shared_ptr<SCREEN_TYPE> at(const std::string& name) {
return screens.at(name);
}
const UIStackMap::key_container_type& names() {
return screens.keys();
}
};
}

9
lel-guecs.wrap Normal file
View file

@ -0,0 +1,9 @@
[wrap-git]
directory=lel-guecs-0.7.3
url=https://git.learnjsthehardway.com/learn-code-the-hard-way/lel-guecs.git
revision=HEAD
depth=1
method=meson
[provide]
lel_guecs = lel_guecs_dep

View file

@ -3,14 +3,21 @@
# 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.7.4',
default_options: [
'cpp_std=c++20',
'cpp_std=c++23',
'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1',
])
# use this for common options only for our executables
cpp_args=[]
cpp_args=[
'-I../subprojects/utf8cpp-4.0.9/source',
'-Wno-unused-parameter',
'-Wno-unused-function',
'-Wno-unused-variable',
'-Wno-unused-but-set-variable',
'-Wno-deprecated-declarations',
]
link_args=[]
# these are passed as override_defaults
exe_defaults = [ 'warning_level=2' ]
@ -30,10 +37,6 @@ if build_machine.system() == 'windows'
exe_defaults += ['werror=false']
elif build_machine.system() == 'darwin'
add_global_link_arguments(
language: 'cpp',
)
opengl = dependency('OpenGL')
corefoundation = dependency('CoreFoundation')
carbon = dependency('Carbon')
@ -48,10 +51,11 @@ elif build_machine.system() == 'darwin'
]
endif
catch2 = subproject('catch2').get_variable('catch2_with_main_dep')
fuc2 = subproject('fuc2').get_variable('fuc2_dep')
fmt = subproject('fmt').get_variable('fmt_dep')
json = subproject('nlohmann_json').get_variable('nlohmann_json_dep')
freetype2 = subproject('freetype2').get_variable('freetype_dep')
utf8cpp = subproject('utf8cpp').get_variable('utf8cpp_dep')
flac = subproject('flac').get_variable('flac_dep')
ogg = subproject('ogg').get_variable('libogg_dep')
@ -66,7 +70,7 @@ sfml_window = subproject('sfml').get_variable('sfml_window_dep')
lib_depends = [ fmt, sfml_graphics, sfml_system, sfml_window ]
lib_sfml_depends = [
fmt, json, freetype2, flac, ogg,
fmt, json, freetype2, flac, ogg, utf8cpp,
vorbis, vorbisfile, vorbisenc, sfml_audio,
]
@ -113,19 +117,20 @@ lel_guecs_sfml_dep = declare_dependency(
link_with: lel_guecs_sfml_lib,
include_directories: lel_guecs_inc)
executable('runtests', [
executable('fuc2it', [
'tests/guecs.cpp',
'tests/lel.cpp',
'tests/main.cpp',
'tests/shaders.cpp',
'tests/sound.cpp',
'tests/textures.cpp',
'tests/guecs.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 + [catch2])
dependencies: dependencies + [fuc2])
executable('clicker_game', [
'demos/clicker_game.cpp',
@ -146,3 +151,23 @@ 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)
executable('meters', [
'demos/meters.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)

0
scripts/reset_build.sh Normal file → Executable file
View file

View file

@ -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");

View file

@ -7,13 +7,34 @@
namespace guecs {
using std::make_shared;
void Textual::init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) {
// BUG: this seems to center things wrong in lel layouts
void sfml_center_helper(auto& 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<sf::Font> font_ptr) {
assert(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) {
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::RenderTarget& 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);
}
@ -38,85 +59,84 @@ namespace guecs {
if(new_name != name) {
name = new_name;
auto sprite_texture = BACKEND->get_sprite(name);
sprite->setTexture(*sprite_texture.texture);
texture = sprite_texture.texture;
sprite->setTexture(*texture);
sprite->setTextureRect({{0,0},sprite_texture.frame_size});
}
}
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};
sprite = make_shared<sf::Sprite>(*sprite_texture.texture, rect);
auto sprite_texture = load_texture(name, is_icon);
auto bounds = sprite_texture.frame_size;
sprite->setPosition({
float(cell.x + padding),
float(cell.y + padding)});
sf::IntRect rect{{0,0}, bounds};
texture = sprite_texture.texture;
sprite = make_shared<sf::Sprite>(*texture, rect);
auto bounds = sprite->getLocalBounds();
sprite->setScale({
float(cell.w - padding * 2) / bounds.size.x,
float(cell.h - padding * 2) / bounds.size.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});
}
sfml_center_helper(sprite, cell, padding);
}
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<sf::Sprite>(*sprite_texture.texture, rect);
sprite->setPosition({ float(cell.x + padding), float(cell.y + padding)});
}
void Icon::render(sf::RenderWindow& window, sf::Shader *shader_ptr) {
void Sprite::render(sf::RenderTarget& 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<sf::RectangleShape>(size);
shape->setPosition({float(cell.x + padding), float(cell.y + padding)});
update();
sfml_center_helper(shape, cell, padding);
}
void Rectangle::render(sf::RenderTarget& window, sf::Shader *shader_ptr) {
window.draw(*shape, shader_ptr);
}
void Rectangle::update() {
shape->setFillColor(color);
shape->setOutlineColor(border_color);
shape->setOutlineThickness(border_px);
}
void Rectangle::render(sf::RenderWindow& window, sf::Shader *shader_ptr) {
window.draw(*shape, shader_ptr);
}
void Meter::init(lel::Cell& cell, Rectangle& bg) {
bg.shape->setFillColor(color);
init(cell);
}
void Meter::init(lel::Cell& cell) {
$cell_x = cell.x;
$cell_y = cell.y;
$cell_w = cell.w;
$cell_h = cell.h;
bar.init(cell);
update();
}
void Meter::render(lel::Cell& cell, sf::RenderWindow& window, sf::Shader *shader_ptr) {
float level = std::clamp(percent, 0.0f, 1.0f) * float(cell.w);
// ZED: this 6 is a border width, make it a thing
bar.shape->setSize({std::max(level, 0.0f), float(cell.h - 6)});
window.draw(*bar.shape, shader_ptr);
void Meter::render(sf::RenderTarget& window, sf::Shader *shader_ptr) {
bar.render(window, shader_ptr);
}
void Meter::update() {
bar.color = color;
bar.padding = padding;
bar.update();
if(vertical) {
float level = std::clamp(percent, 0.0f, 1.0f) * float($cell_h);
sf::Vector2f size{float($cell_w) - padding * 2, std::max(level, 0.0f) - padding};
bar.shape->setSize(size);
bar.shape->setPosition({float($cell_x + padding), float($cell_y) + (float($cell_h) - size.y)});
} else {
float level = std::clamp(percent, 0.0f, 1.0f) * float($cell_w);
bar.shape->setSize({std::max(level, 0.0f) - padding * 2, float($cell_h) - padding * 2});
}
}
void Sound::play(bool hover) {
@ -135,7 +155,6 @@ namespace guecs {
assert(w > 0.0f && "Background.w must be > 0.0f. Forgot $gui.$parser?");
assert(h > 0.0f && "Background.h must be > 0.0f. Forgot $gui.$parser?");
std::cout << "x=" << x << " y=" << y << " w=" << w << " h=" << h << std::endl;
sf::Vector2f size{float(w), float(h)};
if(shape == nullptr) {
shape = make_shared<sf::RectangleShape>(size);
@ -155,7 +174,8 @@ namespace guecs {
auto sprite_texture = BACKEND->get_sprite(name);
sf::IntRect rect{{0,0},sprite_texture.frame_size};
sprite = make_shared<sf::Sprite>(*sprite_texture.texture, rect);
texture = sprite_texture.texture;
sprite = make_shared<sf::Sprite>(*texture, rect);
sprite->setPosition({float(x), float(y)});
@ -168,7 +188,7 @@ namespace guecs {
}
}
void Background::render(sf::RenderWindow& window) {
void Background::render(sf::RenderTarget& window) {
if(shape != nullptr) {
window.draw(*shape);
}

View file

@ -3,6 +3,7 @@
#include <cassert>
#include <fmt/core.h>
#include <iostream>
#include <utf8.h>
namespace guecs {
using std::make_shared;
@ -51,6 +52,10 @@ namespace guecs {
return $ents_name.at(gui_id);
}
bool UI::contains(const string& name) {
return $name_ents.contains(name);
}
Entity UI::entity(const string& name) {
assert($name_ents.contains(name) &&
"GUECS entity does not exist. Mispelled cell name?");
@ -75,18 +80,10 @@ namespace guecs {
});
query<lel::Cell, Meter>([this](auto ent, auto &cell, auto& meter) {
if(auto bg = get_if<Rectangle>(ent)) {
meter.init(cell, *bg);
} else {
meter.init(cell);
}
meter.init(cell);
});
query<lel::Cell, Textual>([this](auto, auto& cell, auto& text) {
text.init(cell, $font);
});
query<lel::Cell, Label>([this](auto, auto& cell, auto& text) {
query<lel::Cell, Text>([this](auto, auto& cell, auto& text) {
text.init(cell, $font);
});
@ -99,7 +96,7 @@ namespace guecs {
});
}
void UI::debug_layout(sf::RenderWindow& window) {
void UI::debug_layout(sf::RenderTarget& window) {
query<lel::Cell>([&](const auto, auto &cell) {
sf::RectangleShape rect{{float(cell.w), float(cell.h)}};
rect.setPosition({float(cell.x), float(cell.y)});
@ -118,7 +115,19 @@ namespace guecs {
}
}
void UI::render(sf::RenderWindow& window) {
void UI::update() {
query<Rectangle>([&](auto ent, auto& rect) {
rect.update();
});
query<Meter>([&](auto ent, auto &meter) {
meter.update();
});
}
// 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<Background>(MAIN)) {
bg->render(window);
}
@ -132,9 +141,9 @@ namespace guecs {
rect.render(window, shader_ptr);
});
query<lel::Cell, Meter>([&](auto ent, auto& cell, auto &meter) {
query<Meter>([&](auto ent, auto &meter) {
auto shader_ptr = find_shader(ent, true);
meter.render(cell, window, shader_ptr);
meter.render(window, shader_ptr);
});
query<Sprite>([&](auto ent, auto& sprite) {
@ -147,20 +156,15 @@ namespace guecs {
icon.render(window, shader_ptr);
});
query<Label>([&](auto ent, auto& text) {
auto shader_ptr = find_shader(ent, false);
text.render(window, shader_ptr);
});
query<Textual>([&](auto ent, auto& text) {
query<Text>([&](auto ent, auto& text) {
auto shader_ptr = find_shader(ent, false);
text.render(window, shader_ptr);
});
}
bool UI::mouse(float x, float y, bool hover) {
bool UI::mouse(float x, float y, Modifiers mods) {
int action_count = 0;
// BUG: Is Parser::hit useful?
bool hover = mods.test(size_t(ModBit::hover));
query<lel::Cell>([&](auto ent, auto& cell) {
if((x >= cell.x && x <= cell.x + cell.w) &&
@ -178,11 +182,11 @@ namespace guecs {
if(hover) return;
click_on(ent);
click_on(ent, mods);
action_count++;
} else {
do_if<Effect>(ent, [hover](auto& effect) {
do_if<Effect>(ent, [](auto& effect) {
effect.stop();
});
@ -206,56 +210,48 @@ namespace guecs {
}
}
void UI::show_icon(const string& region, const string& sprite_name) {
auto ent = entity(region);
if(auto sprite = get_if<Icon>(ent)) {
sprite->update(sprite_name);
} else {
set_init<Icon>(ent, {sprite_name});
}
}
void UI::show_text(const string& region, const wstring& content) {
auto ent = entity(region);
if(auto tc = get_if<Textual>(ent)) {
if(auto tc = get_if<Text>(ent)) {
tc->text->setString(content);
} else {
auto &cell = cell_for(ent);
Textual to_set{content, THEME.TEXT_SIZE};
Text to_set{content, THEME.TEXT_SIZE};
to_set.init(cell, $font);
set<Textual>(ent, to_set);
set<Text>(ent, to_set);
}
}
void UI::click_on(const string& name, bool required) {
auto ent = entity(name);
if(required) {
assert(has<Clickable>(ent) &&
"click_on required '{}' to exist but it doesn't");
}
click_on(ent);
void UI::click_on(const std::string& name, Modifiers mods) {
click_on(entity(name), mods);
}
void UI::click_on(Entity ent) {
if(auto clicked = get_if<Clickable>(ent)) {
if(auto action_data = get_if<ActionData>(ent)) {
clicked->action(ent, action_data->data);
} else {
clicked->action(ent, {});
}
}
}
void UI::show_label(const string& region, const wstring& content) {
auto ent = entity(region);
if(auto tc = get_if<Label>(ent)) {
tc->text->setString(content);
} else {
auto &cell = cell_for(ent);
Label to_set{content, THEME.LABEL_SIZE};
to_set.init(cell, $font);
to_set.text->setFillColor(THEME.TEXT_COLOR);
set<Label>(ent, to_set);
}
void UI::click_on(Entity gui_id, Modifiers mods) {
if(auto to_click = get_if<Clickable>(gui_id)) {
to_click->action(mods);
};
}
wstring to_wstring(const string& str) {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
return $converter.from_bytes(str);
std::wstring result = L"";
try {
utf8::utf8to32(str.begin(), str.end(), std::back_inserter(result));
} catch(const utf8::exception& e) {
std::cerr << e.what() << std::endl;
}
return result;
}
}

View file

@ -1,10 +1,20 @@
#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include "guecs/ui.hpp"
#include <fmt/xchar.h>
#include <fuc2/testing.hpp>
using namespace guecs;
using namespace fuc2;
TEST_CASE("prototype one gui", "[ecs-gui]") {
// accidentally gutted, will recover later
namespace guecs_tests {
void test_prototype() {
// accidentally gutted, will recover later
}
fuc2::Set TESTS{
.name="guecs",
.tests={
TEST(test_prototype),
}
};
}

View file

@ -1,61 +1,72 @@
#include "guecs/lel.hpp"
#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include <string>
#include <fuc2/testing.hpp>
TEST_CASE("test basic ops", "[lel]") {
lel::Parser parser(0, 0, 500, 500);
using namespace fuc2;
bool good = parser.parse(
"[ label_1 | label3 | test1]"
"[ *(300,300)text1 | %(150)people | ^test2]"
"[ >label2 | _ | .test3]"
"[ =message | buttons | test4]");
namespace lel_tests {
void test_basic_ops() {
lel::Parser parser(0, 0, 500, 500);
REQUIRE(good);
bool good = parser.parse(
"[ label_1 | label3 | test1]"
"[ *(300,300)text1 | %(150)people | ^test2]"
"[ >label2 | _ | .test3]"
"[ =message | buttons | test4]");
for(size_t rowcount = 0; rowcount < parser.grid.size(); rowcount++) {
auto& row = parser.grid[rowcount];
CHECK(good);
for(size_t colcount = 0; colcount < row.size(); colcount++) {
auto &name = row[colcount];
if(name == "_") {
REQUIRE(!parser.cells.contains(name));
} else if(name == "_MAIN") {
// it's the main cell which covers the whole area
auto &cell = parser.cells.at(name);
REQUIRE(cell.row == 0);
REQUIRE(cell.col == 0);
REQUIRE(cell.x == parser.grid_x);
REQUIRE(cell.y == parser.grid_y);
REQUIRE(cell.w == parser.grid_w);
REQUIRE(cell.h == parser.grid_h);
} else {
auto &cell = parser.cells.at(name);
REQUIRE(cell.row == int(rowcount));
REQUIRE(cell.col == int(colcount));
for(size_t rowcount = 0; rowcount < parser.grid.size(); rowcount++) {
auto& row = parser.grid[rowcount];
for(size_t colcount = 0; colcount < row.size(); colcount++) {
auto &name = row[colcount];
if(name == "_") {
CHECK(!parser.cells.contains(name));
} else if(name == "_MAIN") {
// it's the main cell which covers the whole area
auto &cell = parser.cells.at(name);
CHECK(cell.row == 0);
CHECK(cell.col == 0);
CHECK(cell.x == parser.grid_x);
CHECK(cell.y == parser.grid_y);
CHECK(cell.w == parser.grid_w);
CHECK(cell.h == parser.grid_h);
} else {
auto &cell = parser.cells.at(name);
CHECK(cell.row == int(rowcount));
CHECK(cell.col == int(colcount));
}
}
}
CHECK(parser.cells.size() == 11);
CHECK(parser.cells.at("label2").right == true);
CHECK(parser.cells.at("text1").expand == true);
CHECK(parser.cells.at("text1").w == 300);
CHECK(parser.cells.at("text1").h == 300);
CHECK(parser.cells.at("people").expand == false);
CHECK(parser.cells.at("message").expand == false);
CHECK(parser.cells.at("message").center == true);
for(auto& [name, cell] : parser.cells) {
CHECK(cell.w > 0);
CHECK(cell.h > 0);
}
auto hit = parser.hit(10, 10);
CHECK(*hit == "label_1");
auto nohit = parser.hit(1000, 1000);
CHECK(!nohit);
CHECK(nohit == std::nullopt);
}
REQUIRE(parser.cells.size() == 11);
REQUIRE(parser.cells.at("label2").right == true);
REQUIRE(parser.cells.at("text1").expand == true);
REQUIRE(parser.cells.at("text1").w == 300);
REQUIRE(parser.cells.at("text1").h == 300);
REQUIRE(parser.cells.at("people").expand == false);
REQUIRE(parser.cells.at("message").expand == false);
REQUIRE(parser.cells.at("message").center == true);
for(auto& [name, cell] : parser.cells) {
REQUIRE(cell.w > 0);
REQUIRE(cell.h > 0);
}
auto hit = parser.hit(10, 10);
REQUIRE(*hit == "label_1");
auto nohit = parser.hit(1000, 1000);
REQUIRE(!nohit);
REQUIRE(nohit == std::nullopt);
fuc2::Set TESTS{
.name="lel",
.tests={
TEST(test_basic_ops),
}
};
}

21
tests/main.cpp Normal file
View file

@ -0,0 +1,21 @@
#include <fuc2/run.hpp>
using namespace fuc2;
TEST_SET(lel_tests);
TEST_SET(guecs_tests);
TEST_SET(shader_tests);
TEST_SET(sound_tests);
TEST_SET(texture_tests);
int main(int argc, char* argv[]) {
std::vector<Set> all_tests{
lel_tests::TESTS,
guecs_tests::TESTS,
shader_tests::TESTS,
sound_tests::TESTS,
texture_tests::TESTS,
};
run_tests(all_tests, argc, argv);
}

View file

@ -1,26 +1,36 @@
#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include <string>
#include "guecs/sfml/shaders.hpp"
#include <fuc2/testing.hpp>
using namespace fuc2;
using namespace fmt;
TEST_CASE("shader loading/init works", "[shaders]") {
shaders::init();
int version = shaders::version();
namespace shader_tests {
void test_shader_loading() {
shaders::init();
int version = shaders::version();
std::shared_ptr<sf::Shader> ui_shader = shaders::get("ui_shader");
auto other_test = shaders::get("ui_shader");
std::shared_ptr<sf::Shader> ui_shader = shaders::get("ui_shader");
auto other_test = shaders::get("ui_shader");
REQUIRE(ui_shader != nullptr);
REQUIRE(ui_shader == other_test);
REQUIRE(shaders::updated(version) == false);
CHECK(ui_shader != nullptr);
CHECK(ui_shader == other_test);
CHECK(shaders::updated(version) == false);
int new_version = shaders::reload();
REQUIRE(version != shaders::version());
REQUIRE(version != new_version);
REQUIRE(shaders::version() == new_version);
REQUIRE(shaders::updated(version) == true);
version = new_version;
int new_version = shaders::reload();
CHECK(version != shaders::version());
CHECK(version != new_version);
CHECK(shaders::version() == new_version);
CHECK(shaders::updated(version) == true);
version = new_version;
}
fuc2::Set TESTS{
.name="shader",
.tests={
TEST(test_shader_loading),
}
};
}

View file

@ -1,14 +1,23 @@
#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include <string>
#include "guecs/sfml/sound.hpp"
#include <fuc2/testing.hpp>
using namespace fmt;
using namespace fuc2;
TEST_CASE("test sound manager", "[sound]") {
sound::init();
namespace sound_tests {
void test_sound_manager() {
sound::init();
sound::play("blank");
sound::play("blank");
sound::play_at("blank", 0.1, 0.1, 0.1);
sound::play_at("blank", 0.1, 0.1, 0.1);
}
fuc2::Set TESTS{
.name="sound",
.tests={
TEST(test_sound_manager),
}
};
}

View file

@ -1,15 +1,24 @@
#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include <string>
#include "guecs/sfml/textures.hpp"
#include <fuc2/testing.hpp>
using namespace fmt;
using namespace fuc2;
TEST_CASE("test texture management", "[textures]") {
textures::init();
auto spider = textures::get("textures_test");
REQUIRE(spider.sprite != nullptr);
REQUIRE(spider.texture != nullptr);
namespace texture_tests {
void test_texture_management() {
textures::init();
auto spider = textures::get("textures_test");
CHECK(spider.sprite != nullptr);
CHECK(spider.texture != nullptr);
auto image = textures::load_image("assets/textures_test.png");
auto image = textures::load_image("assets/textures_test.png");
}
fuc2::Set TESTS{
.name="texture",
.tests={
TEST(test_texture_management),
}
};
}

View file

@ -1,11 +0,0 @@
[wrap-file]
directory = Catch2-3.7.1
source_url = https://github.com/catchorg/Catch2/archive/v3.7.1.tar.gz
source_filename = Catch2-3.7.1.tar.gz
source_hash = c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.7.1-1/Catch2-3.7.1.tar.gz
wrapdb_version = 3.7.1-1
[provide]
catch2 = catch2_dep
catch2-with-main = catch2_with_main_dep

9
wraps/fuc2.wrap Normal file
View file

@ -0,0 +1,9 @@
[wrap-git]
directory=fuc2-0.1.0
url=https://lcthw.dev/cpp/fuc2.git
revision=HEAD
depth=1
method=meson
[provide]
fuc2 = fuc2_dep

9
wraps/utf8cpp.wrap Normal file
View file

@ -0,0 +1,9 @@
[wrap-git]
directory=utf8cpp-4.0.9
url=https://github.com/nemtrif/utfcpp.git
revision=v4.0.9
depth=1
method=cmake
[provide]
utf8cpp = utf8cpp_dep