ArthurSonzogni / FTXUI

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

How to scroll elements , not components #907

Open Deshdeepak1 opened 3 months ago

Deshdeepak1 commented 3 months ago

If I have Components like menu, radiobox, etc in container, it is scrollable. But elements don't seem to be scrollable.

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

ftxui::Element get_document() {
  using namespace ftxui;
  auto cell = [](const char* t) { return text(t) | border; };
  auto document =  //
      gridbox({
          {
              cell("north-west"),
              cell("north"),
              cell("north-east"),
          },
          {
              cell("center-west"),
              gridbox({
                  {
                      cell("center-north-west"),
                      cell("center-north-east"),
                  },
                  {
                      cell("center-south-west"),
                      cell("center-south-east"),
                  },
              }),
              cell("center-east"),
          },
          {
              cell("south-west"),
              cell("south"),
              cell("south-east"),
          },
      });
  return document;
}

int main() {
  using namespace ftxui;
  auto cell = [](const char* t) { return text(t) | border; };

  int n;
  std::cin >> n;

  if (n == 1) {
    Element document = get_document();
    auto component =
        Renderer([document] { return document | vscroll_indicator | frame; });
    auto screen = ScreenInteractive::FullscreenAlternateScreen();
    screen.Loop(component);
  } else {
    Elements documents;
    for (int i = 0; i < 10; i++) {
      auto document = get_document();
      document |= border;
      documents.push_back(document);
    }
    auto component = Renderer(
        [documents] { return vbox(documents) | vscroll_indicator | frame; });
    auto screen = ScreenInteractive::FullscreenAlternateScreen();
    screen.Loop(component);
  }

  return 0;
}

neither the gridbox seems to scroll, nor the vbox containing multiple gridbox

ArthurSonzogni commented 3 months ago

Hello,

You have to use the selected/focus decorator to put the focus on the selected or focused element. Then, the frame will automatically center its view toward this element.

See: https://github.com/ArthurSonzogni/FTXUI/blob/5a9ef876a14b91a3cc5b0313cfdaf6b5527eccf4/include/ftxui/dom/elements.hpp#L151-L159

See also the radiobox implementation.

--

Alternatively, you might want to look at:

Deshdeepak1 commented 3 months ago

Thanks,

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

ftxui::Element get_document() {
  using namespace ftxui;
  auto cell = [](const char* t) { return text(t) | border; };
  auto document =  //
      gridbox({
          {
              cell("north-west"),
              cell("north"),
              cell("north-east"),
          },
          {
              cell("center-west"),
              gridbox({
                  {
                      cell("center-north-west"),
                      cell("center-north-east"),
                  },
                  {
                      cell("center-south-west"),
                      cell("center-south-east"),
                  },
              }),
              cell("center-east"),
          },
          {
              cell("south-west"),
              cell("south"),
              cell("south-east"),
          },
      });
  return document;
}

int main() {
  using namespace ftxui;
  auto cell = [](const char* t) { return text(t) | border; };

  int n;
  std::cin >> n;

  if (n == 1) {
    Element document = get_document();

    auto component = Renderer([document] { return document; });
    component |= Scroller;
    auto screen = ScreenInteractive::FullscreenAlternateScreen();
    screen.Loop(component);
  } else {
    Elements documents;
    for (int i = 0; i < 10; i++) {
      auto document = get_document();
      document |= border;
      documents.push_back(document);
    }
    auto component = Renderer([documents] { return vbox(documents); });
    component |= Scroller;
    auto screen = ScreenInteractive::FullscreenAlternateScreen();
    screen.Loop(component);
  }

  return 0;
}

I am using Scroller , both works .

But This Scroller if applied to component makes component unresponsive to event , but I use this ComponenetDecorator with them, which works.

ftxui::ComponentDecorator vscroll_renderer = ftxui::Renderer(
  [](ftxui::Element e) { return e | ftxui::vscroll_indicator | ftxui::frame; });

Is this the correct way?

Also I want to ask, is this the correct way to convert element to Component, am I doing it correct,

auto component = Renderer([document] { return document; });

or is there some better way ?

Also in catch event is there a way to map bindings to scroll to top /bottom , e.g. something vim like gg for top , G for bottom.

Anyway, thanks again.