Closed Matheus-Garbelini closed 4 years ago
Hi, thank for using the library! I would be happy to help you and see what you are going to build with it.
I am not sure to understand what you call a windows.
If you want to put several element together vertically, you can use a vertical box => vbox If might also be interested into the "paragraph" element
Maybe the "package manager" example could be what you want: https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/dom/package_manager.cpp https://www.youtube.com/watch?v=x0q-olsPgNg
Maybe the starter project could be close to what you want: https://github.com/ArthurSonzogni/ftxui-starter
@ArthurSonzogni Thanks for the fast response. In summary, my application writes a lot of information to the console via cout. I just want to be able to write these debugging messages in different boxes. i.e.,I think the best word to describe is that I'm trying to create a multiline textbox just to print fast information.
I see!
I think you can keep track in "n" vector of the lines you want draw.
Let call this your "model":
std::vector<std::wstring> data_box_1;
std::vector<std::wstring> data_box_2;
std::vector<std::wstring> data_box_3
Let's create a function to render your model:
auto render = [&](){
std::vector<Element> box_1;
for(auto& line : data_box_1)
entries.push_back(text(line))
// [...] Same for box_2 and box_3.
return
hbox(
window(text(L" box_1 "), vbox(std::move(box_1))),
window(text(L" box_2 "), vbox(std::move(box_2))),
window(text(L" box_3 "), vbox(std::move(box_3)))
);
};
Then let' create a function to refresh the screen. You can call it everytime you update your model.
std::string reset_position;
auto refresh = [&](){
auto document = render();
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document.get());
std::cout << reset_position << screen.ToString() << std::flush;
reset_position = screen.ResetPosition();
};
You can use the example: https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/dom/package_manager.cpp
If you want your application to respond to the user event you have to use the components from the "component" directory. If you simply want to display (potentially periodically) a document from you application, you have to use the elements from the "dom" directory. <= I guess this is what you are doing right now. Great!
@ArthurSonzogni Thanks a lot. I had to extend such a template to create a generic logger.
My main concern is real-time logging. Moreover, I had to limit the list of entries to a ring buffer, otherwise, there's no end to messages being printed. With this new class, you can just call: WindowLogger.LOG("Value: ", rand(), <variarg what you want>);
It adds your messages to an internal wstring vector. Then, you can just call auto document = WindowLogger.element()
to return the updated dom.
Check the result in asciinema: https://asciinema.org/a/318630 or in the gif below.
This sort of reactiveness could be useful for more real-time or dynamic components on FTXUI. Particularly for applications which do not have user interaction, but you need APIs to easily interact with the UI components.
The full code with this custom component is shown below. Regards and thanks again.
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
#include <string>
#include <sstream>
#include <iomanip>
#include "ftxui/dom/elements.hpp"
#include "ftxui/screen/screen.hpp"
#include "ftxui/screen/string.hpp"
using namespace std::chrono_literals;
using namespace ftxui;
using namespace std;
class WindowLogger
{
private:
struct MSGS
{
wstring msg;
Color msg_color;
};
vector<Element> line_elements;
vector<MSGS> line_msgs;
wstringstream string_converter;
wstring name;
uint16_t max_lines;
bool enable_flex;
inline auto text_element(wstring msg, Color text_color)
{
if (text_color != Color::Default)
return hbox(text(msg) | color(text_color));
else
return text(msg);
}
template <class... T>
inline void add_msg(Color msg_color, T &&... args)
{
string_converter.seekp(ios::beg); // Reset string stream
((string_converter << forward<T>(args)), ...);
if (line_msgs.size() < max_lines)
{
line_msgs.push_back({string_converter.str(), msg_color});
}
else
{
rotate(line_msgs.begin(), line_msgs.begin() + 1, line_msgs.end());
// insert new text at the last line
line_msgs[line_msgs.size() - 1] = {string_converter.str(), msg_color};
}
}
public:
WindowLogger(const char *window_name, uint16_t max_lines = 1, bool fill_lines = false, bool enable_flex = false)
{
wstringstream s;
s << window_name;
name = s.str();
this->max_lines = max_lines;
line_elements.reserve(max_lines);
line_msgs.reserve(max_lines);
if (fill_lines)
{
// Fill blank lines to keep maximum heigth at start
for (size_t i = 0; i < max_lines; i++)
{
line_msgs.push_back({string_converter.str(), Color::Default});
}
}
this->enable_flex = enable_flex;
};
inline auto element()
{
for (auto &el : line_msgs)
{
line_elements.push_back(text_element(el.msg, el.msg_color));
}
if (enable_flex)
return hbox(
window(text(name), vbox(std::move(line_elements)) | flex));
else
return hbox(
window(text(name), vbox(std::move(line_elements))));
}
template <class... T>
inline void LOG(T &&... args)
{
add_msg(Color::Default, args...);
}
template <class... T>
inline void LOGC(T &&... args)
{
add_msg(Color::Cyan, args...);
}
template <class... T>
inline void LOGR(T &&... args)
{
add_msg(Color::Red, args...);
}
template <class... T>
inline void LOGG(T &&... args)
{
add_msg(Color::Green, args...);
}
template <class... T>
inline void LOGY(T &&... args)
{
add_msg(Color::Yellow, args...);
}
template <class... T>
inline void LOGM(T &&... args)
{
add_msg(Color::Magenta, args...);
}
};
WindowLogger test1(" Information ", 5, true, true);
WindowLogger test2(" Events ", 5, true, true);
int main(int argc, const char *argv[])
{
thread t([&]() {
std::string reset_position;
uint8_t i = 0;
while (true)
{
auto document = window(text(L" Real Time Logs "),
vbox(hbox(test1.element(), test2.element()),
window(hbox(text(L" "), spinner(15, i) | bold, text(L" Summary ")), text(L" Running ") | color(Color::Green))));
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document.get());
std::cout << reset_position << screen.ToString() << std::flush;
reset_position = screen.ResetPosition();
std::this_thread::sleep_for(0.03s);
i += 1 % 10;
}
});
for (;;)
{
test1.LOG("Value: ", rand());
test1.LOGG("Value: ", rand());
test2.LOGM("[", __FILE__, "] ", rand());
test2.LOGY("[", __FILE__, "] ", rand());
std::this_thread::sleep_for(0.1s);
}
std::cout << std::endl;
}
That's beautiful!
Hi, thanks a lot for the library. I was looking at the examples and wonder if it's possible to create multiple windows which are used solely show log information.
In short, I want to log multiple information in different windows. Is there an optimal design using this library to achieve that?
The maximum I could find is two use two windows, each one having a generic element inside which I have to keep updating the text:
However, "text" element doesn't seem to support multiple lines with "\n". Is there a suggestion on how to do that?
Regards.