Bring over the FSM and then use it to make the calculator demo better.
This commit is contained in:
parent
4b07ecac45
commit
c0c63e5b2c
2 changed files with 108 additions and 27 deletions
107
demos/calc.cpp
107
demos/calc.cpp
|
@ -6,6 +6,9 @@
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
#include <fmt/xchar.h>
|
#include <fmt/xchar.h>
|
||||||
|
|
||||||
|
#define FSM_DEBUG 1
|
||||||
|
#include "fsm.hpp"
|
||||||
|
|
||||||
constexpr const int WINDOW_WIDTH=300;
|
constexpr const int WINDOW_WIDTH=300;
|
||||||
constexpr const int WINDOW_HEIGHT=400;
|
constexpr const int WINDOW_HEIGHT=400;
|
||||||
|
|
||||||
|
@ -17,22 +20,85 @@ const std::unordered_map<string, wstring> LABELS {
|
||||||
{"btn5", L"5"}, {"btn6", L"6"}, {"btn7", L"7"},
|
{"btn5", L"5"}, {"btn6", L"6"}, {"btn7", L"7"},
|
||||||
{"btn8", L"8"}, {"btn9", L"9"}, {"mult", L"*"},
|
{"btn8", L"8"}, {"btn9", L"9"}, {"mult", L"*"},
|
||||||
{"minus", L"-"}, {"plus", L"+"}, {"neg", L"!"},
|
{"minus", L"-"}, {"plus", L"+"}, {"neg", L"!"},
|
||||||
{"dot", L"."}, {"eq", L"="}
|
{"div", L"/"}, {"eq", L"="}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Calculator {
|
enum class Event {
|
||||||
guecs::UI $gui;
|
NUMBER=0,
|
||||||
wstring $input;
|
OP=1,
|
||||||
double $value = 0.0;
|
CLR=2,
|
||||||
|
NOT=3,
|
||||||
|
EQ=4
|
||||||
|
};
|
||||||
|
|
||||||
Calculator() {
|
enum class State {
|
||||||
|
START=0,
|
||||||
|
CALCULATING=1,
|
||||||
|
CLEARED=3,
|
||||||
|
DISPLAYED=4
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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.position(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
$gui.layout(
|
$gui.layout(
|
||||||
"[*%(400)readout|_|_|_|clear]"
|
"[*%(400)readout|_|_|_|clear]"
|
||||||
"[btn7|btn8|btn9|mult]"
|
"[btn7|btn8|btn9|mult]"
|
||||||
"[btn4|btn5|btn6|minus]"
|
"[btn4|btn5|btn6|minus]"
|
||||||
"[btn1|btn2|btn3|plus]"
|
"[btn1|btn2|btn3|plus]"
|
||||||
"[neg|btn0|dot|eq]");
|
"[neg|btn0|eq|div]");
|
||||||
}
|
}
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
|
@ -79,39 +145,26 @@ struct Calculator {
|
||||||
case L'7':
|
case L'7':
|
||||||
case L'8':
|
case L'8':
|
||||||
case L'9':
|
case L'9':
|
||||||
case L'.':
|
$fsm.event(Event::NUMBER, op);
|
||||||
if($input.size() <= 10) {
|
|
||||||
$input += op;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case L'*':
|
case L'*':
|
||||||
$value = $value * std::stof($input);
|
|
||||||
$input = L"";
|
|
||||||
break;
|
|
||||||
case L'-':
|
case L'-':
|
||||||
$value = $value - std::stof($input);
|
|
||||||
$input = L"";
|
|
||||||
break;
|
|
||||||
case L'+':
|
case L'+':
|
||||||
$value = $value + std::stof($input);
|
case L'/':
|
||||||
$input = L"";
|
|
||||||
break;
|
|
||||||
case L'!':
|
case L'!':
|
||||||
$value = $value * -1.0;
|
$fsm.event(Event::OP, op);
|
||||||
$input = L"";
|
|
||||||
break;
|
break;
|
||||||
case L'=':
|
case L'=':
|
||||||
$input = fmt::format(L"{}", $value);
|
$fsm.event(Event::EQ, op);
|
||||||
$value = 0.0;
|
|
||||||
break;
|
break;
|
||||||
case L'C':
|
case L'C':
|
||||||
$input = L"";
|
$fsm.event(Event::CLR, op);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto readout = $gui.entity("readout");
|
auto readout = $gui.entity("readout");
|
||||||
auto& label = $gui.get<guecs::Textual>(readout);
|
auto& label = $gui.get<guecs::Textual>(readout);
|
||||||
label.update($input);
|
label.update($fsm.input);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,7 +179,7 @@ int main() {
|
||||||
window.setFramerateLimit(FRAME_LIMIT);
|
window.setFramerateLimit(FRAME_LIMIT);
|
||||||
window.setVerticalSyncEnabled(VSYNC);
|
window.setVerticalSyncEnabled(VSYNC);
|
||||||
|
|
||||||
Calculator calc;
|
CalculatorUI calc;
|
||||||
calc.init();
|
calc.init();
|
||||||
|
|
||||||
while(window.isOpen()) {
|
while(window.isOpen()) {
|
||||||
|
|
28
fsm.hpp
Normal file
28
fsm.hpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
#ifndef FSM_DEBUG
|
||||||
|
#define FSM_STATE(C, S, E, ...) case C::S: S(E, ##__VA_ARGS__); break
|
||||||
|
#else
|
||||||
|
#define FSM_STATE(C, S, E, ...) case C::S: fmt::println(">> " #C " " #S " event={}, state={}", int(E), int($state)); S(E, ##__VA_ARGS__); fmt::println("<< " #C " state={}", int($state)); break
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename S, typename E>
|
||||||
|
class DeadSimpleFSM {
|
||||||
|
protected:
|
||||||
|
// BUG: don't put this in your class because state() won't work
|
||||||
|
S $state = S::START;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename... Types>
|
||||||
|
void event(E event, Types... args);
|
||||||
|
|
||||||
|
void state(S next_state) {
|
||||||
|
$state = next_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in_state(S state) {
|
||||||
|
return $state == state;
|
||||||
|
}
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue