#include "sfml/components.hpp" #include "sfml/sound.hpp" #include "sfml/shaders.hpp" #include "sfml/textures.hpp" #include "guecs.hpp" #include "constants.hpp" #include #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 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=0, OP=1, CLR=2, NOT=3, EQ=4 }; enum class State { START=0, CALCULATING=1, CLEARED=3, DISPLAYED=4 }; struct Calculator : DeadSimpleFSM { 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) { if(ev == Event::NUMBER) { input += op; } else if(ev == Event::OP) { } else if(ev == Event::NOT) { } else if(ev == Event::EQ) { } else if(ev == Event::CLR) { input = L""; state(State::CLEARED); } } 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($gui.MAIN, {}); for(auto& [name, cell] : $gui.cells()) { auto id = $gui.entity(name); auto& label = LABELS.at(name); $gui.set(id, {}); $gui.set(id, {}); if(name == "readout") { $gui.set(id, {L"", 40}); } else { $gui.set(id, { label }); $gui.set(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) { 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(Event::NUMBER, op); break; case L'*': case L'-': case L'+': case L'/': case L'!': $fsm.event(Event::OP, op); break; case L'=': $fsm.event(Event::EQ, op); break; case L'C': $fsm.event(Event::CLR, op); break; } auto readout = $gui.entity("readout"); auto& label = $gui.get(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()) { window.close(); } if(const auto* mouse = event->getIf()) { 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(); } }