ArthurSonzogni / FTXUI

:computer: C++ Functional Terminal User Interface. :heart:
MIT License
6.49k stars 392 forks source link

Console doesn't clear previous renderer text after new renderer #809

Open darkonaito opened 6 months ago

darkonaito commented 6 months ago

When I select the menu first option, I get the both the current renderer's text "Now, please enter the server manager's address and port, so that you'll be able to see all the available public servers!" and the previous "Welcome, my client".

I tried using screen.Clear() too, and it didn't work.

#include <ftxui/component/component.hpp>
#include <ftxui/dom/elements.hpp>
#include <ftxui/screen/screen.hpp>
#include <ftxui/component/screen_interactive.hpp>

#include <iostream>
#include <array>
#include <string>

using namespace ftxui;
using namespace std;

enum class ConnectionMethod
{
    ByName, ByAddress
};

void promptForConnectionMethod();

void promptForServerName();
void promptForServerAddress();
void promptForServerManager();

auto screen {
    ScreenInteractive::TerminalOutput()
};

int main()
{
    promptForConnectionMethod();

    return 0;
}

void promptForConnectionMethod()
{
    std::vector<std::string> conncet_choices {
        "Connect by name...",
        "Connect by address and port..."
    };

    int selected {0};

    MenuOption option;

    option.on_enter = screen.ExitLoopClosure();

    auto connect_menu {
        Menu(                    
            &conncet_choices,
            &selected,
            option
        )
    };

    auto renderer {
        Renderer(
            connect_menu,
            [&]
            {
                return
                    center(
                        vbox(
                            center(text("Welcome, my client!") | color(Color::Red3Bis) | bold),
                            gauge(0), gauge(0),
                            text(
                                "Welcome to my first working multiplayer game, Medium Boxes."
                            )
                            | color(Color::LightSkyBlue1),
                            gauge(0),
                            center(
                                text("Now, choose how you'd prefer to connect to a server!")
                                | color(Color::LightCyan3)
                            ),
                            gauge(0),
                            border(connect_menu->Render())
                        )
                    )
                ;
            }
        )
    };

    screen.Loop(renderer);

    if(selected == 0)
    {
        promptForServerManager();
        promptForServerName();
    } 
    else 
    {
        promptForServerAddress();
    }
}

void promptForServerName()
{

}

void promptForServerAddress()
{

    auto screen {
        ScreenInteractive::TerminalOutput()
    };
}

void promptForServerManager()
{
    auto renderer {
        Renderer(
            [&]
            {
                return 
                    center(
                        vbox(
                            text("Now, please enter the server manager's address and port, so that you'll be able to see all the available public servers!")  | color(Color::LightGreenBis),
                            gauge(0)
                        )
                    )
                ;
            }
        )
    };

    screen.Loop(renderer);
}
ArthurSonzogni commented 6 months ago

Thanks!

I think this is the consequence of: https://github.com/ArthurSonzogni/FTXUI/blob/main/src/ftxui/component/screen_interactive.cpp#L544-L548

That is to say, after unwrapping the last screen, a new line is inserted at the end, so that the shell's command line starts below and do not override the previous frame.

In your case, there is a single Screen, where Loop is called multiple time. So a new line is added each time.

I believe this is something we should improve.

In the meantime, I propose two alternatives:

1. Use "nested" screen.

That is to say, use different screen in each function, and call one function while executing the previous one.

code ```cpp #include #include #include #include #include #include #include using namespace ftxui; using namespace std; enum class ConnectionMethod { ByName, ByAddress }; void promptForConnectionMethod(); void promptForServerName(); void promptForServerAddress(); void promptForServerManager(); int main() { promptForConnectionMethod(); return 0; } void promptForConnectionMethod() { auto screen = ScreenInteractive::TerminalOutput(); std::vector connect_choice{ "Connect by name...", "Connect by address and port...", "Exit", }; int selected = 0; MenuOption option; option.on_enter = [&] { if (selected == 0) { promptForServerManager(); } else if (selected == 1) { promptForServerAddress(); } else if (selected == 2) { screen.Exit(); } }; auto connect_menu = Menu(&connect_choice, &selected, option); auto renderer = Renderer(connect_menu, [&] { return vbox({ text("Welcome, my client!") | color(Color::Red3Bis) | bold | center, text(""), text("Selected = " + std::to_string(selected)) | color(Color::LightGreenBis) | bold | center, text(""), text("Welcome to my first working multiplayer game, Medium " "Boxes.") | color(Color::LightSkyBlue1), text(""), text("Now, choose how you'd prefer to connect to a server!") | color(Color::LightCyan3) | center, text(""), connect_menu->Render() | border, }) | center; }); screen.Loop(renderer); } void promptForServerName() {} void promptForServerAddress() { auto screen = ScreenInteractive::TerminalOutput(); } void promptForServerManager() { auto screen = ScreenInteractive::TerminalOutput(); auto renderer = Renderer([&] { return vbox({ text("Now, please enter the server manager's address and " "port, so that you'll be able to see all the available " "public servers!") | color(Color::LightGreenBis), gauge(0), }) | center; }); screen.Loop(renderer); } ```

2. Use the Tab

Create a "super" component that will regroup every "page" you want to display.

The "super" component will be a Tab({...pages}, &selected_page). To display a different page, you will have to update the selected_page integer.

darkonaito commented 6 months ago

@ArthurSonzogni Using nested screens doesn't seem to work, now I get the whole previous frame

untyper commented 5 months ago

Can confirm, I have the same bug. The bug seems to be present in the Tab container too when switching to a new tab. The screen isnt cleared after the first time the new tab is rendered.

For now I'm using a conditional to check whether the new "tab" has already been rendered to decide whether the program should render it again, and it seems to be working good