A GUI library for games that's so small you won't even know its there.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
lel-guecs/demos/calc.cpp

230 lines
4.9 KiB

#include "sfml/components.hpp"
#include "sfml/sound.hpp"
#include "sfml/shaders.hpp"
#include "sfml/textures.hpp"
#include "guecs.hpp"
#include "constants.hpp"
#include <fmt/xchar.h>
#define FSM_DEBUG 1
#include "fsm.hpp"
constexpr const int WINDOW_WIDTH=300;
constexpr const int WINDOW_HEIGHT=400;
using std::string, std::wstring;
const std::unordered_map<string, wstring> LABELS {
{"readout", L""}, {"clear", L"CLR"}, {"btn0", L"0"}, {"btn1", L"1"},
{"btn2", L"2"}, {"btn3", L"3"}, {"btn4", L"4"},
{"btn5", L"5"}, {"btn6", L"6"}, {"btn7", L"7"},
{"btn8", L"8"}, {"btn9", L"9"}, {"mult", L"*"},
{"minus", L"-"}, {"plus", L"+"}, {"neg", L"±"},
{"div", L"/"}, {"eq", L"="} };
enum class Event {
NUMBER, ADD, SUB, MUL,
DIV, CLR, NOT, EQ,
};
enum class State {
START,
CALCULATING,
CLEARED,
DISPLAYED
};
struct Calculator : DeadSimpleFSM<State, Event> {
wstring input;
double value = 0.0;
void event(Event ev, wchar_t op) {
switch($state) {
FSM_STATE(State, START, ev, op);
FSM_STATE(State, CALCULATING, ev, op);
FSM_STATE(State, CLEARED, ev, op);
FSM_STATE(State, DISPLAYED, ev, op);
}
}
void START(Event ev, wchar_t op) {
if(ev == Event::NUMBER) {
input += op;
state(State::CALCULATING);
}
}
void CALCULATING(Event ev, wchar_t op) {
using enum Event;
switch(ev) {
case NUMBER:
input += op;
break;
case ADD:
value += std::stof(input);
input = L"";
break;
case SUB:
value -= std::stof(input);
input = L"";
break;
case MUL:
value *= std::stof(input);
input = L"";
break;
case DIV:
value /= std::stof(input);
input = L"";
break;
case NOT:
value = std::stof(input) * -1;
input = L"";
break;
case EQ:
value = std::stof(input);
break;
case CLR:
input = L"";
state(State::CLEARED);
break;
}
}
void CLEARED(Event ev, wchar_t op) {
if(ev == Event::NUMBER) {
input += op;
state(State::CALCULATING);
}
}
void DISPLAYED(Event ev, wchar_t op) {
fmt::println(L"ev={}, op={}", (int)ev, op);
state(State::CALCULATING);
}
};
struct CalculatorUI {
guecs::UI $gui;
Calculator $fsm;
CalculatorUI() {
$gui.position(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
$gui.layout(
"[*%(400)readout|_|_|_|clear]"
"[btn7|btn8|btn9|mult]"
"[btn4|btn5|btn6|minus]"
"[btn1|btn2|btn3|plus]"
"[neg|btn0|eq|div]");
}
void init() {
$gui.set<guecs::Background>($gui.MAIN, {});
for(auto& [name, cell] : $gui.cells()) {
auto id = $gui.entity(name);
auto& label = LABELS.at(name);
$gui.set<guecs::Rectangle>(id, {});
$gui.set<guecs::Effect>(id, {});
if(name == "readout") {
$gui.set<guecs::Textual>(id, {L"", 40});
} else {
$gui.set<guecs::Label>(id, { label });
$gui.set<guecs::Clickable>(id, {
[&, name](auto, auto) { handle_button(label[0]); }
});
}
}
$gui.init();
}
void render(sf::RenderWindow& window) {
$gui.render(window);
// $gui.debug_layout(window);
}
void mouse(float x, float y, bool hover) {
$gui.mouse(x, y, hover);
}
void handle_button(wchar_t op) {
using enum Event;
switch(op) {
case L'0':
case L'1':
case L'2':
case L'3':
case L'4':
case L'5':
case L'6':
case L'7':
case L'8':
case L'9':
$fsm.event(NUMBER, op);
break;
case L'*':
$fsm.event(MUL, op);
break;
case L'-':
$fsm.event(SUB, op);
break;
case L'+':
$fsm.event(ADD, op);
break;
case L'/':
$fsm.event(DIV, op);
break;
case L'±':
$fsm.event(NOT, op);
break;
case L'=':
$fsm.event(EQ, op);
break;
case L'C':
$fsm.event(CLR, op);
break;
}
auto readout = $gui.entity("readout");
auto& label = $gui.get<guecs::Textual>(readout);
label.update($fsm.input);
}
};
int main() {
sound::init();
shaders::init();
textures::init();
sf::RenderWindow window(sf::VideoMode({WINDOW_WIDTH, WINDOW_HEIGHT}), "LEL-GUECS Calculator");
window.setFramerateLimit(FRAME_LIMIT);
window.setVerticalSyncEnabled(VSYNC);
CalculatorUI calc;
calc.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);
calc.mouse(pos.x, pos.y, false);
}
}
}
calc.render(window);
window.display();
}
}