Closed b1060t closed 3 years ago
Hello @Biobots!
I am not totally sure of what you want. Let me present the two potential solution:
You can use:
auto document = hbox(paragraph(long_string))
hflow
implements the flow layout.paragraph
helps breaking a long string into a list of words.Try with the example: https://arthursonzogni.com/FTXUI/doc/_2examples_2dom_2paragraph_8cpp-example.html#a3
I don't have anything built-in. Here is an example you can adapt: https://github.com/ArthurSonzogni/chrome-log-beautifier/blob/master/src/ui/log_displayer.cpp#L28
Selected parts:
Element LogDisplayer::RenderLines(const std::vector<std::string>& lines) {
Elements list;
for (auto& line : lines) {
Decorator line_decorator = nothing;
if (index++ == selected_) {
if (Focused())
line_decorator = focus | inverted;
else
line_decorator = focus;
}
list.push_back(text(line) | line_decorator)
}
return vbox(std::move(list)) | frame | border;
}
bool LogDisplayer::OnEvent(Event event) {
if (!Focused())
return false;
int old_selected = selected_;
if (event == Event::ArrowUp || event == Event::Character('k'))
selected_--;
if (event == Event::ArrowDown || event == Event::Character('j'))
selected_++;
if (event == Event::Tab && size)
selected_ = (selected_ + 1) % size;
if (event == Event::TabReverse && size)
selected_ = (selected_ + size - 1) % size;
selected_ = std::max(0, std::min(size-1, selected_));
if (selected_ != old_selected) {
return true;
}
return false;
}
@ArthurSonzogni Thanks a lot! I'm trying the second solution now. However. there's another problem. It seems that in interactive mode, the 'render()' function is only called when there are some keyboard input. Is it possible to render the screen at a fixed speed like 30FPS since the output log should be automatically updated?
My logger's render function is called by logger.RenderLog()
which is defined as:
class LogDisplayer : public Component
{
private:
vector<string> _payload;
public:
LogDisplayer() = default;
void getLog(string str)
{
istringstream ss(str);
string tmp;
while(getline(ss, tmp))
{
_payload.push_back(tmp);
}
}
Element RenderLog()
{
Elements list;
for(int i = _payload.size() - 13; i < _payload.size(); i++)
{
Element doc = hbox({
text(buildwstring(_payload[i])),
}) | flex;
list.push_back(doc);
}
if(list.empty()) list.push_back(text(L"empty"));
return window(text(L"test"), vbox(list) | flex);
}
};
You can post custom event using PostEvent
and a Event::Custom
. Those will refresh the view.
You usually want to send them after you know some data have been added. However, I guess in your case you are not actively waiting, but are checking periodically if there are updates. In this case, I guess you can create a new thread that will periodically send such events.
std::thread update([&screen]() {
for (;;) {
using namespace std::chrono_literals;
std::this_thread::sleep_for(0.05s);
screen.PostEvent(Event::Custom);
}
});
From the example: https://github.com/ArthurSonzogni/FTXUI/blob/92ec5ab4ca0f71e042844fa8ac88bc086a1426a9/examples/component/homescreen.cpp#L370
Actually I evoked a new thread to keep query for log needed. By adding PostEvent
, it indeed worked as I expected.
system("clear");
auto screen = ScreenInteractive::FixedSize(100, 30);
MainWindow component;
thread t([&](){
AsyncCommand cmd(std::string("logcat"));
cmd.execute();
while(!cmd.isDone())
{
string log = cmd.getOutput();
if(log.length() > 0)
{
component.getLog(log);
screen.PostEvent(Event::Custom);
}
}
});
t.detach();
screen.Loop(&component);
However, another problem I've met is that it's a bit unconvenient when the length of a sentence is greater than the maximum width of the screen. Though this can be implemented by the user, I'm still wondering if you are considering adding a built-in textarea which can automatically increase its height when there's not enough space to show one sentence without line break.
The difficulty is:
The children pass their Requirement
to their parent:
https://github.com/ArthurSonzogni/FTXUI/blob/92ec5ab4ca0f71e042844fa8ac88bc086a1426a9/include/ftxui/dom/node.hpp#L27
Then the parent give them a Box
where they are allowed to draw themselves.
https://github.com/ArthurSonzogni/FTXUI/blob/92ec5ab4ca0f71e042844fa8ac88bc086a1426a9/include/ftxui/dom/node.hpp#L31
For the hflow
layout, it is very difficult to provide a Requirement
. Because the dependencies are inversed. We need to know the parent's final width to determine the children required height.
For now, the hflow
layout just require a 1x1 pixel to draw itself and declare themselves to be happy about being expanded if possible:
https://github.com/ArthurSonzogni/FTXUI/blob/92ec5ab4ca0f71e042844fa8ac88bc086a1426a9/src/ftxui/dom/hflow.cpp#L13
I regret I don't have something built-in for this use case.
Thanks! Another question:
This is how I define the movement of the window:
bool OnEvent(Event e)
{
if(!Focused()) return false;
if(e == Event::ArrowUp) _yoffset++;
if(e == Event::ArrowDown) _yoffset--;
if(e == Event::ArrowRight) _xoffset++;
if(e == Event::ArrowLeft) _xoffset--;
return true;
}
Once the component I defined is activated, I can never focus to the other part of the screen. I also tried hjkl
to define the movement, but the the focused component still stays fixed using the arrow button.
I guess it depends on which component contains your component.
When the parent receives an event, it:
Your component, as a children seems to accept any event.
See for instance the default implementation Container, which is used in most built-in component: https://github.com/ArthurSonzogni/FTXUI/blob/92ec5ab4ca0f71e042844fa8ac88bc086a1426a9/src/ftxui/component/container.cpp#L32
bool Container::OnEvent(Event event) {
if (!Focused())
return false;
if (ActiveChild() && ActiveChild()->OnEvent(event))
return true;
return (this->*event_handler_)(event);
}
I guess in your case:
(ActiveChild() && ActiveChild()->OnEvent(event))
is always true when the ActiveChild() is your component.
Then this->*eventhandler is never called. So the current ActiveChild stays the same.
Thanks. It seems that my OnEvent
function is always returning true once focused. After fixing that, I'm able to enter the other component.
I'd like to render a multi-line string which is scrollable to display logs. Is there any element I can use to achieve this?