ArthurSonzogni / FTXUI

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

Modal windows, what am I doing wrong? #897

Open mietschie opened 1 month ago

mietschie commented 1 month ago

Hey,

I'm trying to get modal to work for my project but I can't get it right. Maybe someone can give me a hint why the following minimum example doesn't work? Is it even supposed to work like that? Any help appreciated!

Summary:

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

int main() {
  auto screen = ftxui::ScreenInteractive::Fullscreen();
  auto showHide = false;

  auto main = ftxui::Renderer([&]() {
    return ftxui::vbox(ftxui::text("main") | ftxui::center) | ftxui::border;
  });

  // goal: open modal dialog on Enter key
  main |= ftxui::CatchEvent([&](ftxui::Event event) {
    if (event == ftxui::Event::Return) {
      showHide = true;
      return true;
    }
    return false;
  });

  auto modal = ftxui::Renderer([&]() {
    return ftxui::vbox(ftxui::text("modal") | ftxui::center) | ftxui::border;
  });

  // goal: close modal dialog on Q button
  modal |= ftxui::CatchEvent([&](ftxui::Event event) {
    if (event == ftxui::Event::Character('q')) {
      showHide = false;
      return true;
    }
    return false;
  });

  main |= ftxui::Modal(modal, &showHide);

  screen.Loop(main);
  return 0;
}
ArthurSonzogni commented 1 month ago

I believe this is due to both of the component not to be "focusable". Indeed, the user needs to know "where" the events are applied. Here, this probably block one this early return.

Ideally, you wouldn't use ftxui::Renderer, this is mostly meant for non interactive component. Nevertheless, it accept a bool focused to allow a minimal kind of interactivity, being 'selected'.

Here is the solution:

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

int main() {
  auto screen = ftxui::ScreenInteractive::Fullscreen();
  auto showHide = false;

  auto main = ftxui::Renderer([&](bool focused) {
    auto element = ftxui::vbox(ftxui::text("main") | ftxui::center) | ftxui::border;
    if (focused) {
      element |= ftxui::focus;
      element |= ftxui::bold;
    }
    return element;
  });

  // goal: open modal dialog on Enter key
  main |= ftxui::CatchEvent([&](ftxui::Event event) {
    if (event == ftxui::Event::Return) {
      showHide = true;
      return true;
    }
    return false;
  });

  auto modal = ftxui::Renderer([&](bool focused) {
    auto element = ftxui::vbox(ftxui::text("modal") | ftxui::center) | ftxui::border;
    if (focused) {
      element |= ftxui::focus;
      element |= ftxui::bold;
    }
    return element;
  });

  // goal: close modal dialog on Q button
  modal |= ftxui::CatchEvent([&](ftxui::Event event) {
    if (event == ftxui::Event::Character('q')) {
      showHide = false;
      return true;
    }
    return false;
  });

  main |= ftxui::Modal(modal, &showHide);

  screen.Loop(main);
  return 0;
}
mietschie commented 1 month ago

Hey, thank you so much for the quick support. Works like a charm! I'm still learning the possibilities of FTXUI. You're saying you would not use ftxui::Render for this application. What should I use instead, if I - let's say - have a simple vertical container with a bunch of ftxui::MenuEntry's (as main view), which on Enter-key should open a modal dialog showing some detailed data about the entry?

Thanks again, really appreciated!