15#include <initializer_list>
39#define DEFINE_CONSOLEV2_PROPERTIES
40#define WIN32_LEAN_AND_MEAN
47#error Must be compiled in UNICODE mode
51#include <sys/select.h>
63 screen->RequestAnimationFrame();
68class ThrottledRequest {
70 ThrottledRequest(App* app,
71 std::function<
void()> send,
72 task::TaskRunner& task_runner)
73 : app_(app), send_(std::move(send)), task_runner_(task_runner) {}
75 void Request(
bool force =
false) {
76 if (!app_->is_a_tty_) {
91 const auto now = std::chrono::steady_clock::now();
92 const auto delta = now - last_request_time_;
93 const auto delay = std::chrono::milliseconds(500) - delta;
95 if (delay <= std::chrono::milliseconds(0)) {
100 request_queued_ =
true;
103 request_queued_ =
false;
109 void OnReply() { pending_request_ =
false; }
111 bool HasPending()
const {
112 if (pending_request_) {
113 const auto now = std::chrono::steady_clock::now();
114 if (now - last_sent_time_ < std::chrono::seconds(5)) {
118 return request_queued_;
124 last_sent_time_ = std::chrono::steady_clock::now();
125 pending_request_ =
true;
130 std::function<void()> send_;
131 task::TaskRunner& task_runner_;
132 bool pending_request_ =
false;
133 std::chrono::steady_clock::time_point last_request_time_ =
134 std::chrono::steady_clock::now() - std::chrono::hours(1);
135 std::chrono::steady_clock::time_point last_sent_time_ =
136 std::chrono::steady_clock::now() - std::chrono::hours(1);
137 bool request_queued_ =
false;
140struct App::Internal {
142 TerminalInputParser terminal_input_parser;
144 task::TaskRunner task_runner;
147 std::chrono::time_point<std::chrono::steady_clock> last_char_time =
148 std::chrono::steady_clock::now();
155 std::string output_buffer;
160 explicit Internal(App* app, std::function<
void(Event)> out);
165App* g_active_screen =
nullptr;
167std::stack<Closure> on_exit_functions;
170 while (!on_exit_functions.empty()) {
171 on_exit_functions.top()();
172 on_exit_functions.pop();
178#elif defined(__EMSCRIPTEN__)
179#include <emscripten.h>
183void ftxui_on_resize(
int columns,
int rows) {
188 std::raise(SIGWINCH);
194int CheckStdinReady(
int fd) {
199 select(fd + 1, &fds,
nullptr,
nullptr, &tv);
200 return FD_ISSET(fd, &fds);
205std::atomic<int> g_signal_exit_count = 0;
207std::atomic<int> g_signal_stop_count = 0;
208std::atomic<int> g_signal_resize_count = 0;
212void RecordSignal(
int signal) {
220 g_signal_exit_count++;
225 g_signal_stop_count++;
229 g_signal_resize_count++;
238void ExecuteSignalHandlers() {
239 int signal_exit_count = g_signal_exit_count.exchange(0);
240 while (signal_exit_count--) {
245 int signal_stop_count = g_signal_stop_count.exchange(0);
246 while (signal_stop_count--) {
250 int signal_resize_count = g_signal_resize_count.exchange(0);
251 while (signal_resize_count--) {
257void InstallSignalHandler(
int sig) {
258 auto old_signal_handler = std::signal(sig, RecordSignal);
259 on_exit_functions.emplace(
260 [=] { std::ignore = std::signal(sig, old_signal_handler); });
264const std::string CSI =
"\x1b[";
267const std::string DCS =
"\x1bP";
270const std::string ST =
"\x1b\\";
274const std::string DECRQSS_DECSCUSR = DCS +
"$q q" + ST;
277enum class DECMode : std::uint16_t {
283 kMouseVt200Highlight = 1001,
285 kMouseBtnEventMouse = 1002,
286 kMouseAnyEvent = 1003,
289 kMouseSgrExtMode = 1006,
290 kMouseUrxvtMode = 1015,
291 kMouseSgrPixelsMode = 1016,
292 kAlternateScreen = 1049,
296enum class DSRMode : std::uint8_t {
300std::string Serialize(
const std::vector<DECMode>& parameters) {
303 for (
const DECMode parameter : parameters) {
307 out += std::to_string(
int(parameter));
314std::string Set(
const std::vector<DECMode>& parameters) {
315 return CSI +
"?" + Serialize(parameters) +
"h";
319std::string Reset(
const std::vector<DECMode>& parameters) {
320 return CSI +
"?" + Serialize(parameters) +
"l";
324std::string DeviceStatusReport(DSRMode ps) {
325 return CSI + std::to_string(
int(ps)) +
"n";
328class CapturedMouseImpl :
public CapturedMouseInterface {
330 explicit CapturedMouseImpl(std::function<
void(
void)> callback)
331 : callback_(std::move(callback)) {}
332 ~CapturedMouseImpl()
override { callback_(); }
333 CapturedMouseImpl(
const CapturedMouseImpl&) =
delete;
334 CapturedMouseImpl(CapturedMouseImpl&&) =
delete;
335 CapturedMouseImpl& operator=(
const CapturedMouseImpl&) =
delete;
336 CapturedMouseImpl& operator=(CapturedMouseImpl&&) =
delete;
339 std::function<void(
void)> callback_;
344App::Internal::Internal(App* app, std::function<
void(Event)> out)
345 : terminal_input_parser(std::move(out)),
346 cursor_position_request(
348 [app] { app->TerminalSend(DeviceStatusReport(DSRMode::kCursor)); },
350 cursor_shape_request(
352 [app] { app->TerminalSend(DECRQSS_DECSCUSR); },
355App::App(
Dimension dimension,
int dimx,
int dimy,
bool use_alternative_screen)
356 : Screen(dimx, dimy),
357 dimension_(dimension),
358 use_alternative_screen_(use_alternative_screen) {
359 internal_ = std::make_unique<Internal>(
360 this, [&](Event event) { PostEvent(std::move(event)); });
388 Dimension::Fullscreen,
401 Dimension::Fullscreen,
414 Dimension::TerminalOutput,
429 Dimension::FitComponent,
452 track_mouse_ = enable;
464 handle_piped_input_ = enable;
470 internal_->task_runner.
PostTask([
this, task = std::move(task)]()
mutable {
471 HandleTask(component_, task);
484 if (animation_requested_) {
487 animation_requested_ =
true;
488 auto now = animation::Clock::now();
489 const auto time_histeresis = std::chrono::milliseconds(33);
490 if (now - previous_animation_time_ >= time_histeresis) {
491 previous_animation_time_ = now;
499 if (mouse_captured) {
502 mouse_captured =
true;
503 return std::make_unique<CapturedMouseImpl>(
504 [
this] { mouse_captured =
false; });
510 class Loop loop(this, std::move(component));
515bool App::HasQuitted() {
522 if (g_active_screen) {
523 std::swap(suspended_screen_, g_active_screen);
525 suspended_screen_->TerminalSend(suspended_screen_->ResetCursorPosition());
527 suspended_screen_->internal_->output_buffer,
529 suspended_screen_->
dimx_ = 0;
530 suspended_screen_->
dimy_ = 0;
533 suspended_screen_->Uninstall();
537 g_active_screen =
this;
538 g_active_screen->Install();
540 previous_animation_time_ = animation::Clock::now();
544void App::PostMain() {
546 TerminalSend(ResetCursorPosition());
548 g_active_screen =
nullptr;
551 if (suspended_screen_) {
557 std::swap(g_active_screen, suspended_screen_);
558 g_active_screen->Install();
565 if (!use_alternative_screen_) {
568 std::cout << std::flush;
586 force_handle_ctrl_c_ = force;
592 force_handle_ctrl_z_ = force;
600 return selection_->GetParts();
604 selection_on_change_ = std::move(callback);
610 return g_active_screen;
615 frame_valid_ =
false;
624 InstallPipedInputHandling();
628 on_exit_functions.emplace([
this] { TerminalFlush(); });
632 RequestCursorShape();
633 on_exit_functions.emplace([
this] {
634 TerminalSend(
"\033[?25h");
635 TerminalSend(
"\033[" + std::to_string(cursor_reset_shape_) +
" q");
640 for (
const int signal : {SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE}) {
641 InstallSignalHandler(signal);
647 auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
648 auto stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
652 GetConsoleMode(stdout_handle, &out_mode);
653 GetConsoleMode(stdin_handle, &in_mode);
654 on_exit_functions.push([=] { SetConsoleMode(stdout_handle, out_mode); });
655 on_exit_functions.push([=] { SetConsoleMode(stdin_handle, in_mode); });
658 const int enable_virtual_terminal_processing = 0x0004;
659 const int disable_newline_auto_return = 0x0008;
660 out_mode |= enable_virtual_terminal_processing;
661 out_mode |= disable_newline_auto_return;
664 const int enable_line_input = 0x0002;
665 const int enable_echo_input = 0x0004;
666 const int enable_virtual_terminal_input = 0x0200;
667 const int enable_window_input = 0x0008;
668 in_mode &= ~enable_echo_input;
669 in_mode &= ~enable_line_input;
670 in_mode |= enable_virtual_terminal_input;
671 in_mode |= enable_window_input;
673 SetConsoleMode(stdin_handle, in_mode);
674 SetConsoleMode(stdout_handle, out_mode);
684 for (
const int signal : {SIGWINCH, SIGTSTP}) {
685 InstallSignalHandler(signal);
688 struct termios terminal;
689 tcgetattr(tty_fd_, &terminal);
690 on_exit_functions.emplace([terminal = terminal, tty_fd_ = tty_fd_] {
691 tcsetattr(tty_fd_, TCSANOW, &terminal);
695 terminal.c_iflag &= ~IGNBRK;
696 terminal.c_iflag &= ~BRKINT;
698 terminal.c_iflag &= ~PARMRK;
699 terminal.c_iflag &= ~ISTRIP;
700 terminal.c_iflag &= ~INLCR;
701 terminal.c_iflag &= ~IGNCR;
702 terminal.c_iflag &= ~ICRNL;
703 terminal.c_iflag &= ~IXON;
705 terminal.c_lflag &= ~ECHO;
706 terminal.c_lflag &= ~ECHONL;
707 terminal.c_lflag &= ~ICANON;
708 terminal.c_lflag &= ~ISIG;
713 terminal.c_lflag &= ~IEXTEN;
714 terminal.c_cflag |= CS8;
716 terminal.c_cc[VMIN] = 0;
718 terminal.c_cc[VTIME] = 0;
720 tcsetattr(tty_fd_, TCSANOW, &terminal);
724 auto enable = [&](
const std::vector<DECMode>& parameters) {
725 TerminalSend(Set(parameters));
726 on_exit_functions.emplace(
727 [
this, parameters] { TerminalSend(Reset(parameters)); });
730 auto disable = [&](
const std::vector<DECMode>& parameters) {
731 TerminalSend(Reset(parameters));
732 on_exit_functions.emplace(
733 [
this, parameters] { TerminalSend(Set(parameters)); });
736 if (use_alternative_screen_) {
738 DECMode::kAlternateScreen,
748 enable({DECMode::kMouseVt200});
749 enable({DECMode::kMouseAnyEvent});
750 enable({DECMode::kMouseUrxvtMode});
751 enable({DECMode::kMouseSgrExtMode});
765void App::InstallPipedInputHandling() {
767#if defined(__EMSCRIPTEN__)
770 is_a_tty_ = _isatty(_fileno(stdin));
772 tty_fd_ = STDIN_FILENO;
776 if (!handle_piped_input_) {
777 is_a_tty_ = isatty(STDIN_FILENO);
782 if (isatty(STDIN_FILENO)) {
788 tty_fd_ = open(
"/dev/tty", O_RDONLY);
791 tty_fd_ = STDIN_FILENO;
792 is_a_tty_ = isatty(STDIN_FILENO);
799 on_exit_functions.emplace([
this] {
807void App::Uninstall() {
810 auto start = std::chrono::steady_clock::now();
811 while (internal_->cursor_position_request.HasPending() ||
812 internal_->cursor_shape_request.HasPending()) {
813 FetchTerminalEvents();
816 if (std::chrono::steady_clock::now() - start > std::chrono::seconds(1)) {
819 std::this_thread::sleep_for(std::chrono::milliseconds(10));
827void App::RunOnceBlocking(
Component component) {
829 const auto time_per_frame = std::chrono::microseconds(16666);
831 auto time = std::chrono::steady_clock::now();
832 size_t executed_task = internal_->task_runner.
ExecutedTasks();
835 while (executed_task == internal_->task_runner.
ExecutedTasks() &&
839 const auto now = std::chrono::steady_clock::now();
840 const auto delta = now - time;
843 if (delta < time_per_frame) {
844 const auto sleep_duration = time_per_frame - delta;
845 std::this_thread::sleep_for(sleep_duration);
852 AutoReset set_component(&component_, component);
853 ExecuteSignalHandlers();
854 FetchTerminalEvents();
857 const size_t executed_task = internal_->task_runner.
ExecutedTasks();
860 if (executed_task == internal_->task_runner.
ExecutedTasks()) {
864 ExecuteSignalHandlers();
867 if (selection_data_previous_ != selection_data_) {
868 selection_data_previous_ = selection_data_;
869 if (selection_on_change_) {
870 selection_on_change_();
881 using T = std::decay_t<
decltype(arg)>;
885 if constexpr (std::is_same_v<T, Event>) {
887 if (arg.is_cursor_position()) {
888 cursor_x_ = arg.cursor_x();
889 cursor_y_ = arg.cursor_y();
890 internal_->cursor_position_request.OnReply();
894 if (arg.is_cursor_shape()) {
895 cursor_reset_shape_= arg.cursor_shape();
896 internal_->cursor_shape_request.OnReply();
900 if (quit_ || !installed_) {
904 if (arg.is_mouse()) {
905 arg.mouse().x -= cursor_x_;
906 arg.mouse().y -= cursor_y_;
911 bool handled = component->OnEvent(arg);
913 handled = HandleSelection(handled, arg);
915 if (arg ==
Event::CtrlC && (!handled || force_handle_ctrl_c_)) {
916 RecordSignal(SIGABRT);
920 if (arg ==
Event::CtrlZ && (!handled || force_handle_ctrl_z_)) {
921 RecordSignal(SIGTSTP);
925 frame_valid_ =
false;
930 if constexpr (std::is_same_v<T, Closure>) {
931 if (quit_ || !installed_) {
940 if constexpr (std::is_same_v<T, AnimationTask>) {
941 if (quit_ || !installed_) {
945 if (!animation_requested_) {
949 animation_requested_ =
false;
952 previous_animation_time_ = now;
954 animation::Params params(delta);
955 component->OnAnimation(params);
956 frame_valid_ =
false;
965bool App::HandleSelection(
bool handled, Event event) {
967 selection_pending_ =
nullptr;
968 selection_data_.empty =
true;
969 selection_ =
nullptr;
973 if (!event.is_mouse()) {
977 auto& mouse =
event.mouse();
984 selection_data_.start_x = mouse.x;
985 selection_data_.start_y = mouse.y;
986 selection_data_.end_x = mouse.x;
987 selection_data_.end_y = mouse.y;
991 if (!selection_pending_) {
996 if ((mouse.x != selection_data_.end_x) ||
997 (mouse.y != selection_data_.end_y)) {
998 selection_data_.end_x = mouse.x;
999 selection_data_.end_y = mouse.y;
1000 selection_data_.empty =
false;
1007 selection_pending_ =
nullptr;
1008 selection_data_.end_x = mouse.x;
1009 selection_data_.end_y = mouse.y;
1010 selection_data_.empty =
false;
1023 auto document = component->Render();
1027 document->ComputeRequirement();
1028 switch (dimension_) {
1029 case Dimension::Fixed:
1033 case Dimension::TerminalOutput:
1034 dimx = terminal.dimx;
1037 case Dimension::Fullscreen:
1038 dimx = terminal.dimx;
1039 dimy = terminal.dimy;
1041 case Dimension::FitComponent:
1048 TerminalSend(
"\033[?25l");
1051 TerminalSend(ResetCursorPosition());
1053 if (frame_count_ != 0) {
1060 if ((
dimx <
dimx_) && !use_alternative_screen_) {
1061 TerminalSend(
"\033[J");
1062 TerminalSend(
"\033[H");
1070 cells_ = std::vector<std::vector<Cell>>(
dimy, std::vector<Cell>(
dimx));
1078 if (!use_alternative_screen_) {
1079 RequestCursorPosition(previous_frame_resized_);
1081 previous_frame_resized_ = resized;
1083 selection_ = selection_data_.empty
1084 ? std::make_unique<Selection>()
1085 : std::make_unique<Selection>(
1086 selection_data_.start_x, selection_data_.start_y,
1087 selection_data_.end_x, selection_data_.end_y);
1088 Render(*
this, document.get(), *selection_);
1095 set_cursor_position_.clear();
1096 reset_cursor_position_.clear();
1099 set_cursor_position_ +=
"\x1B[" + std::to_string(dy) +
"A";
1100 reset_cursor_position_ +=
"\x1B[" + std::to_string(dy) +
"B";
1104 set_cursor_position_ +=
"\x1B[" + std::to_string(dx) +
"D";
1105 reset_cursor_position_ +=
"\x1B[" + std::to_string(dx) +
"C";
1109 set_cursor_position_ +=
"\033[?25h";
1110 set_cursor_position_ +=
1115 ToString(internal_->output_buffer);
1116 TerminalSend(set_cursor_position_);
1120 frame_valid_ =
true;
1125std::string App::ResetCursorPosition() {
1126 std::string result = std::move(reset_cursor_position_);
1127 reset_cursor_position_ =
"";
1132void App::RequestCursorPosition(
bool force) {
1133 internal_->cursor_position_request.Request(force);
1137void App::RequestCursorShape() {
1138 internal_->cursor_shape_request.Request();
1142void App::TerminalSend(std::string_view s) {
1143 internal_->output_buffer += s;
1147void App::TerminalFlush() {
1149 internal_->output_buffer +=
'\0';
1150 std::cout << internal_->output_buffer << std::flush;
1151 internal_->output_buffer.clear();
1156 return [
this] {
Exit(); };
1161 Post([
this] { ExitNow(); });
1165void App::ExitNow() {
1170void App::Signal(
int signal) {
1171 if (signal == SIGABRT) {
1178 if (signal == SIGTSTP) {
1180 TerminalSend(ResetCursorPosition());
1185 std::raise(SIGTSTP);
1191 if (signal == SIGWINCH) {
1198size_t App::FetchTerminalEvents() {
1200 auto get_input_records = [&]() -> std::vector<INPUT_RECORD> {
1202 auto console = GetStdHandle(STD_INPUT_HANDLE);
1203 DWORD number_of_events = 0;
1204 if (!GetNumberOfConsoleInputEvents(console, &number_of_events)) {
1205 return std::vector<INPUT_RECORD>();
1207 if (number_of_events <= 0) {
1209 return std::vector<INPUT_RECORD>();
1212 std::vector<INPUT_RECORD> records(number_of_events);
1213 DWORD number_of_events_read = 0;
1214 if (!ReadConsoleInput(console, records.data(), (DWORD)records.size(),
1215 &number_of_events_read)) {
1216 return std::vector<INPUT_RECORD>();
1218 records.resize(number_of_events_read);
1222 auto records = get_input_records();
1223 if (records.size() == 0) {
1224 const auto timeout =
1225 std::chrono::steady_clock::now() - internal_->last_char_time;
1226 const size_t timeout_microseconds =
1227 std::chrono::duration_cast<std::chrono::microseconds>(timeout).count();
1228 internal_->terminal_input_parser.
Timeout(timeout_microseconds);
1231 internal_->last_char_time = std::chrono::steady_clock::now();
1236 std::wstring wstring;
1237 for (
const auto& r : records) {
1238 switch (r.EventType) {
1240 auto key_event = r.Event.KeyEvent;
1242 if (key_event.bKeyDown == FALSE) {
1245 const wchar_t wc = key_event.uChar.UnicodeChar;
1247 if (wc >= 0xd800 && wc <= 0xdbff) {
1252 internal_->terminal_input_parser.
Add(it);
1256 case WINDOW_BUFFER_SIZE_EVENT:
1266 return records.size();
1267#elif defined(__EMSCRIPTEN__)
1270 std::array<char, 128> out{};
1271 size_t l = read(STDIN_FILENO, out.data(), out.size());
1273 const auto timeout =
1274 std::chrono::steady_clock::now() - internal_->last_char_time;
1275 const size_t timeout_microseconds =
1276 std::chrono::duration_cast<std::chrono::microseconds>(timeout).count();
1277 internal_->terminal_input_parser.
Timeout(timeout_microseconds);
1280 internal_->last_char_time = std::chrono::steady_clock::now();
1283 for (
size_t i = 0; i < l; ++i) {
1284 internal_->terminal_input_parser.
Add(out[i]);
1288 if (!CheckStdinReady(tty_fd_)) {
1289 const auto timeout =
1290 std::chrono::steady_clock::now() - internal_->last_char_time;
1291 const size_t timeout_ms =
1292 std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count();
1293 internal_->terminal_input_parser.
Timeout(timeout_ms);
1296 internal_->last_char_time = std::chrono::steady_clock::now();
1299 std::array<char, 128> out{};
1300 size_t l = read(tty_fd_, out.data(), out.size());
1303 for (
size_t i = 0; i < l; ++i) {
1304 internal_->terminal_input_parser.
Add(out[i]);
1310void App::PostAnimationTask() {
1311 Post(AnimationTask());
1315 internal_->task_runner.
PostDelayedTask([
this] { PostAnimationTask(); },
1316 std::chrono::milliseconds(15));
1319bool App::SelectionData::operator==(
const App::SelectionData& other)
const {
1320 if (empty && other.empty) {
1323 if (empty || other.empty) {
1326 return start_x == other.start_x && start_y == other.start_y &&
1327 end_x == other.end_x && end_y == other.end_y;
1330bool App::SelectionData::operator!=(
const App::SelectionData& other)
const {
1331 return !(*
this == other);
static void Signal(App &s, int signal)
auto PostTask(Task task) -> void
Schedules a task to be executed immediately.
auto RunUntilIdle() -> std::optional< std::chrono::steady_clock::duration >
Runs the tasks in the queue.
auto PostDelayedTask(Task task, std::chrono::steady_clock::duration duration) -> void
Schedules a task to be executed after a certain duration.
size_t ExecutedTasks() const
void HandlePipedInput(bool enable=true)
Enable or disable automatic piped input handling. When enabled, FTXUI will detect piped input and red...
void Exit()
Exit the main loop.
void PostEvent(Event event)
Add an event to the main loop. It will be executed later, after every other scheduled events.
static App FitComponent()
static App * Active()
Return the currently active screen, or null if none.
void Post(Task task)
Add a task to the main loop. It will be executed later, after every other scheduled tasks.
static Event Special(std::string_view)
An custom event whose meaning is defined by the user of the library.
static App FullscreenPrimaryScreen()
static const Event Custom
static App TerminalOutput()
static App FullscreenAlternateScreen()
CapturedMouse CaptureMouse()
Try to get the unique lock about behing able to capture the mouse.
static App FixedSize(int dimx, int dimy)
std::string GetSelection()
Returns the content of the current selection.
void TrackMouse(bool enable=true)
Set whether mouse is tracked and events reported. called outside of the main loop....
void SelectionChange(std::function< void()> callback)
void RequestAnimationFrame()
Add a task to draw the screen one more time, until all the animations are done.
friend class ThrottledRequest
Closure ExitLoopClosure()
Return a function to exit the main loop.
void ForceHandleCtrlC(bool force)
Force FTXUI to handle or not handle Ctrl-C, even if the component catches the Event::CtrlC.
void ForceHandleCtrlZ(bool force)
Force FTXUI to handle or not handle Ctrl-Z, even if the component catches the Event::CtrlZ.
Closure WithRestoredIO(Closure)
Decorate a function. It executes the same way, but with the currently active screen terminal hooks te...
App is a Screen that can handle events, run a main loop, and manage components.
Loop is a class that manages the event loop for a component.
void RequestAnimationFrame()
RequestAnimationFrame is a function that requests a new frame to be drawn in the next animation cycle...
Represent an event. It can be key press event, a terminal resize, or more ...
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
std::string ToString() const
std::string ResetPosition(bool clear=false) const
Return a string to be printed in order to reset the cursor position to the beginning of the screen.
void Clear()
Clear all the cells from the screen.
std::vector< std::vector< Cell > > cells_
Dimensions Size()
Get the terminal size.
The FTXUI ftxui::Dimension:: namespace.
The FTXUI ftxui::animation:: namespace.
void SetFallbackSize(const Dimensions &fallbackSize)
Override terminal size in case auto-detection fails.
std::chrono::duration< float > Duration
std::chrono::time_point< Clock > TimePoint
constexpr const T & clamp(const T &v, const T &lo, const T &hi)
The FTXUI ftxui:: namespace.
std::unique_ptr< CapturedMouseInterface > CapturedMouse
std::string to_string(std::wstring_view s)
Convert a std::wstring into a UTF8 std::string.
Element select(Element e)
Set the child to be the one focused among its siblings.
std::variant< Event, Closure, AnimationTask > Task
std::function< void()> Closure
std::shared_ptr< ComponentBase > Component