Support reentrant screen. (#196)

This commit is contained in:
Arthur Sonzogni 2021-09-01 17:47:48 +02:00 committed by GitHub
parent 9763dbf744
commit 2ccc599db9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 11 deletions

View File

@ -2,6 +2,7 @@ set(DIRECTORY_LIB component)
example(button) example(button)
example(checkbox) example(checkbox)
example(nested_screen)
example(checkbox_in_frame) example(checkbox_in_frame)
example(composition) example(composition)
example(gallery) example(gallery)

View File

@ -0,0 +1,51 @@
#include <memory> // for shared_ptr, __shared_ptr_access
#include <string> // for operator+, to_wstring
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Button, Horizontal, Renderer
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for ButtonOption
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for separator, gauge, Element, operator|, vbox, border
using namespace ftxui;
void Nested(std::string path) {
auto screen = ScreenInteractive::FitComponent();
auto back_button = Button("Back", screen.ExitLoopClosure());
auto goto_1 = Button("Goto /1", [path] { Nested(path + "/1"); });
auto goto_2 = Button("Goto /2", [path] { Nested(path + "/2"); });
auto goto_3 = Button("Goto /3", [path] { Nested(path + "/3"); });
auto layout = Container::Vertical({
back_button,
goto_1,
goto_2,
goto_3,
});
auto renderer = Renderer(layout, [&] {
return vbox({
text("path: " + path),
separator(),
back_button->Render(),
goto_1->Render(),
goto_2->Render(),
goto_3->Render(),
}) | border;
});
screen.Loop(renderer);
}
int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::FitComponent();
auto button_quit = Button("Quit", screen.ExitLoopClosure());
auto button_nested = Button("Nested", [] { Nested(""); });
screen.Loop(Container::Vertical({
button_quit,
button_nested,
}));
return 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.

View File

@ -6,6 +6,7 @@
#include <functional> // for function #include <functional> // for function
#include <memory> // for shared_ptr #include <memory> // for shared_ptr
#include <string> // for string #include <string> // for string
#include <thread>
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/event.hpp" // for Event #include "ftxui/component/event.hpp" // for Event
@ -31,6 +32,11 @@ class ScreenInteractive : public Screen {
CapturedMouse CaptureMouse(); CapturedMouse CaptureMouse();
private: private:
void Install();
void Uninstall();
void Main(Component component);
ScreenInteractive* suspended_screen_ = nullptr;
void Draw(Component component); void Draw(Component component);
void EventLoop(Component component); void EventLoop(Component component);
@ -54,6 +60,7 @@ class ScreenInteractive : public Screen {
std::string reset_cursor_position; std::string reset_cursor_position;
std::atomic<bool> quit_ = false; std::atomic<bool> quit_ = false;
std::thread event_listener_;
int cursor_x_ = 1; int cursor_x_ = 1;
int cursor_y_ = 1; int cursor_y_ = 1;

View File

@ -275,6 +275,43 @@ CapturedMouse ScreenInteractive::CaptureMouse() {
} }
void ScreenInteractive::Loop(Component component) { void ScreenInteractive::Loop(Component component) {
static ScreenInteractive* g_active_screen = nullptr;
// Suspend previously active screen:
if (g_active_screen) {
std::swap(suspended_screen_, g_active_screen);
std::cout << suspended_screen_->reset_cursor_position
<< suspended_screen_->ResetPosition(/*clear=*/true);
suspended_screen_->dimx_ = 0;
suspended_screen_->dimy_ = 0;
suspended_screen_->Uninstall();
}
// This screen is now active:
g_active_screen = this;
g_active_screen->Install();
g_active_screen->Main(component);
g_active_screen->Uninstall();
g_active_screen = nullptr;
// Put cursor position at the end of the drawing.
std::cout << reset_cursor_position;
// Restore suspended screen.
if (suspended_screen_) {
std::cout << ResetPosition(/*clear=*/true);
dimx_ = 0;
dimy_ = 0;
std::swap(g_active_screen, suspended_screen_);
g_active_screen->Install();
} else {
// On final exit, keep the current drawing and reset cursor position one
// line after it.
std::cout << std::endl;
}
}
void ScreenInteractive::Install() {
on_exit_functions.push([this] { ExitLoopClosure()(); }); on_exit_functions.push([this] { ExitLoopClosure()(); });
// Install signal handlers to restore the terminal state on exit. The default // Install signal handlers to restore the terminal state on exit. The default
@ -349,18 +386,12 @@ void ScreenInteractive::Loop(Component component) {
on_exit_functions.push([=] { std::cout << Set(parameters); }); on_exit_functions.push([=] { std::cout << Set(parameters); });
}; };
flush();
if (use_alternative_screen_) { if (use_alternative_screen_) {
enable({ enable({
DECMode::kAlternateScreen, DECMode::kAlternateScreen,
}); });
} }
// On exit, reset cursor one line after the current drawing.
on_exit_functions.push(
[this] { std::cout << reset_cursor_position << std::endl; });
disable({ disable({
DECMode::kCursor, DECMode::kCursor,
DECMode::kLineWrap, DECMode::kLineWrap,
@ -375,10 +406,19 @@ void ScreenInteractive::Loop(Component component) {
flush(); flush();
auto event_listener = quit_ = false;
event_listener_ =
std::thread(&EventListener, &quit_, event_receiver_->MakeSender()); std::thread(&EventListener, &quit_, event_receiver_->MakeSender());
}
// The main loop. void ScreenInteractive::Uninstall() {
ExitLoopClosure()();
event_listener_.join();
OnExit(0);
}
void ScreenInteractive::Main(Component component) {
while (!quit_) { while (!quit_) {
if (!event_receiver_->HasPending()) { if (!event_receiver_->HasPending()) {
Draw(component); Draw(component);
@ -405,9 +445,6 @@ void ScreenInteractive::Loop(Component component) {
event.screen_ = this; event.screen_ = this;
component->OnEvent(event); component->OnEvent(event);
} }
event_listener.join();
OnExit(0);
} }
void ScreenInteractive::Draw(Component component) { void ScreenInteractive::Draw(Component component) {