Compare commits
32 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fffa51729 | ||
|
|
aa153f8e84 | ||
|
|
1d11c8b162 | ||
|
|
4f3fa59793 | ||
|
|
2dbfac27c6 | ||
|
|
48b672eec4 | ||
|
|
ad116b5515 | ||
|
|
c390ca5616 | ||
|
|
1950f45f46 | ||
|
|
3d44b7ccc5 | ||
|
|
d6dd68bcef | ||
|
|
6aa1a877c9 | ||
|
|
25e3935170 | ||
|
|
aff0fb839c | ||
|
|
31adf011ef | ||
|
|
e88179b788 | ||
|
|
516f20124f | ||
|
|
3e9047177c | ||
|
|
e8c178db6f | ||
|
|
070244269e | ||
|
|
41d568ab25 | ||
|
|
4b4992185f | ||
|
|
5d0d8b16fc | ||
|
|
4c019048d0 | ||
|
|
7e64879f78 | ||
|
|
0d91c554c6 | ||
|
|
6837192583 | ||
|
|
9e9b9620c9 | ||
|
|
2c22da022f | ||
|
|
3752522597 | ||
|
|
a22342cd7e | ||
|
|
b7cfa4db2d |
24 changed files with 787 additions and 293 deletions
4
Makefile
4
Makefile
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
124
demos/meters.cpp
Normal 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
123
demos/multiscreen.cpp
Normal 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";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#pragma once
|
||||
#include "guecs/ui.hpp"
|
||||
|
||||
namespace sfml {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
97
include/guecs/uistack.hpp
Normal 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
9
lel-guecs.wrap
Normal 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
|
||||
49
meson.build
49
meson.build
|
|
@ -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
0
scripts/reset_build.sh
Normal file → Executable 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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
116
src/guecs/ui.cpp
116
src/guecs/ui.cpp
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
107
tests/lel.cpp
107
tests/lel.cpp
|
|
@ -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
21
tests/main.cpp
Normal 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);
|
||||
}
|
||||
|
|
@ -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),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
9
wraps/fuc2.wrap
Normal 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
9
wraps/utf8cpp.wrap
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue