Figured out that I don't need a special screen, just send events to the component directly with OnEvent. However, you have to component->Add() or call Render(component, []) with it or else it's not considered a child.
This commit is contained in:
parent
e3cff8142c
commit
96ee16e598
9 changed files with 32 additions and 1078 deletions
18
gui.cpp
18
gui.cpp
|
@ -33,7 +33,7 @@ GUI::GUI(DinkyECS::World &world, Map& game_map) :
|
|||
$game_map(game_map),
|
||||
$log({{"Welcome to the game!"}}),
|
||||
$status_ui(SCREEN_X, SCREEN_Y, 0, 0),
|
||||
$map_view(30, 10, GAME_MAP_POS, 0, false),
|
||||
$map_view(0, 0, GAME_MAP_POS, 0, false),
|
||||
$view_port{0,0},
|
||||
$world(world),
|
||||
$sounds("./assets"),
|
||||
|
@ -65,12 +65,14 @@ void GUI::create_renderer() {
|
|||
Terminal::SetColorSupport(Terminal::Color::TrueColor);
|
||||
auto player = $world.get_the<Player>();
|
||||
|
||||
$map_view.set_renderer([&] {
|
||||
$map_view.set_renderer(Renderer([&] {
|
||||
System::draw_map($world, $game_map, $canvas, $view_port.x, $view_port.y);
|
||||
return canvas($canvas);
|
||||
});
|
||||
}));
|
||||
|
||||
$status_ui.set_renderer([&, player]{
|
||||
auto test_button = Button("TEST", [&]{ println("pressed"); });
|
||||
|
||||
auto status_rend = Renderer(test_button, [&, test_button, player]{
|
||||
const auto& player_combat = $world.get<Combat>(player.entity);
|
||||
const auto& inventory = $world.get<Inventory>(player.entity);
|
||||
$status_text = player_combat.hp > 0 ? "NOT DEAD" : "DEAD!!!!!!";
|
||||
|
@ -85,6 +87,7 @@ void GUI::create_renderer() {
|
|||
return hbox({
|
||||
hflow(
|
||||
vbox(
|
||||
test_button->Render(),
|
||||
text(format("HP: {: >3} GOLD: {: >3}",
|
||||
player_combat.hp, inventory.gold)) | border,
|
||||
text($status_text) | border,
|
||||
|
@ -96,6 +99,8 @@ void GUI::create_renderer() {
|
|||
hbox(),
|
||||
});
|
||||
});
|
||||
|
||||
$status_ui.set_renderer(status_rend);
|
||||
}
|
||||
|
||||
void GUI::handle_world_events() {
|
||||
|
@ -151,6 +156,7 @@ bool GUI::handle_ui_events() {
|
|||
if(event.type == sf::Event::Closed) {
|
||||
$renderer.close();
|
||||
} else if(event.type == sf::Event::KeyPressed) {
|
||||
// ZED: Uh we can just do this...?
|
||||
auto& player_motion = $world.get<Motion>(player.entity);
|
||||
|
||||
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
|
||||
|
@ -171,6 +177,10 @@ bool GUI::handle_ui_events() {
|
|||
resize_map(map_font_size - 10);
|
||||
} else if(sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
|
||||
save_world();
|
||||
} else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Tab)) {
|
||||
$status_ui.$component->OnEvent(Event::Tab);
|
||||
} else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Enter)) {
|
||||
$status_ui.$component->OnEvent(Event::Return);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
6
gui.hpp
6
gui.hpp
|
@ -7,6 +7,9 @@
|
|||
#include <ftxui/component/component.hpp>
|
||||
#include <ftxui/screen/screen.hpp>
|
||||
#include <ftxui/dom/canvas.hpp>
|
||||
#include "ftxui/component/component.hpp" // for Button, operator|=, Renderer, Vertical, Modal
|
||||
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive, Component
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, separator, text, size, Element, vbox, border, GREATER_THAN, WIDTH, center, HEIGHT
|
||||
#include <locale>
|
||||
#include <string>
|
||||
#include "map.hpp"
|
||||
|
@ -17,7 +20,7 @@
|
|||
#include "panel.hpp"
|
||||
|
||||
using std::string;
|
||||
using ftxui::Canvas, ftxui::Component, ftxui::Screen;
|
||||
using ftxui::Canvas, ftxui::Component, ftxui::Screen, ftxui::Button;
|
||||
|
||||
constexpr int SCREEN_X = 40;
|
||||
constexpr int SCREEN_Y = 30;
|
||||
|
@ -41,6 +44,7 @@ class GUI {
|
|||
Panel $status_ui;
|
||||
Panel $map_view;
|
||||
Point $view_port;
|
||||
Component $test_button;
|
||||
DinkyECS::World& $world;
|
||||
SoundManager $sounds;
|
||||
SFMLRender $renderer;
|
||||
|
|
419
input_parser.cpp
419
input_parser.cpp
|
@ -1,419 +0,0 @@
|
|||
// 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
|
|
@ -1,72 +0,0 @@
|
|||
// 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,7 +49,6 @@ roguish = executable('roguish', [
|
|||
'render.cpp',
|
||||
'config.cpp',
|
||||
'save.cpp',
|
||||
'sfml_screen.cpp',
|
||||
'panel.cpp',
|
||||
],
|
||||
dependencies: dependencies)
|
||||
|
|
10
panel.cpp
10
panel.cpp
|
@ -2,12 +2,16 @@
|
|||
|
||||
void Panel::resize(int width, int height) {
|
||||
$dirty = true;
|
||||
// $screen = ScreenInteractive::FixedSize(width, height);
|
||||
$screen = Screen(width, height);
|
||||
}
|
||||
|
||||
void Panel::set_renderer(std::function< Element()> render) {
|
||||
void Panel::set_renderer(Component renderer) {
|
||||
$dirty = true;
|
||||
$component = Renderer(render);
|
||||
$component = renderer;
|
||||
}
|
||||
|
||||
void Panel::add(Component child) {
|
||||
$component->Add(child);
|
||||
}
|
||||
|
||||
void Panel::render() {
|
||||
|
|
11
panel.hpp
11
panel.hpp
|
@ -6,7 +6,6 @@
|
|||
#include <ftxui/dom/canvas.hpp>
|
||||
#include <ftxui/screen/screen.hpp>
|
||||
#include <ftxui/dom/canvas.hpp>
|
||||
#include "sfml_screen.hpp" // for SFMLScreen
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
|
@ -20,7 +19,7 @@ struct Panel {
|
|||
std::wstring $screenout;
|
||||
bool $dirty = true;
|
||||
Component $component;
|
||||
SFMLScreen $screen;
|
||||
Screen $screen;
|
||||
bool $must_clear = true;
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
|
||||
|
||||
|
@ -29,12 +28,14 @@ struct Panel {
|
|||
y(y),
|
||||
width(width),
|
||||
height(height),
|
||||
$screen(SFMLScreen::FixedSize(width, height)),
|
||||
$screen(Screen(width, height)),
|
||||
$must_clear(must_clear)
|
||||
{};
|
||||
{
|
||||
};
|
||||
|
||||
void resize(int width, int height);
|
||||
void set_renderer(std::function< Element()> render);
|
||||
void set_renderer(Component renderer);
|
||||
void add(Component child);
|
||||
void render();
|
||||
const std::wstring &to_string();
|
||||
const Screen &screen();
|
||||
|
|
460
sfml_screen.cpp
460
sfml_screen.cpp
|
@ -1,460 +0,0 @@
|
|||
// 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
113
sfml_screen.hpp
|
@ -1,113 +0,0 @@
|
|||
// 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 */
|
Loading…
Add table
Add a link
Reference in a new issue