FTXUI icon indicating copy to clipboard operation
FTXUI copied to clipboard

Console doesn't clear previous renderer text after new renderer

Open darkonaito opened this issue 1 year ago • 3 comments

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);
}

darkonaito avatar Jan 14 '24 10:01 darkonaito

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
#include <ftxui/component/component.hpp>
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>
#include <ftxui/screen/screen.hpp>

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

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<std::string> 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.

ArthurSonzogni avatar Jan 14 '24 16:01 ArthurSonzogni

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

darkonaito avatar Jan 15 '24 10:01 darkonaito

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

untyper avatar Jan 31 '24 20:01 untyper