Barely working stripped down version of ScreenInteractive. Now to bring on the SFML events.
This commit is contained in:
parent
7d3605f58b
commit
e3cff8142c
9 changed files with 1070 additions and 7 deletions
2
gui.cpp
2
gui.cpp
|
@ -33,7 +33,7 @@ GUI::GUI(DinkyECS::World &world, Map& game_map) :
|
||||||
$game_map(game_map),
|
$game_map(game_map),
|
||||||
$log({{"Welcome to the game!"}}),
|
$log({{"Welcome to the game!"}}),
|
||||||
$status_ui(SCREEN_X, SCREEN_Y, 0, 0),
|
$status_ui(SCREEN_X, SCREEN_Y, 0, 0),
|
||||||
$map_view(0, 0, GAME_MAP_POS, 0, false),
|
$map_view(30, 10, GAME_MAP_POS, 0, false),
|
||||||
$view_port{0,0},
|
$view_port{0,0},
|
||||||
$world(world),
|
$world(world),
|
||||||
$sounds("./assets"),
|
$sounds("./assets"),
|
||||||
|
|
419
input_parser.cpp
Normal file
419
input_parser.cpp
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
#include "input_parser.hpp"
|
||||||
|
|
||||||
|
#include <cstdint> // for uint32_t
|
||||||
|
#include <ftxui/component/mouse.hpp> // for Mouse, Mouse::Button, Mouse::Motion
|
||||||
|
#include <ftxui/component/receiver.hpp> // for SenderImpl, Sender
|
||||||
|
#include <map>
|
||||||
|
#include <memory> // for unique_ptr, allocator
|
||||||
|
#include <utility> // for move
|
||||||
|
|
||||||
|
#include "ftxui/component/event.hpp" // for Event
|
||||||
|
#include "ftxui/component/task.hpp" // for Task
|
||||||
|
|
||||||
|
namespace ftxui {
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE
|
||||||
|
const std::map<std::string, std::string> g_uniformize = {
|
||||||
|
// Microsoft's terminal uses a different new line character for the return
|
||||||
|
// key. This also happens with linux with the `bind` command:
|
||||||
|
// See https://github.com/ArthurSonzogni/FTXUI/issues/337
|
||||||
|
// Here, we uniformize the new line character to `\n`.
|
||||||
|
{"\r", "\n"},
|
||||||
|
|
||||||
|
// See: https://github.com/ArthurSonzogni/FTXUI/issues/508
|
||||||
|
{std::string({8}), std::string({127})},
|
||||||
|
|
||||||
|
// See: https://github.com/ArthurSonzogni/FTXUI/issues/626
|
||||||
|
//
|
||||||
|
// Depending on the Cursor Key Mode (DECCKM), the terminal sends different
|
||||||
|
// escape sequences:
|
||||||
|
//
|
||||||
|
// Key Normal Application
|
||||||
|
// ----- -------- -----------
|
||||||
|
// Up ESC [ A ESC O A
|
||||||
|
// Down ESC [ B ESC O B
|
||||||
|
// Right ESC [ C ESC O C
|
||||||
|
// Left ESC [ D ESC O D
|
||||||
|
// Home ESC [ H ESC O H
|
||||||
|
// End ESC [ F ESC O F
|
||||||
|
//
|
||||||
|
{"\x1BOA", "\x1B[A"}, // UP
|
||||||
|
{"\x1BOB", "\x1B[B"}, // DOWN
|
||||||
|
{"\x1BOC", "\x1B[C"}, // RIGHT
|
||||||
|
{"\x1BOD", "\x1B[D"}, // LEFT
|
||||||
|
{"\x1BOH", "\x1B[H"}, // HOME
|
||||||
|
{"\x1BOF", "\x1B[F"}, // END
|
||||||
|
|
||||||
|
// Variations around the FN keys.
|
||||||
|
// Internally, we are using:
|
||||||
|
// vt220, xterm-vt200, xterm-xf86-v44, xterm-new, mgt, screen
|
||||||
|
// See: https://invisible-island.net/xterm/xterm-function-keys.html
|
||||||
|
|
||||||
|
// For linux OS console (CTRL+ALT+FN), who do not belong to any
|
||||||
|
// real standard.
|
||||||
|
// See: https://github.com/ArthurSonzogni/FTXUI/issues/685
|
||||||
|
{"\x1B[[A", "\x1BOP"}, // F1
|
||||||
|
{"\x1B[[B", "\x1BOQ"}, // F2
|
||||||
|
{"\x1B[[C", "\x1BOR"}, // F3
|
||||||
|
{"\x1B[[D", "\x1BOS"}, // F4
|
||||||
|
{"\x1B[[E", "\x1B[15~"}, // F5
|
||||||
|
|
||||||
|
// xterm-r5, xterm-r6, rxvt
|
||||||
|
{"\x1B[11~", "\x1BOP"}, // F1
|
||||||
|
{"\x1B[12~", "\x1BOQ"}, // F2
|
||||||
|
{"\x1B[13~", "\x1BOR"}, // F3
|
||||||
|
{"\x1B[14~", "\x1BOS"}, // F4
|
||||||
|
|
||||||
|
// vt100
|
||||||
|
{"\x1BOt", "\x1B[15~"}, // F5
|
||||||
|
{"\x1BOu", "\x1B[17~"}, // F6
|
||||||
|
{"\x1BOv", "\x1B[18~"}, // F7
|
||||||
|
{"\x1BOl", "\x1B[19~"}, // F8
|
||||||
|
{"\x1BOw", "\x1B[20~"}, // F9
|
||||||
|
{"\x1BOx", "\x1B[21~"}, // F10
|
||||||
|
|
||||||
|
// scoansi
|
||||||
|
{"\x1B[M", "\x1BOP"}, // F1
|
||||||
|
{"\x1B[N", "\x1BOQ"}, // F2
|
||||||
|
{"\x1B[O", "\x1BOR"}, // F3
|
||||||
|
{"\x1B[P", "\x1BOS"}, // F4
|
||||||
|
{"\x1B[Q", "\x1B[15~"}, // F5
|
||||||
|
{"\x1B[R", "\x1B[17~"}, // F6
|
||||||
|
{"\x1B[S", "\x1B[18~"}, // F7
|
||||||
|
{"\x1B[T", "\x1B[19~"}, // F8
|
||||||
|
{"\x1B[U", "\x1B[20~"}, // F9
|
||||||
|
{"\x1B[V", "\x1B[21~"}, // F10
|
||||||
|
{"\x1B[W", "\x1B[23~"}, // F11
|
||||||
|
{"\x1B[X", "\x1B[24~"}, // F12
|
||||||
|
};
|
||||||
|
|
||||||
|
TerminalInputParser::TerminalInputParser(Sender<Task> out)
|
||||||
|
: out_(std::move(out)) {}
|
||||||
|
|
||||||
|
void TerminalInputParser::Timeout(int time) {
|
||||||
|
timeout_ += time;
|
||||||
|
const int timeout_threshold = 50;
|
||||||
|
if (timeout_ < timeout_threshold) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timeout_ = 0;
|
||||||
|
if (!pending_.empty()) {
|
||||||
|
Send(SPECIAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalInputParser::Add(char c) {
|
||||||
|
pending_ += c;
|
||||||
|
timeout_ = 0;
|
||||||
|
position_ = -1;
|
||||||
|
Send(Parse());
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char TerminalInputParser::Current() {
|
||||||
|
return pending_[position_];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TerminalInputParser::Eat() {
|
||||||
|
position_++;
|
||||||
|
return position_ < static_cast<int>(pending_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalInputParser::Send(TerminalInputParser::Output output) {
|
||||||
|
switch (output.type) {
|
||||||
|
case UNCOMPLETED:
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DROP:
|
||||||
|
pending_.clear();
|
||||||
|
return;
|
||||||
|
|
||||||
|
case CHARACTER:
|
||||||
|
out_->Send(Event::Character(std::move(pending_)));
|
||||||
|
pending_.clear();
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SPECIAL: {
|
||||||
|
auto it = g_uniformize.find(pending_);
|
||||||
|
if (it != g_uniformize.end()) {
|
||||||
|
pending_ = it->second;
|
||||||
|
}
|
||||||
|
out_->Send(Event::Special(std::move(pending_)));
|
||||||
|
pending_.clear();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case MOUSE:
|
||||||
|
out_->Send(Event::Mouse(std::move(pending_), output.mouse)); // NOLINT
|
||||||
|
pending_.clear();
|
||||||
|
return;
|
||||||
|
|
||||||
|
case CURSOR_REPORTING:
|
||||||
|
out_->Send(Event::CursorReporting(std::move(pending_), // NOLINT
|
||||||
|
output.cursor.x, // NOLINT
|
||||||
|
output.cursor.y)); // NOLINT
|
||||||
|
pending_.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// NOT_REACHED().
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalInputParser::Output TerminalInputParser::Parse() {
|
||||||
|
if (!Eat()) {
|
||||||
|
return UNCOMPLETED;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (Current()) {
|
||||||
|
case 24: // CAN NOLINT
|
||||||
|
case 26: // SUB NOLINT
|
||||||
|
return DROP;
|
||||||
|
|
||||||
|
case '\x1B':
|
||||||
|
return ParseESC();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Current() < 32) { // C0 NOLINT
|
||||||
|
return SPECIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Current() == 127) { // Delete // NOLINT
|
||||||
|
return SPECIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseUTF8();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code point <-> UTF-8 conversion
|
||||||
|
//
|
||||||
|
// ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┓
|
||||||
|
// ┃Byte 1 ┃Byte 2 ┃Byte 3 ┃Byte 4 ┃
|
||||||
|
// ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━┩
|
||||||
|
// │0xxxxxxx│ │ │ │
|
||||||
|
// ├────────┼────────┼────────┼────────┤
|
||||||
|
// │110xxxxx│10xxxxxx│ │ │
|
||||||
|
// ├────────┼────────┼────────┼────────┤
|
||||||
|
// │1110xxxx│10xxxxxx│10xxxxxx│ │
|
||||||
|
// ├────────┼────────┼────────┼────────┤
|
||||||
|
// │11110xxx│10xxxxxx│10xxxxxx│10xxxxxx│
|
||||||
|
// └────────┴────────┴────────┴────────┘
|
||||||
|
//
|
||||||
|
// Then some sequences are illegal if it exist a shorter representation of the
|
||||||
|
// same codepoint.
|
||||||
|
TerminalInputParser::Output TerminalInputParser::ParseUTF8() {
|
||||||
|
auto head = Current();
|
||||||
|
unsigned char selector = 0b1000'0000; // NOLINT
|
||||||
|
|
||||||
|
// The non code-point part of the first byte.
|
||||||
|
unsigned char mask = selector;
|
||||||
|
|
||||||
|
// Find the first zero in the first byte.
|
||||||
|
unsigned int first_zero = 8; // NOLINT
|
||||||
|
for (unsigned int i = 0; i < 8; ++i) { // NOLINT
|
||||||
|
mask |= selector;
|
||||||
|
if (!(head & selector)) {
|
||||||
|
first_zero = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
selector >>= 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate the value of the first byte.
|
||||||
|
auto value = uint32_t(head & ~mask); // NOLINT
|
||||||
|
|
||||||
|
// Invalid UTF8, with more than 5 bytes.
|
||||||
|
const unsigned int max_utf8_bytes = 5;
|
||||||
|
if (first_zero == 1 || first_zero >= max_utf8_bytes) {
|
||||||
|
return DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multi byte UTF-8.
|
||||||
|
for (unsigned int i = 2; i <= first_zero; ++i) {
|
||||||
|
if (!Eat()) {
|
||||||
|
return UNCOMPLETED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid continuation byte.
|
||||||
|
head = Current();
|
||||||
|
if ((head & 0b1100'0000) != 0b1000'0000) { // NOLINT
|
||||||
|
return DROP;
|
||||||
|
}
|
||||||
|
value <<= 6; // NOLINT
|
||||||
|
value += head & 0b0011'1111; // NOLINT
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for overlong UTF8 encoding.
|
||||||
|
int extra_byte = 0;
|
||||||
|
if (value <= 0b000'0000'0111'1111) { // NOLINT
|
||||||
|
extra_byte = 0; // NOLINT
|
||||||
|
} else if (value <= 0b000'0111'1111'1111) { // NOLINT
|
||||||
|
extra_byte = 1; // NOLINT
|
||||||
|
} else if (value <= 0b1111'1111'1111'1111) { // NOLINT
|
||||||
|
extra_byte = 2; // NOLINT
|
||||||
|
} else if (value <= 0b1'0000'1111'1111'1111'1111) { // NOLINT
|
||||||
|
extra_byte = 3; // NOLINT
|
||||||
|
} else { // NOLINT
|
||||||
|
return DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extra_byte != position_) {
|
||||||
|
return DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CHARACTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalInputParser::Output TerminalInputParser::ParseESC() {
|
||||||
|
if (!Eat()) {
|
||||||
|
return UNCOMPLETED;
|
||||||
|
}
|
||||||
|
switch (Current()) {
|
||||||
|
case 'P':
|
||||||
|
return ParseDCS();
|
||||||
|
case '[':
|
||||||
|
return ParseCSI();
|
||||||
|
case ']':
|
||||||
|
return ParseOSC();
|
||||||
|
default:
|
||||||
|
if (!Eat()) {
|
||||||
|
return UNCOMPLETED;
|
||||||
|
} else {
|
||||||
|
return SPECIAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalInputParser::Output TerminalInputParser::ParseDCS() {
|
||||||
|
// Parse until the string terminator ST.
|
||||||
|
while (true) {
|
||||||
|
if (!Eat()) {
|
||||||
|
return UNCOMPLETED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Current() != '\x1B') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Eat()) {
|
||||||
|
return UNCOMPLETED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Current() != '\\') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SPECIAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalInputParser::Output TerminalInputParser::ParseCSI() {
|
||||||
|
bool altered = false;
|
||||||
|
int argument = 0;
|
||||||
|
std::vector<int> arguments;
|
||||||
|
while (true) {
|
||||||
|
if (!Eat()) {
|
||||||
|
return UNCOMPLETED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Current() == '<') {
|
||||||
|
altered = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Current() >= '0' && Current() <= '9') {
|
||||||
|
argument *= 10; // NOLINT
|
||||||
|
argument += Current() - '0';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Current() == ';') {
|
||||||
|
arguments.push_back(argument);
|
||||||
|
argument = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSI is terminated by a character in the range 0x40–0x7E
|
||||||
|
// (ASCII @A–Z[\]^_`a–z{|}~),
|
||||||
|
if (Current() >= '@' && Current() <= '~' &&
|
||||||
|
// Note: I don't remember why we exclude '<'
|
||||||
|
Current() != '<' &&
|
||||||
|
// To handle F1-F4, we exclude '['.
|
||||||
|
Current() != '[') {
|
||||||
|
arguments.push_back(argument);
|
||||||
|
argument = 0; // NOLINT
|
||||||
|
|
||||||
|
switch (Current()) {
|
||||||
|
case 'M':
|
||||||
|
return ParseMouse(altered, true, std::move(arguments));
|
||||||
|
case 'm':
|
||||||
|
return ParseMouse(altered, false, std::move(arguments));
|
||||||
|
case 'R':
|
||||||
|
return ParseCursorReporting(std::move(arguments));
|
||||||
|
default:
|
||||||
|
return SPECIAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid ESC in CSI.
|
||||||
|
if (Current() == '\x1B') {
|
||||||
|
return SPECIAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalInputParser::Output TerminalInputParser::ParseOSC() {
|
||||||
|
// Parse until the string terminator ST.
|
||||||
|
while (true) {
|
||||||
|
if (!Eat()) {
|
||||||
|
return UNCOMPLETED;
|
||||||
|
}
|
||||||
|
if (Current() != '\x1B') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!Eat()) {
|
||||||
|
return UNCOMPLETED;
|
||||||
|
}
|
||||||
|
if (Current() != '\\') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return SPECIAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalInputParser::Output TerminalInputParser::ParseMouse( // NOLINT
|
||||||
|
bool altered,
|
||||||
|
bool pressed,
|
||||||
|
std::vector<int> arguments) {
|
||||||
|
if (arguments.size() != 3) {
|
||||||
|
return SPECIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)altered;
|
||||||
|
|
||||||
|
Output output(MOUSE);
|
||||||
|
output.mouse.button = Mouse::Button((arguments[0] & 3) + // NOLINT
|
||||||
|
((arguments[0] & 64) >> 4)); // NOLINT
|
||||||
|
output.mouse.motion = Mouse::Motion(pressed); // NOLINT
|
||||||
|
output.mouse.shift = bool(arguments[0] & 4); // NOLINT
|
||||||
|
output.mouse.meta = bool(arguments[0] & 8); // NOLINT
|
||||||
|
output.mouse.x = arguments[1]; // NOLINT
|
||||||
|
output.mouse.y = arguments[2]; // NOLINT
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE
|
||||||
|
TerminalInputParser::Output TerminalInputParser::ParseCursorReporting(
|
||||||
|
std::vector<int> arguments) {
|
||||||
|
if (arguments.size() != 2) {
|
||||||
|
return SPECIAL;
|
||||||
|
}
|
||||||
|
Output output(CURSOR_REPORTING);
|
||||||
|
output.cursor.y = arguments[0]; // NOLINT
|
||||||
|
output.cursor.x = arguments[1]; // NOLINT
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ftxui
|
72
input_parser.hpp
Normal file
72
input_parser.hpp
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
#ifndef FTXUI_COMPONENT_TERMINAL_INPUT_PARSER
|
||||||
|
#define FTXUI_COMPONENT_TERMINAL_INPUT_PARSER
|
||||||
|
|
||||||
|
#include <memory> // for unique_ptr
|
||||||
|
#include <string> // for string
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
|
#include "ftxui/component/event.hpp" // for Event (ptr only)
|
||||||
|
#include "ftxui/component/mouse.hpp" // for Mouse
|
||||||
|
#include "ftxui/component/receiver.hpp" // for Sender
|
||||||
|
#include "ftxui/component/task.hpp" // for Task
|
||||||
|
|
||||||
|
namespace ftxui {
|
||||||
|
struct Event;
|
||||||
|
|
||||||
|
// Parse a sequence of |char| accross |time|. Produces |Event|.
|
||||||
|
class TerminalInputParser {
|
||||||
|
public:
|
||||||
|
TerminalInputParser(Sender<Task> out);
|
||||||
|
void Timeout(int time);
|
||||||
|
void Add(char c);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char Current();
|
||||||
|
bool Eat();
|
||||||
|
|
||||||
|
enum Type {
|
||||||
|
UNCOMPLETED,
|
||||||
|
DROP,
|
||||||
|
CHARACTER,
|
||||||
|
SPECIAL,
|
||||||
|
MOUSE,
|
||||||
|
CURSOR_REPORTING,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CursorReporting {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Output {
|
||||||
|
Type type;
|
||||||
|
union {
|
||||||
|
Mouse mouse;
|
||||||
|
CursorReporting cursor;
|
||||||
|
};
|
||||||
|
|
||||||
|
Output(Type t) : type(t) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Send(Output output);
|
||||||
|
Output Parse();
|
||||||
|
Output ParseUTF8();
|
||||||
|
Output ParseESC();
|
||||||
|
Output ParseDCS();
|
||||||
|
Output ParseCSI();
|
||||||
|
Output ParseOSC();
|
||||||
|
Output ParseMouse(bool altered, bool pressed, std::vector<int> arguments);
|
||||||
|
Output ParseCursorReporting(std::vector<int> arguments);
|
||||||
|
|
||||||
|
Sender<Task> out_;
|
||||||
|
int position_ = -1;
|
||||||
|
int timeout_ = 0;
|
||||||
|
std::string pending_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ftxui
|
||||||
|
|
||||||
|
#endif /* end of include guard: FTXUI_COMPONENT_TERMINAL_INPUT_PARSER */
|
|
@ -49,6 +49,7 @@ roguish = executable('roguish', [
|
||||||
'render.cpp',
|
'render.cpp',
|
||||||
'config.cpp',
|
'config.cpp',
|
||||||
'save.cpp',
|
'save.cpp',
|
||||||
|
'sfml_screen.cpp',
|
||||||
'panel.cpp',
|
'panel.cpp',
|
||||||
],
|
],
|
||||||
dependencies: dependencies)
|
dependencies: dependencies)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
void Panel::resize(int width, int height) {
|
void Panel::resize(int width, int height) {
|
||||||
$dirty = true;
|
$dirty = true;
|
||||||
$screen = Screen(width, height);
|
// $screen = ScreenInteractive::FixedSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::set_renderer(std::function< Element()> render) {
|
void Panel::set_renderer(std::function< Element()> render) {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <ftxui/dom/node.hpp> // for Render
|
#include <ftxui/dom/node.hpp> // for Render
|
||||||
#include <ftxui/component/component.hpp>
|
#include <ftxui/component/component.hpp>
|
||||||
#include <ftxui/screen/screen.hpp>
|
|
||||||
#include <ftxui/dom/canvas.hpp>
|
#include <ftxui/dom/canvas.hpp>
|
||||||
#include <ftxui/screen/screen.hpp>
|
#include <ftxui/screen/screen.hpp>
|
||||||
#include <ftxui/dom/canvas.hpp>
|
#include <ftxui/dom/canvas.hpp>
|
||||||
#include <ftxui/screen/screen.hpp>
|
#include <ftxui/screen/screen.hpp>
|
||||||
#include <ftxui/dom/canvas.hpp>
|
#include <ftxui/dom/canvas.hpp>
|
||||||
|
#include "sfml_screen.hpp" // for SFMLScreen
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ struct Panel {
|
||||||
std::wstring $screenout;
|
std::wstring $screenout;
|
||||||
bool $dirty = true;
|
bool $dirty = true;
|
||||||
Component $component;
|
Component $component;
|
||||||
Screen $screen;
|
SFMLScreen $screen;
|
||||||
bool $must_clear = true;
|
bool $must_clear = true;
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ struct Panel {
|
||||||
y(y),
|
y(y),
|
||||||
width(width),
|
width(width),
|
||||||
height(height),
|
height(height),
|
||||||
$screen(width, height),
|
$screen(SFMLScreen::FixedSize(width, height)),
|
||||||
$must_clear(must_clear)
|
$must_clear(must_clear)
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
|
460
sfml_screen.cpp
Normal file
460
sfml_screen.cpp
Normal file
|
@ -0,0 +1,460 @@
|
||||||
|
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
#include <cassert>
|
||||||
|
#include <algorithm> // for copy, max, min
|
||||||
|
#include <array> // for array
|
||||||
|
#include <chrono> // for operator-, milliseconds, operator>=, duration, common_type<>::type, time_point
|
||||||
|
#include <cstdio> // for fileno, stdin
|
||||||
|
#include <ftxui/component/task.hpp> // for Task, Closure, AnimationTask
|
||||||
|
#include <ftxui/screen/screen.hpp> // for Pixel, Screen::Cursor, Screen, Screen::Cursor::Hidden
|
||||||
|
#include <functional> // for function
|
||||||
|
#include <initializer_list> // for initializer_list
|
||||||
|
#include <iostream> // for cout, ostream, operator<<, basic_ostream, endl, flush
|
||||||
|
#include <stack> // for stack
|
||||||
|
#include <thread> // for thread, sleep_for
|
||||||
|
#include <tuple> // for _Swallow_assign, ignore
|
||||||
|
#include <type_traits> // for decay_t
|
||||||
|
#include <utility> // for move, swap
|
||||||
|
#include <variant> // for visit, variant
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
|
#include <ftxui/component/animation.hpp> // for TimePoint, Clock, Duration, Params, RequestAnimationFrame
|
||||||
|
#include <ftxui/component/captured_mouse.hpp> // for CapturedMouse, CapturedMouseInterface
|
||||||
|
#include <ftxui/component/component_base.hpp> // for ComponentBase
|
||||||
|
#include <ftxui/component/event.hpp> // for Event
|
||||||
|
#include <ftxui/component/loop.hpp> // for Loop
|
||||||
|
#include <ftxui/component/receiver.hpp> // for ReceiverImpl, Sender, MakeReceiver, SenderImpl, Receiver
|
||||||
|
#include <ftxui/dom/node.hpp> // for Node, Render
|
||||||
|
#include <ftxui/dom/requirement.hpp> // for Requirement
|
||||||
|
#include <ftxui/screen/terminal.hpp> // for Dimensions, Size
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include "sfml_screen.hpp"
|
||||||
|
|
||||||
|
// Quick exit is missing in standard CLang headers
|
||||||
|
#if defined(__clang__) && defined(__APPLE__)
|
||||||
|
#define quick_exit(a) exit(a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
namespace ftxui {
|
||||||
|
namespace animation {
|
||||||
|
void RequestAnimationFrame() {
|
||||||
|
auto* screen = SFMLScreen::Active();
|
||||||
|
if (screen) {
|
||||||
|
screen->RequestAnimationFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace animation
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
SFMLScreen* g_active_screen = nullptr; // NOLINT
|
||||||
|
|
||||||
|
void Flush() {
|
||||||
|
// Emscripten doesn't implement flush. We interpret zero as flush.
|
||||||
|
std::cout << '\0' << std::flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int timeout_milliseconds = 20;
|
||||||
|
[[maybe_unused]] constexpr int timeout_microseconds =
|
||||||
|
timeout_milliseconds * 1000;
|
||||||
|
|
||||||
|
std::stack<Closure> on_exit_functions; // NOLINT
|
||||||
|
void OnExit() {
|
||||||
|
while (!on_exit_functions.empty()) {
|
||||||
|
on_exit_functions.top()();
|
||||||
|
on_exit_functions.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bruxisma: std::thread has some special magic built in so that if you pass in a std::reference_wrapper it'll unpack it and treat it as a reference. So you can pass it as a reference with `std::ref` for mutable references, and `st***ref` for constant references
|
||||||
|
*
|
||||||
|
* ZED: This is al Windows specific code that needs to be replaced
|
||||||
|
* with SFML's events system, so the quit here will die.
|
||||||
|
*/
|
||||||
|
void EventListener(Sender<Task> out) {
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
while (true) {
|
||||||
|
// get the sfml window inputs
|
||||||
|
fmt::println("WAITING FOR EVENT");
|
||||||
|
std::this_thread::sleep_for(1000ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ZED: This can stay but it doesn't need to be a thread, make it a function
|
||||||
|
* that is called in the event loop.
|
||||||
|
*/
|
||||||
|
void AnimationListener(Sender<Task> out) {
|
||||||
|
// Animation at around 60fps.
|
||||||
|
const auto time_delta = std::chrono::milliseconds(15);
|
||||||
|
while (true) {
|
||||||
|
out->Send(ftxui::AnimationTask());
|
||||||
|
std::this_thread::sleep_for(time_delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SFMLScreen::SFMLScreen(int dimx,
|
||||||
|
int dimy,
|
||||||
|
Dimension dimension,
|
||||||
|
bool use_alternative_screen)
|
||||||
|
: Screen(dimx, dimy),
|
||||||
|
dimension_(dimension),
|
||||||
|
use_alternative_screen_(use_alternative_screen) {
|
||||||
|
task_receiver_ = ftxui::MakeReceiver<Task>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
SFMLScreen SFMLScreen::FixedSize(int dimx, int dimy) {
|
||||||
|
return {
|
||||||
|
dimx,
|
||||||
|
dimy,
|
||||||
|
Dimension::Fixed,
|
||||||
|
false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
SFMLScreen SFMLScreen::Fullscreen() {
|
||||||
|
return {
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Dimension::Fullscreen,
|
||||||
|
true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
SFMLScreen SFMLScreen::TerminalOutput() {
|
||||||
|
return {
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Dimension::TerminalOutput,
|
||||||
|
false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
SFMLScreen SFMLScreen::FitComponent() {
|
||||||
|
return {
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Dimension::FitComponent,
|
||||||
|
false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @ingroup component
|
||||||
|
/// @brief Set whether mouse is tracked and events reported.
|
||||||
|
/// called outside of the main loop. E.g `SFMLScreen::Loop(...)`.
|
||||||
|
/// @param enable Whether to enable mouse event tracking.
|
||||||
|
/// @note This muse be called outside of the main loop. E.g. before calling
|
||||||
|
/// `SFMLScreen::Loop`.
|
||||||
|
/// @note Mouse tracking is enabled by default.
|
||||||
|
/// @note Mouse tracking is only supported on terminals that supports it.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// ```cpp
|
||||||
|
/// auto screen = SFMLScreen::TerminalOutput();
|
||||||
|
/// screen.TrackMouse(false);
|
||||||
|
/// screen.Loop(component);
|
||||||
|
/// ```
|
||||||
|
void SFMLScreen::TrackMouse(bool enable) {
|
||||||
|
track_mouse_ = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Add a task to the main loop.
|
||||||
|
/// It will be executed later, after every other scheduled tasks.
|
||||||
|
/// @ingroup component
|
||||||
|
void SFMLScreen::Post(Task task) {
|
||||||
|
// Task/Events sent toward inactive screen or screen waiting to become
|
||||||
|
// inactive are dropped.
|
||||||
|
if (!task_sender_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
task_sender_->Send(std::move(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Add an event to the main loop.
|
||||||
|
/// It will be executed later, after every other scheduled events.
|
||||||
|
/// @ingroup component
|
||||||
|
void SFMLScreen::PostEvent(Event event) {
|
||||||
|
Post(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Add a task to draw the screen one more time, until all the animations
|
||||||
|
/// are done.
|
||||||
|
void SFMLScreen::RequestAnimationFrame() {
|
||||||
|
if (animation_requested_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
animation_requested_ = true;
|
||||||
|
auto now = ftxui::animation::Clock::now();
|
||||||
|
const auto time_histeresis = std::chrono::milliseconds(33);
|
||||||
|
if (now - previous_animation_time_ >= time_histeresis) {
|
||||||
|
previous_animation_time_ = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Return whether the main loop has been quit.
|
||||||
|
/// @ingroup component
|
||||||
|
bool SFMLScreen::HasQuitted() {
|
||||||
|
return task_receiver_->HasQuitted();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
void SFMLScreen::PreMain() {
|
||||||
|
// Suspend previously active screen:
|
||||||
|
if (g_active_screen) {
|
||||||
|
std::swap(suspended_screen_, g_active_screen);
|
||||||
|
// Reset cursor position to the top of the screen and clear the screen.
|
||||||
|
suspended_screen_->ResetCursorPosition();
|
||||||
|
std::cout << suspended_screen_->ResetPosition(/*clear=*/true);
|
||||||
|
suspended_screen_->dimx_ = 0;
|
||||||
|
suspended_screen_->dimy_ = 0;
|
||||||
|
|
||||||
|
// Reset dimensions to force drawing the screen again next time:
|
||||||
|
suspended_screen_->Uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This screen is now active:
|
||||||
|
g_active_screen = this;
|
||||||
|
g_active_screen->Install();
|
||||||
|
|
||||||
|
previous_animation_time_ = ftxui::animation::Clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
void SFMLScreen::PostMain() {
|
||||||
|
// Put cursor position at the end of the drawing.
|
||||||
|
ResetCursorPosition();
|
||||||
|
|
||||||
|
g_active_screen = nullptr;
|
||||||
|
|
||||||
|
// Restore suspended screen.
|
||||||
|
if (suspended_screen_) {
|
||||||
|
// Clear screen, and put the cursor at the beginning of the drawing.
|
||||||
|
std::cout << ResetPosition(/*clear=*/true);
|
||||||
|
dimx_ = 0;
|
||||||
|
dimy_ = 0;
|
||||||
|
Uninstall();
|
||||||
|
std::swap(g_active_screen, suspended_screen_);
|
||||||
|
g_active_screen->Install();
|
||||||
|
} else {
|
||||||
|
Uninstall();
|
||||||
|
|
||||||
|
std::cout << '\r';
|
||||||
|
// On final exit, keep the current drawing and reset cursor position one
|
||||||
|
// line after it.
|
||||||
|
if (!use_alternative_screen_) {
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Decorate a function. It executes the same way, but with the currently
|
||||||
|
/// active screen terminal hooks temporarilly uninstalled during its execution.
|
||||||
|
/// @param fn The function to decorate.
|
||||||
|
Closure SFMLScreen::WithRestoredIO(Closure fn) { // NOLINT
|
||||||
|
return [this, fn] {
|
||||||
|
Uninstall();
|
||||||
|
fn();
|
||||||
|
Install();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Return the currently active screen, or null if none.
|
||||||
|
// static
|
||||||
|
SFMLScreen* SFMLScreen::Active() {
|
||||||
|
return g_active_screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
void SFMLScreen::Install() {
|
||||||
|
frame_valid_ = false;
|
||||||
|
|
||||||
|
// After uninstalling the new configuration, flush it to the terminal to
|
||||||
|
// ensure it is fully applied:
|
||||||
|
on_exit_functions.push([] { Flush(); });
|
||||||
|
|
||||||
|
on_exit_functions.push([this] { ExitLoopClosure()(); });
|
||||||
|
|
||||||
|
// After installing the new configuration, flush it to the terminal to
|
||||||
|
// ensure it is fully applied:
|
||||||
|
Flush();
|
||||||
|
|
||||||
|
task_sender_ = task_receiver_->MakeSender();
|
||||||
|
event_listener_ =
|
||||||
|
std::thread(&EventListener, task_receiver_->MakeSender());
|
||||||
|
animation_listener_ =
|
||||||
|
std::thread(&AnimationListener, task_receiver_->MakeSender());
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
void SFMLScreen::Uninstall() {
|
||||||
|
ExitNow();
|
||||||
|
event_listener_.join();
|
||||||
|
animation_listener_.join();
|
||||||
|
OnExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
// NOLINTNEXTLINE
|
||||||
|
void SFMLScreen::RunOnceBlocking(Component component) {
|
||||||
|
Task task;
|
||||||
|
if (task_receiver_->Receive(&task)) {
|
||||||
|
HandleTask(component, task);
|
||||||
|
}
|
||||||
|
RunOnce(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
void SFMLScreen::RunOnce(Component component) {
|
||||||
|
Task task;
|
||||||
|
while (task_receiver_->ReceiveNonBlocking(&task)) {
|
||||||
|
HandleTask(component, task);
|
||||||
|
}
|
||||||
|
Draw(std::move(component));
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
void SFMLScreen::HandleTask(Component component, Task& task) {
|
||||||
|
// clang-format off
|
||||||
|
std::visit([&](auto&& arg) {
|
||||||
|
using T = std::decay_t<decltype(arg)>;
|
||||||
|
|
||||||
|
// Handle Event.
|
||||||
|
if constexpr (std::is_same_v<T, Event>) {
|
||||||
|
if (arg.is_cursor_reporting()) {
|
||||||
|
cursor_x_ = arg.cursor_x();
|
||||||
|
cursor_y_ = arg.cursor_y();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.is_mouse()) {
|
||||||
|
arg.mouse().x -= cursor_x_;
|
||||||
|
arg.mouse().y -= cursor_y_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZED: arg.screen_ = this;
|
||||||
|
component->OnEvent(arg);
|
||||||
|
frame_valid_ = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle callback
|
||||||
|
if constexpr (std::is_same_v<T, Closure>) {
|
||||||
|
arg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Animation
|
||||||
|
if constexpr (std::is_same_v<T, ftxui::AnimationTask>) {
|
||||||
|
if (!animation_requested_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
animation_requested_ = false;
|
||||||
|
const ftxui::animation::TimePoint now = ftxui::animation::Clock::now();
|
||||||
|
const ftxui::animation::Duration delta = now - previous_animation_time_;
|
||||||
|
previous_animation_time_ = now;
|
||||||
|
|
||||||
|
ftxui::animation::Params params(delta);
|
||||||
|
component->OnAnimation(params);
|
||||||
|
frame_valid_ = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
task);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
// NOLINTNEXTLINE
|
||||||
|
void SFMLScreen::Draw(Component component) {
|
||||||
|
if (frame_valid_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto document = component->Render();
|
||||||
|
int dimx = 0;
|
||||||
|
int dimy = 0;
|
||||||
|
// ZED: replace this
|
||||||
|
// auto terminal = Terminal::Size();
|
||||||
|
document->ComputeRequirement();
|
||||||
|
switch (dimension_) {
|
||||||
|
case Dimension::Fixed:
|
||||||
|
dimx = dimx_;
|
||||||
|
dimy = dimy_;
|
||||||
|
break;
|
||||||
|
case Dimension::TerminalOutput:
|
||||||
|
assert(false && "NOT IMPLEMENTED!");
|
||||||
|
// dimx = terminal.dimx;
|
||||||
|
// dimy = document->requirement().min_y;
|
||||||
|
break;
|
||||||
|
case Dimension::Fullscreen:
|
||||||
|
assert(false && "NOT IMPLEMENTED!");
|
||||||
|
// dimx = terminal.dimx;
|
||||||
|
// dimy = terminal.dimy;
|
||||||
|
break;
|
||||||
|
case Dimension::FitComponent:
|
||||||
|
assert(false && "NOT IMPLEMENTED!");
|
||||||
|
// dimx = std::min(document->requirement().min_x, terminal.dimx);
|
||||||
|
// dimy = std::min(document->requirement().min_y, terminal.dimy);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool resized = (dimx != dimx_) || (dimy != dimy_);
|
||||||
|
ResetCursorPosition();
|
||||||
|
std::cout << ResetPosition(/*clear=*/resized);
|
||||||
|
|
||||||
|
// Resize the screen if needed.
|
||||||
|
if (resized) {
|
||||||
|
dimx_ = dimx;
|
||||||
|
dimy_ = dimy;
|
||||||
|
pixels_ = std::vector<std::vector<ftxui::Pixel>>(dimy, std::vector<ftxui::Pixel>(dimx));
|
||||||
|
cursor_.x = dimx_ - 1;
|
||||||
|
cursor_.y = dimy_ - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZED: I removed a bunch of terminal stuff but probably need to bring back
|
||||||
|
// resizing?
|
||||||
|
//
|
||||||
|
previous_frame_resized_ = resized;
|
||||||
|
|
||||||
|
Render(*this, document);
|
||||||
|
|
||||||
|
std::cout << ToString() << set_cursor_position;
|
||||||
|
Flush();
|
||||||
|
Clear();
|
||||||
|
frame_valid_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
void SFMLScreen::ResetCursorPosition() {
|
||||||
|
std::cout << reset_cursor_position;
|
||||||
|
reset_cursor_position = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Return a function to exit the main loop.
|
||||||
|
/// @ingroup component
|
||||||
|
Closure SFMLScreen::ExitLoopClosure() {
|
||||||
|
return [this] { Exit(); };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Exit the main loop.
|
||||||
|
/// @ingroup component
|
||||||
|
void SFMLScreen::Exit() {
|
||||||
|
Post([this] { ExitNow(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// private:
|
||||||
|
void SFMLScreen::ExitNow() {
|
||||||
|
task_sender_.reset();
|
||||||
|
}
|
113
sfml_screen.hpp
Normal file
113
sfml_screen.hpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
#ifndef FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP
|
||||||
|
#define FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP
|
||||||
|
|
||||||
|
#include <atomic> // for atomic
|
||||||
|
#include <ftxui/component/receiver.hpp> // for Receiver, Sender
|
||||||
|
#include <functional> // for function
|
||||||
|
#include <memory> // for shared_ptr
|
||||||
|
#include <string> // for string
|
||||||
|
#include <thread> // for thread
|
||||||
|
#include <variant> // for variant
|
||||||
|
|
||||||
|
#include <ftxui/component/animation.hpp> // for TimePoint
|
||||||
|
#include <ftxui/component/captured_mouse.hpp> // for CapturedMouse
|
||||||
|
#include <ftxui/component/event.hpp> // for Event
|
||||||
|
#include <ftxui/component/task.hpp> // for Task, Closure
|
||||||
|
#include <ftxui/screen/screen.hpp> // for Screen
|
||||||
|
|
||||||
|
using ftxui::Component, ftxui::Task, ftxui::Closure, ftxui::Event, ftxui::Sender, ftxui::Receiver;
|
||||||
|
|
||||||
|
namespace ftxui {
|
||||||
|
class ComponentBase;
|
||||||
|
struct Event;
|
||||||
|
|
||||||
|
using Component = std::shared_ptr<ComponentBase>;
|
||||||
|
class SFMLScreenPrivate;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SFMLScreen : public ftxui::Screen {
|
||||||
|
public:
|
||||||
|
// Constructors:
|
||||||
|
static SFMLScreen FixedSize(int dimx, int dimy);
|
||||||
|
static SFMLScreen Fullscreen();
|
||||||
|
static SFMLScreen FitComponent();
|
||||||
|
static SFMLScreen TerminalOutput();
|
||||||
|
|
||||||
|
// Options. Must be called before Loop().
|
||||||
|
void TrackMouse(bool enable = true);
|
||||||
|
|
||||||
|
// Return the currently active screen, nullptr if none.
|
||||||
|
static SFMLScreen* Active();
|
||||||
|
|
||||||
|
// Start/Stop the main loop.
|
||||||
|
void Exit();
|
||||||
|
Closure ExitLoopClosure();
|
||||||
|
|
||||||
|
// Post tasks to be executed by the loop.
|
||||||
|
void Post(Task task);
|
||||||
|
void PostEvent(Event event);
|
||||||
|
void RequestAnimationFrame();
|
||||||
|
|
||||||
|
// Decorate a function. The outputted one will execute similarly to the
|
||||||
|
// inputted one, but with the currently active screen terminal hooks
|
||||||
|
// temporarily uninstalled.
|
||||||
|
ftxui::Closure WithRestoredIO(ftxui::Closure);
|
||||||
|
|
||||||
|
void ExitNow();
|
||||||
|
|
||||||
|
void Install();
|
||||||
|
void Uninstall();
|
||||||
|
|
||||||
|
void PreMain();
|
||||||
|
void PostMain();
|
||||||
|
|
||||||
|
bool HasQuitted();
|
||||||
|
void RunOnce(Component component);
|
||||||
|
void RunOnceBlocking(Component component);
|
||||||
|
|
||||||
|
void HandleTask(Component component, Task& task);
|
||||||
|
void Draw(Component component);
|
||||||
|
void ResetCursorPosition();
|
||||||
|
|
||||||
|
SFMLScreen* suspended_screen_ = nullptr;
|
||||||
|
enum class Dimension {
|
||||||
|
FitComponent,
|
||||||
|
Fixed,
|
||||||
|
Fullscreen,
|
||||||
|
TerminalOutput,
|
||||||
|
};
|
||||||
|
Dimension dimension_ = Dimension::Fixed;
|
||||||
|
bool use_alternative_screen_ = false;
|
||||||
|
|
||||||
|
SFMLScreen(int dimx,
|
||||||
|
int dimy,
|
||||||
|
Dimension dimension,
|
||||||
|
bool use_alternative_screen);
|
||||||
|
|
||||||
|
bool track_mouse_ = true;
|
||||||
|
|
||||||
|
Sender<Task> task_sender_;
|
||||||
|
Receiver<Task> task_receiver_;
|
||||||
|
|
||||||
|
std::string set_cursor_position;
|
||||||
|
std::string reset_cursor_position;
|
||||||
|
|
||||||
|
std::thread event_listener_;
|
||||||
|
std::thread animation_listener_;
|
||||||
|
bool animation_requested_ = false;
|
||||||
|
ftxui::animation::TimePoint previous_animation_time_;
|
||||||
|
|
||||||
|
int cursor_x_ = 1;
|
||||||
|
int cursor_y_ = 1;
|
||||||
|
|
||||||
|
bool mouse_captured = false;
|
||||||
|
bool previous_frame_resized_ = false;
|
||||||
|
|
||||||
|
bool frame_valid_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* end of include guard: FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP */
|
|
@ -3,8 +3,6 @@ NOTES:
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
* panels and everything except renderer should use character coodinates
|
* panels and everything except renderer should use character coodinates
|
||||||
* camera shake broken
|
|
||||||
* draw_screen doesn't do x axis offset render
|
|
||||||
* Can std::any be defaulted to a noop in the events?
|
* Can std::any be defaulted to a noop in the events?
|
||||||
* Save file isn't saving gold.
|
* Save file isn't saving gold.
|
||||||
* Inventory needs to be better, but need some kinds of "weapons" or other loot to get and not just gold.
|
* Inventory needs to be better, but need some kinds of "weapons" or other loot to get and not just gold.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue