#include "sfml/components.hpp" #include "sfml/sound.hpp" #include "sfml/shaders.hpp" #include "sfml/textures.hpp" #include "guecs.hpp" #include "constants.hpp" #include #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; enum class Event { NUMBER, ADD, SUB, MUL, DIV, CLR, NEG, EQ, PUSH, POP }; 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"}, {"mul", L"*"}, {"sub", L"-"}, {"add", L"+"}, {"neg", L"±"}, {"div", L"/"}, {"eq", L"="}, {"push", L"^"}, {"pop", L"v"} }; const std::unordered_map EVENTS { {L'0', Event::NUMBER}, {L'1', Event::NUMBER}, {L'2', Event::NUMBER}, {L'3', Event::NUMBER}, {L'4', Event::NUMBER}, {L'5', Event::NUMBER}, {L'6', Event::NUMBER}, {L'7', Event::NUMBER}, {L'8', Event::NUMBER}, {L'9', Event::NUMBER}, {L'*', Event::MUL}, {L'-', Event::SUB}, {L'+', Event::ADD}, {L'/', Event::DIV}, {L'C', Event::CLR}, {L'^', Event::PUSH}, {L'±', Event::NEG}, {L'v', Event::POP}, {L'=', Event::EQ} }; struct Calculator { wstring input; std::deque stack; double temp = 0.0; double result = 0.0; void dump() { fmt::println("STACK: "); for(auto num : stack) { fmt::println("{}", num); } } void do_op(Event ev) { if(stack.size() < 2) return; using enum Event; double left = stack.back(); stack.pop_back(); double right = stack.back(); stack.pop_back(); switch(ev) { case MUL: temp = left * right; break; case ADD: temp = left + right; break; case SUB: temp = left - right; break; case DIV: temp = left / right; break; default: dbc::sentinel("invalid op passed to op()"); } stack.push_back(temp); } void event(Event ev, wchar_t op) { using enum Event; switch(ev) { case NUMBER: input += op; break; case MUL: do_op(MUL); break; case ADD: do_op(ADD); break; case SUB: do_op(SUB); break; case DIV: do_op(DIV); break; case CLR: input = L""; stack.clear(); break; case POP: if(!stack.empty()) { temp = stack.back(); input = fmt::format(L"{}", temp); stack.pop_back(); } break; case PUSH: if(input.size() > 0) { temp = std::stof(input); input = L""; stack.push_back(temp); } break; case NEG: if(input.size() > 0) { temp = std::stof(input); input = fmt::format(L"{}", temp * -1); } break; case EQ: break; } dump(); } }; struct CalculatorUI { guecs::UI $gui; Calculator $fsm; CalculatorUI() { $gui.position(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); $gui.layout( "[*%(400)readout|_|_|_]" "[push|pop|clear|eq]" "[add|sub|mul|div]" "[btn7|btn8|btn9]" "[btn4|btn5|btn6]" "[btn1|btn2|btn3]" "[neg|btn0|_]"); } 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 }); wchar_t op = label[0]; $gui.set(id, { [&, op](auto, auto) { handle_button(op); } }); } } $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) { $fsm.event(EVENTS.at(op), op); 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(); } }