ArthurSonzogni / FTXUI

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

Forcing the selection of a menu item. #296

Closed azchatlanin closed 2 years ago

azchatlanin commented 2 years ago

Hello. I created a menu. Then at some point in time, I need to change this menu. And the size of the menu becomes smaller than it was.

If the selected menu item was larger than the size of the new menu before the change, then there is no selection and the menu item becomes indeterminate until you move the mouse or keyboard:)

Is there any official way to change for example focused_entry() = 0? I couldn't get to him. Or I'm doing something wrong.

Example:

#include <string>
#include <vector>
#include <memory>
#include <filesystem>
#include <algorithm>
#include <iterator>

#include <ftxui/component/captured_mouse.hpp>
#include <ftxui/component/component.hpp>    
#include <ftxui/component/component_base.hpp>
#include <ftxui/component/component_options.hpp> 
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>

int main(int argc, const char* argv[]) {

  std::vector<std::string> current_list = { "asdf 1", "adsf 2", "adsf 3", "asdf 4" };

  int selected = 0;
  auto files = ftxui::Menu(&current_list, &selected);

  auto files_rnd = ftxui::Renderer(files, [&] {
    return ftxui::hbox({
      ftxui::vbox({
        ftxui::text("some text"),
      }) | ftxui::border | size(ftxui::WIDTH, ftxui::EQUAL, 800) | ftxui::xflex,

      ftxui::vbox({
        files->Render(),
      }) | ftxui::border | size(ftxui::WIDTH, ftxui::EQUAL, 800) | ftxui::xflex,

      ftxui::vbox({
        ftxui::text("some text"),
      }) | ftxui::border | size(ftxui::WIDTH, ftxui::EQUAL, 800) | ftxui::xflex,
    }) | ftxui::flex;
  });

  auto container = ftxui::Container::Vertical({
    files_rnd,
  });

  auto main_container = ftxui::CatchEvent(container, [&](ftxui::Event event) {
    if (event == ftxui::Event::Character('h')) 
    {
      current_list.erase(current_list.begin());
      current_list.erase(current_list.begin());
      return true;
    }

    return false;
  });

  auto screen = ftxui::ScreenInteractive::Fullscreen();
  screen.Loop(main_container);
}

https://user-images.githubusercontent.com/21025345/147914105-f3e6f174-1705-4963-acf3-af650a64f8b6.mp4

ArthurSonzogni commented 2 years ago

Thanks for submitting this issue!

I guess we clamp selected in OnEvent only. Should we do it in Render() too?

I believe the solution might be to:

*selected_ = std::clamp(*selected_, 0, entries.size() - 1);

In MenuBase::Render(): https://github.com/ArthurSonzogni/FTXUI/blob/fc92f52b4c30ecdf7e2723ad9fcc2a84a26af1ab/src/ftxui/component/menu.cpp#L32

We probably want to do something similar for RadioboxBase and ToggleBase, which are constructed the same way.

Do you want to try it yourself and if this solves your problem, to submit a PR?

azchatlanin commented 2 years ago

`

Thanks for submitting this issue!

I guess we clamp selected in OnEvent only. Should we do it in Render() too?

I believe the solution might be to:

*selected_ = std::clamp(*selected_, 0, entries.size() - 1);

In MenuBase::Render():

https://github.com/ArthurSonzogni/FTXUI/blob/fc92f52b4c30ecdf7e2723ad9fcc2a84a26af1ab/src/ftxui/component/menu.cpp#L32

We probably want to do something similar for RadioboxBase and ToggleBase, which are constructed the same way.

Do you want to try it yourself and if this solves your problem, to submit a PR?

Yes, I will do PR in the near future. Yesterday, when I looked at the code, that's exactly what I thought it would be nice to do a border check in the render.