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.
222 lines
5.2 KiB
222 lines
5.2 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>
|
|
#include <deque>
|
|
|
|
#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, DUP
|
|
};
|
|
|
|
const std::unordered_map<string, wstring> LABELS {
|
|
{"stack", L""}, {"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<wchar_t, Event> 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::DUP},
|
|
{L'=', Event::EQ} };
|
|
|
|
|
|
struct Calculator {
|
|
wstring input;
|
|
wstring curstack;
|
|
std::deque<double> stack;
|
|
double temp = 0.0;
|
|
double result = 0.0;
|
|
|
|
void display() {
|
|
curstack = L"";
|
|
for(auto num : stack) {
|
|
curstack += fmt::format(L"{} ", 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 DUP:
|
|
if(stack.size() > 0) {
|
|
input = fmt::format(L"{}", stack.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:
|
|
if(!stack.empty()) {
|
|
temp = stack.back();
|
|
input = fmt::format(L"{}", temp);
|
|
stack.pop_back();
|
|
}
|
|
break;
|
|
}
|
|
|
|
display();
|
|
}
|
|
};
|
|
|
|
struct CalculatorUI {
|
|
guecs::UI $gui;
|
|
Calculator $calc;
|
|
|
|
CalculatorUI() {
|
|
$gui.position(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
|
$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|_ ]");
|
|
}
|
|
|
|
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 if(name == "stack") {
|
|
$gui.set<guecs::Textual>(id, {L"", 20});
|
|
} else {
|
|
$gui.set<guecs::Label>(id, { label });
|
|
wchar_t op = label[0];
|
|
$gui.set<guecs::Clickable>(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) {
|
|
$calc.event(EVENTS.at(op), op);
|
|
$gui.show_text("readout", $calc.input);
|
|
$gui.show_text("stack", $calc.curstack);
|
|
}
|
|
};
|
|
|
|
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();
|
|
}
|
|
}
|
|
|