mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-09-18 09:08:08 +08:00
23
src/ftxui/component/checkbox.cpp
Normal file
23
src/ftxui/component/checkbox.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "ftxui/component/checkbox.hpp"
|
||||
#include <functional>
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Element CheckBox::Render() {
|
||||
bool is_focused = Focused();
|
||||
auto style = is_focused ? focused_style : unfocused_style;
|
||||
auto focus_management = is_focused ? focus : state ? select : nothing;
|
||||
return hbox(text(state ? checked : unchecked),
|
||||
text(label) | style | focus_management);
|
||||
}
|
||||
|
||||
bool CheckBox::OnEvent(Event event) {
|
||||
if (event == Event::Character(' ') || event == Event::Return) {
|
||||
state = !state;
|
||||
on_change();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
56
src/ftxui/component/component.cpp
Normal file
56
src/ftxui/component/component.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "ftxui/component/component.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
namespace ftxui {
|
||||
void Component::Detach() { if (!parent_) return; auto it = std::find(std::begin(parent_->children_),
|
||||
std::end(parent_->children_), this);
|
||||
parent_->children_.erase(it);
|
||||
|
||||
}
|
||||
|
||||
void Component::Attach(Component* parent) {
|
||||
Detach();
|
||||
parent_ = parent;
|
||||
parent_->children_.push_back(this);
|
||||
}
|
||||
|
||||
void Component::Add(Component* child) {
|
||||
child->Attach(this);
|
||||
}
|
||||
|
||||
Component::~Component() {
|
||||
Detach();
|
||||
}
|
||||
|
||||
bool Component::OnEvent(Event event) {
|
||||
for(Component* child : children_) {
|
||||
if (child->OnEvent(event))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Component* Component::ActiveChild() {
|
||||
return children_.empty() ? nullptr : children_.front();
|
||||
}
|
||||
|
||||
Element Component::Render() {
|
||||
if (children_.size() == 1)
|
||||
return children_.front()->Render();
|
||||
|
||||
return text(L"Not implemented component");
|
||||
}
|
||||
|
||||
bool Component::Focused() {
|
||||
Component* current = this;
|
||||
for(;;) {
|
||||
Component* parent = current->parent_;
|
||||
if (!parent)
|
||||
return true;
|
||||
if (parent->ActiveChild() != current)
|
||||
return false;
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
108
src/ftxui/component/container.cpp
Normal file
108
src/ftxui/component/container.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "ftxui/component/container.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
// static
|
||||
Container Container::Horizontal() {
|
||||
Container container;
|
||||
container.event_handler_ = &Container::HorizontalEvent;
|
||||
container.render_handler_ = &Container::HorizontalRender;
|
||||
return container;
|
||||
}
|
||||
|
||||
// static
|
||||
Container Container::Vertical() {
|
||||
Container container;
|
||||
container.event_handler_ = &Container::VerticalEvent;
|
||||
container.render_handler_ = &Container::VerticalRender;
|
||||
return container;
|
||||
}
|
||||
|
||||
// static
|
||||
Container Container::Tab(int* selector) {
|
||||
Container container;
|
||||
container.event_handler_ = &Container::TabEvent;
|
||||
container.render_handler_ = &Container::TabRender;
|
||||
container.selector_ = selector;
|
||||
return container;
|
||||
}
|
||||
|
||||
bool Container::OnEvent(Event event) {
|
||||
if (!Focused())
|
||||
return false;
|
||||
|
||||
if (ActiveChild()->OnEvent(event))
|
||||
return true;
|
||||
|
||||
return (this->*event_handler_)(event);
|
||||
}
|
||||
|
||||
Component* Container::ActiveChild() {
|
||||
return children_[*selector_ % children_.size()];
|
||||
}
|
||||
|
||||
bool Container::VerticalEvent(Event event) {
|
||||
selected_ %= children_.size();
|
||||
// Left pressed ?
|
||||
if (event == Event::ArrowUp || event == Event::Character('k')) {
|
||||
if (selected_ != 0) {
|
||||
selected_--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Left pressed ?
|
||||
if (event == Event::ArrowDown || event == Event::Character('j')) {
|
||||
if (selected_ != int(children_.size()) - 1) {
|
||||
selected_++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Container::HorizontalEvent(Event event) {
|
||||
selected_ %= children_.size();
|
||||
// Left pressed ?
|
||||
if (event == Event::ArrowLeft || event == Event::Character('h')) {
|
||||
if (selected_ != 0) {
|
||||
selected_--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Left pressed ?
|
||||
if (event == Event::ArrowRight || event == Event::Character('l')) {
|
||||
if (selected_ != int(children_.size()) - 1) {
|
||||
selected_++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Element Container::Render() {
|
||||
return (this->*render_handler_)();
|
||||
}
|
||||
|
||||
Element Container::VerticalRender() {
|
||||
Elements elements;
|
||||
for(auto& it : children_)
|
||||
elements.push_back(it->Render());
|
||||
return vbox(std::move(elements));
|
||||
}
|
||||
|
||||
Element Container::HorizontalRender() {
|
||||
Elements elements;
|
||||
for(auto& it : children_)
|
||||
elements.push_back(it->Render());
|
||||
return hbox(std::move(elements));
|
||||
}
|
||||
|
||||
Element Container::TabRender() {
|
||||
return ActiveChild()->Render();
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
39
src/ftxui/component/event.cpp
Normal file
39
src/ftxui/component/event.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "ftxui/component/event.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
constexpr int ESC = int(27);
|
||||
|
||||
// --- Character ---
|
||||
Event Event::Character(int c) {
|
||||
return Event{c};
|
||||
}
|
||||
|
||||
// --- Arrow ---
|
||||
Event Event::ArrowLeft{ESC, '[', 'D'};
|
||||
Event Event::ArrowRight{ESC, '[', 'C'};
|
||||
Event Event::ArrowUp{ESC, '[', 'A'};
|
||||
Event Event::ArrowDown{ESC, '[', 'B'};
|
||||
|
||||
// --- Other ---
|
||||
Event Event::Backspace{127};
|
||||
Event Event::Delete{ESC, '[', '3', '~'};
|
||||
Event Event::Escape{ESC};
|
||||
Event Event::Return{10};
|
||||
|
||||
Event Event::F1{ESC, '[', 'O', 'P'};
|
||||
Event Event::F2{ESC, '[', 'O', 'Q'};
|
||||
Event Event::F3{ESC, '[', 'O', 'R'};
|
||||
Event Event::F4{ESC, '[', 'O', 'S'};
|
||||
Event Event::F5{ESC, '[', '1', '5', '~'};
|
||||
Event Event::F6{ESC, '[', '1', '7', '~'};
|
||||
Event Event::F7{ESC, '[', '1', '8', '~'};
|
||||
Event Event::F8{ESC, '[', '1', '9', '~'};
|
||||
Event Event::F9{ESC, '[', '2', '0', '~'};
|
||||
Event Event::F10{ESC, '[', '2', '1', '~'};
|
||||
Event Event::F11{ESC, '[', '2', '1', '~'}; // Same as F10 ?
|
||||
Event Event::F12{ESC, '[', '2', '4', '~'};
|
||||
|
||||
Event Event::Custom{0, 0, 0, 0, 0};
|
||||
|
||||
} // namespace ftxui
|
86
src/ftxui/component/input.cpp
Normal file
86
src/ftxui/component/input.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "ftxui/component/input.hpp"
|
||||
#include "ftxui/screen/string.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
// Component implementation.
|
||||
Element Input::Render() {
|
||||
cursor_position = std::max(0, std::min<int>(content.size(), cursor_position));
|
||||
auto main_decorator = flex | size(HEIGHT, EQUAL, 1);
|
||||
bool is_focused = Focused();
|
||||
|
||||
// Placeholder.
|
||||
if (content.size() == 0) {
|
||||
if (is_focused)
|
||||
return text(placeholder) | dim | inverted | main_decorator;
|
||||
else
|
||||
return text(placeholder) | dim | main_decorator;
|
||||
}
|
||||
|
||||
// Not focused.
|
||||
if (!is_focused)
|
||||
return text(content) | main_decorator;
|
||||
|
||||
std::wstring part_before_cursor = content.substr(0,cursor_position);
|
||||
std::wstring part_at_cursor = cursor_position < (int)content.size()
|
||||
? content.substr(cursor_position, 1)
|
||||
: L" ";
|
||||
std::wstring part_after_cursor = cursor_position < (int)content.size() - 1
|
||||
? content.substr(cursor_position + 1)
|
||||
: L"";
|
||||
auto focused =
|
||||
is_focused ? focus : select;
|
||||
|
||||
return
|
||||
hbox(
|
||||
text(part_before_cursor),
|
||||
text(part_at_cursor) | underlined | focused,
|
||||
text(part_after_cursor)
|
||||
) | flex | inverted | frame | main_decorator;
|
||||
|
||||
}
|
||||
bool Input::OnEvent(Event event) {
|
||||
cursor_position = std::max(0, std::min<int>(content.size(), cursor_position));
|
||||
std::wstring c;
|
||||
|
||||
// Backspace.
|
||||
if (event == Event::Backspace) {
|
||||
if (cursor_position == 0)
|
||||
return false;
|
||||
content.erase(cursor_position - 1, 1);
|
||||
cursor_position--;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Enter.
|
||||
if (event == Event::Return) {
|
||||
on_enter();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event == Event::Custom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event == Event::ArrowLeft && cursor_position > 0) {
|
||||
cursor_position--;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event == Event::ArrowRight && cursor_position < (int)content.size()) {
|
||||
cursor_position++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Content
|
||||
constexpr char ESC = char(27);
|
||||
if (event.values[0] != ESC) {
|
||||
wchar_t v = (char)event.values[0];
|
||||
content.insert(cursor_position, 1, v);
|
||||
cursor_position++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
46
src/ftxui/component/menu.cpp
Normal file
46
src/ftxui/component/menu.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "ftxui/component/menu.hpp"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Element Menu::Render() {
|
||||
std::vector<Element> elements;
|
||||
bool is_focused = Focused();
|
||||
for (size_t i = 0; i < entries.size(); ++i) {
|
||||
auto style = (selected != int(i))
|
||||
? normal_style
|
||||
: is_focused ? focused_style : selected_style;
|
||||
auto focused = (selected != int(i)) ? nothing : is_focused ? focus : select;
|
||||
auto icon = (selected != int(i)) ? L" " : L"> ";
|
||||
elements.push_back(text(icon + entries[i]) | style | focused);
|
||||
}
|
||||
return vbox(std::move(elements));
|
||||
}
|
||||
|
||||
bool Menu::OnEvent(Event event) {
|
||||
if (!Focused())
|
||||
return false;
|
||||
|
||||
int new_selected = selected;
|
||||
if (event == Event::ArrowUp || event == Event::Character('k'))
|
||||
new_selected--;
|
||||
if (event == Event::ArrowDown || event == Event::Character('j'))
|
||||
new_selected++;
|
||||
new_selected = std::max(0, std::min(int(entries.size()) - 1, new_selected));
|
||||
|
||||
if (selected != new_selected) {
|
||||
selected = new_selected;
|
||||
on_change();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event == Event::Return) {
|
||||
on_enter();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
45
src/ftxui/component/radiobox.cpp
Normal file
45
src/ftxui/component/radiobox.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "ftxui/component/radiobox.hpp"
|
||||
#include <functional>
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Element RadioBox::Render() {
|
||||
std::vector<Element> elements;
|
||||
bool is_focused = Focused();
|
||||
for (size_t i = 0; i < entries.size(); ++i) {
|
||||
auto style =
|
||||
(focused == int(i) && is_focused) ? focused_style : unfocused_style;
|
||||
auto focus_management =
|
||||
(focused != int(i)) ? nothing : is_focused ? focus : select;
|
||||
|
||||
const std::wstring& symbol = selected == int(i) ? checked : unchecked;
|
||||
elements.push_back(hbox(text(symbol), text(entries[i]) | style) |
|
||||
focus_management);
|
||||
}
|
||||
return vbox(std::move(elements));
|
||||
}
|
||||
|
||||
bool RadioBox::OnEvent(Event event) {
|
||||
if (!Focused())
|
||||
return false;
|
||||
|
||||
int new_focused = focused;
|
||||
if (event == Event::ArrowUp || event == Event::Character('k'))
|
||||
new_focused--;
|
||||
if (event == Event::ArrowDown || event == Event::Character('j'))
|
||||
new_focused++;
|
||||
new_focused = std::max(0, std::min(int(entries.size()) - 1, new_focused));
|
||||
if (focused != new_focused) {
|
||||
focused = new_focused;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event == Event::Character(' ') || event==Event::Return) {
|
||||
selected = focused;
|
||||
on_change();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
177
src/ftxui/component/screen_interactive.cpp
Normal file
177
src/ftxui/component/screen_interactive.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include "ftxui/component/screen_interactive.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include "ftxui/component/component.hpp"
|
||||
#include "ftxui/screen/terminal.hpp"
|
||||
#include <thread>
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
namespace {
|
||||
constexpr int ESC = 27;
|
||||
constexpr int WAT = 195;
|
||||
constexpr int WAT2 = 194;
|
||||
constexpr int WATWAIT = 91;
|
||||
|
||||
Event GetEvent() {
|
||||
int v1 = getchar();
|
||||
if (v1 == ESC) {
|
||||
int v2 = getchar();
|
||||
int v3 = getchar();
|
||||
|
||||
// if (v2 == WATWAIT) {
|
||||
// int v4 = getchar();
|
||||
// int v5 = getchar();
|
||||
// return Event{v1, v2, v3, v4, v5};
|
||||
//}
|
||||
return Event{v1, v2, v3};
|
||||
}
|
||||
|
||||
if (v1 == WAT) {
|
||||
int v2 = getchar();
|
||||
return Event{v1, v2};
|
||||
}
|
||||
|
||||
if (v1 == WAT2) {
|
||||
int v2 = getchar();
|
||||
return Event{v1, v2};
|
||||
}
|
||||
|
||||
return Event{v1};
|
||||
};
|
||||
|
||||
}; // namespace
|
||||
|
||||
ScreenInteractive::ScreenInteractive(int dimx,
|
||||
int dimy,
|
||||
Dimension dimension)
|
||||
: Screen(dimx, dimy), dimension_(dimension) {}
|
||||
ScreenInteractive::~ScreenInteractive() {}
|
||||
|
||||
// static
|
||||
ScreenInteractive ScreenInteractive::FixedSize(int dimx, int dimy) {
|
||||
return ScreenInteractive(dimx, dimy, Dimension::Fixed);
|
||||
}
|
||||
|
||||
// static
|
||||
ScreenInteractive ScreenInteractive::Fullscreen() {
|
||||
return ScreenInteractive(0, 0, Dimension::Fullscreen);
|
||||
}
|
||||
|
||||
// static
|
||||
ScreenInteractive ScreenInteractive::TerminalOutput() {
|
||||
return ScreenInteractive(0, 0, Dimension::TerminalOutput);
|
||||
}
|
||||
|
||||
// static
|
||||
ScreenInteractive ScreenInteractive::FitComponent() {
|
||||
return ScreenInteractive(0, 0, Dimension::FitComponent);
|
||||
}
|
||||
|
||||
void ScreenInteractive::PostEvent(Event event) {
|
||||
std::unique_lock<std::mutex> lock(events_queue_mutex);
|
||||
events_queue.push(event);
|
||||
events_queue_wait.notify_one();
|
||||
}
|
||||
|
||||
void ScreenInteractive::EventLoop(Component* component) {
|
||||
bool handled = 0;
|
||||
for (;;) {
|
||||
std::unique_lock<std::mutex> lock(events_queue_mutex);
|
||||
while (!events_queue.empty()) {
|
||||
component->OnEvent(events_queue.front());
|
||||
events_queue.pop();
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (handled)
|
||||
return;
|
||||
events_queue_wait.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenInteractive::Loop(Component* component) {
|
||||
//std::cout << "\033[?9h"; [> Send Mouse Row & Column on Button Press <]
|
||||
//std::cout << "\033[?1000h"; [> Send Mouse X & Y on button press and release <]
|
||||
//std::cout << std::flush;
|
||||
|
||||
// Save the old terminal configuration.
|
||||
struct termios terminal_configuration_old;
|
||||
tcgetattr(STDIN_FILENO, &terminal_configuration_old);
|
||||
|
||||
// Set the new terminal configuration
|
||||
struct termios terminal_configuration_new;
|
||||
terminal_configuration_new = terminal_configuration_old;
|
||||
|
||||
// Non canonique terminal.
|
||||
terminal_configuration_new.c_lflag &= ~ICANON;
|
||||
// Do not print after a key press.
|
||||
terminal_configuration_new.c_lflag &= ~ECHO;
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &terminal_configuration_new);
|
||||
|
||||
std::thread read_char([this]() {
|
||||
while (!quit_)
|
||||
PostEvent(GetEvent());
|
||||
});
|
||||
|
||||
std::string reset_position;
|
||||
while (!quit_) {
|
||||
reset_position = ResetPosition();
|
||||
Draw(component);
|
||||
std::cout << reset_position << ToString() << std::flush;
|
||||
Clear();
|
||||
EventLoop(component);
|
||||
}
|
||||
|
||||
// Restore the old terminal configuration.
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &terminal_configuration_old);
|
||||
|
||||
read_char.join();
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void ScreenInteractive::Draw(Component* component) {
|
||||
auto document = component->Render();
|
||||
int dimx;
|
||||
int dimy;
|
||||
switch (dimension_) {
|
||||
case Dimension::Fixed:
|
||||
dimx = dimx_;
|
||||
dimy = dimy_;
|
||||
break;
|
||||
case Dimension::TerminalOutput:
|
||||
document->ComputeRequirement();
|
||||
dimx = Terminal::Size().dimx;
|
||||
dimy = document->requirement().min.y;
|
||||
break;
|
||||
case Dimension::Fullscreen:
|
||||
dimx = Terminal::Size().dimx;
|
||||
dimy = Terminal::Size().dimy;
|
||||
break;
|
||||
case Dimension::FitComponent:
|
||||
auto terminal = Terminal::Size();
|
||||
document->ComputeRequirement();
|
||||
dimx = std::min(document->requirement().min.x, terminal.dimx);
|
||||
dimy = std::min(document->requirement().min.y, terminal.dimy);
|
||||
break;
|
||||
}
|
||||
|
||||
if (dimx != dimx_ || dimy != dimy_) {
|
||||
dimx_ = dimx;
|
||||
dimy_ = dimy;
|
||||
pixels_ = std::vector<std::vector<Pixel>>(
|
||||
dimy, std::vector<Pixel>(dimx));
|
||||
}
|
||||
|
||||
Render(*this, document.get());
|
||||
}
|
||||
|
||||
std::function<void()> ScreenInteractive::ExitLoopClosure() {
|
||||
return [this]() { quit_ = true; };
|
||||
}
|
||||
|
||||
} // namespace ftxui.
|
42
src/ftxui/component/toggle.cpp
Normal file
42
src/ftxui/component/toggle.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "ftxui/component/toggle.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Element Toggle::Render() {
|
||||
bool is_focused = Focused();
|
||||
|
||||
Elements children;
|
||||
for(size_t i = 0; i<entries.size(); ++i) {
|
||||
// Separator.
|
||||
if (i != 0)
|
||||
children.push_back(separator());
|
||||
|
||||
// Entry.
|
||||
auto style = (selected != int(i))
|
||||
? normal_style
|
||||
: is_focused ? focused_style : selected_style;
|
||||
auto focused = (selected != int(i)) ? nothing : is_focused ? focus : select;
|
||||
children.push_back(text(entries[i]) | style | focused);
|
||||
}
|
||||
return hbox(std::move(children));
|
||||
}
|
||||
|
||||
bool Toggle::OnEvent(Event event) {
|
||||
if (selected > 0 &&
|
||||
(event == Event::ArrowLeft || event == Event::Character('h'))) {
|
||||
selected--;
|
||||
on_change();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (selected < int(entries.size()) - 1 &&
|
||||
(event == Event::ArrowRight || event == Event::Character('l'))) {
|
||||
selected++;
|
||||
on_change();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
25
src/ftxui/dom/blink.cpp
Normal file
25
src/ftxui/dom/blink.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "ftxui/dom/node_decorator.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
class Blink : public NodeDecorator {
|
||||
public:
|
||||
Blink(Elements children) : NodeDecorator(std::move(children)) {}
|
||||
~Blink() override {}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
Node::Render(screen);
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
screen.PixelAt(x, y).blink = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> blink(Element child) {
|
||||
return std::make_unique<Blink>(unpack(std::move(child)));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
25
src/ftxui/dom/bold.cpp
Normal file
25
src/ftxui/dom/bold.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "ftxui/dom/node_decorator.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
class Bold : public NodeDecorator {
|
||||
public:
|
||||
Bold(Elements children) : NodeDecorator(std::move(children)) {}
|
||||
~Bold() override {}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
screen.PixelAt(x,y).bold = true;
|
||||
}
|
||||
}
|
||||
Node::Render(screen);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> bold(Element child) {
|
||||
return std::make_unique<Bold>(unpack(std::move(child)));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
118
src/ftxui/dom/border.cpp
Normal file
118
src/ftxui/dom/border.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
using namespace ftxui;
|
||||
|
||||
static wchar_t simple_border_charset[] = L"┌┐└┘─│┬┴┤├";
|
||||
|
||||
class Border : public Node {
|
||||
public:
|
||||
Border(Elements children)
|
||||
: Node(std::move(children)),
|
||||
charset(std::begin(simple_border_charset),
|
||||
std::end(simple_border_charset)) {}
|
||||
Border(Elements children, Pixel pixel)
|
||||
: Node(std::move(children)), charset_pixel(10, pixel) {}
|
||||
~Border() override {}
|
||||
|
||||
std::vector<Pixel> charset_pixel;
|
||||
std::vector<wchar_t> charset;
|
||||
|
||||
void ComputeRequirement() override {
|
||||
Node::ComputeRequirement();
|
||||
requirement_ = children[0]->requirement();
|
||||
requirement_.min.x += 2;
|
||||
requirement_.min.y += 2;
|
||||
if (children.size() == 2) {
|
||||
requirement_.min.x =
|
||||
std::max(requirement_.min.x, children[1]->requirement().min.x + 2);
|
||||
}
|
||||
requirement_.selected_box.x_min++;
|
||||
requirement_.selected_box.x_max++;
|
||||
requirement_.selected_box.y_min++;
|
||||
requirement_.selected_box.y_max++;
|
||||
}
|
||||
|
||||
void SetBox(Box box) override {
|
||||
Node::SetBox(box);
|
||||
if (children.size() == 2) {
|
||||
Box title_box;
|
||||
title_box.x_min = box.x_min + 1;
|
||||
title_box.x_max = box.x_max - 1;
|
||||
title_box.y_min = box.y_min;
|
||||
title_box.y_max = box.y_min;
|
||||
children[1]->SetBox(title_box);
|
||||
}
|
||||
box.x_min++;
|
||||
box.x_max--;
|
||||
box.y_min++;
|
||||
box.y_max--;
|
||||
children[0]->SetBox(box);
|
||||
}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
// Draw content.
|
||||
children[0]->Render(screen);
|
||||
|
||||
// Draw the border.
|
||||
if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max)
|
||||
return;
|
||||
|
||||
if (!charset.empty())
|
||||
RenderPixel(screen);
|
||||
else
|
||||
RenderChar(screen);
|
||||
}
|
||||
|
||||
void RenderPixel(Screen& screen) {
|
||||
screen.at(box_.x_min, box_.y_min) = charset[0];
|
||||
screen.at(box_.x_max, box_.y_min) = charset[1];
|
||||
screen.at(box_.x_min, box_.y_max) = charset[2];
|
||||
screen.at(box_.x_max, box_.y_max) = charset[3];
|
||||
for(float x = box_.x_min + 1; x<box_.x_max; ++x) {
|
||||
screen.at(x, box_.y_min) = charset[4];
|
||||
screen.at(x, box_.y_max) = charset[4];
|
||||
}
|
||||
for(float y = box_.y_min + 1; y<box_.y_max; ++y) {
|
||||
screen.at(box_.x_min, y) = charset[5];
|
||||
screen.at(box_.x_max,y) = charset[5];
|
||||
}
|
||||
|
||||
// Draw title.
|
||||
if (children.size() == 2)
|
||||
children[1]->Render(screen);
|
||||
}
|
||||
|
||||
void RenderChar(Screen& screen) {
|
||||
screen.PixelAt(box_.x_min, box_.y_min) = charset_pixel[0];
|
||||
screen.PixelAt(box_.x_max, box_.y_min) = charset_pixel[1];
|
||||
screen.PixelAt(box_.x_min, box_.y_max) = charset_pixel[2];
|
||||
screen.PixelAt(box_.x_max, box_.y_max) = charset_pixel[3];
|
||||
for(float x = box_.x_min + 1; x<box_.x_max; ++x) {
|
||||
screen.PixelAt(x, box_.y_min) = charset_pixel[4];
|
||||
screen.PixelAt(x, box_.y_max) = charset_pixel[4];
|
||||
}
|
||||
for(float y = box_.y_min + 1; y<box_.y_max; ++y) {
|
||||
screen.PixelAt(box_.x_min, y) = charset_pixel[5];
|
||||
screen.PixelAt(box_.x_max,y) = charset_pixel[5];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> border(Element child) {
|
||||
return std::make_unique<Border>(unpack(std::move(child)));
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> window(Element title, Element content) {
|
||||
return std::make_unique<Border>(unpack(std::move(content), std::move(title)));
|
||||
}
|
||||
|
||||
Decorator borderWith(Pixel pixel) {
|
||||
return [pixel](Element child) {
|
||||
return std::make_unique<Border>(unpack(std::move(child)), pixel);
|
||||
};
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
61
src/ftxui/dom/color.cpp
Normal file
61
src/ftxui/dom/color.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "ftxui/dom/node_decorator.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
class BgColor : public NodeDecorator {
|
||||
public:
|
||||
BgColor(Elements children, Color color)
|
||||
: NodeDecorator(std::move(children)), color_(color) {}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
screen.PixelAt(x, y).background_color = color_;
|
||||
}
|
||||
}
|
||||
NodeDecorator::Render(screen);
|
||||
}
|
||||
|
||||
Color color_;
|
||||
};
|
||||
|
||||
class FgColor : public NodeDecorator {
|
||||
public:
|
||||
FgColor(Elements children, Color color)
|
||||
: NodeDecorator(std::move(children)), color_(color) {}
|
||||
~FgColor() override {}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
screen.PixelAt(x, y).foreground_color = color_;
|
||||
}
|
||||
}
|
||||
NodeDecorator::Render(screen);
|
||||
}
|
||||
|
||||
Color color_;
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> color(Color c, Element child) {
|
||||
return std::make_unique<FgColor>(unpack(std::move(child)), c);
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> bgcolor(Color c, Element child) {
|
||||
return std::make_unique<BgColor>(unpack(std::move(child)), c);
|
||||
}
|
||||
|
||||
Decorator color(Color c) {
|
||||
return [c](Element child) {
|
||||
return color(c, std::move(child));
|
||||
};
|
||||
}
|
||||
|
||||
Decorator bgcolor(Color c) {
|
||||
return [c](Element child) {
|
||||
return bgcolor(c, std::move(child));
|
||||
};
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
22
src/ftxui/dom/composite_decorator.cpp
Normal file
22
src/ftxui/dom/composite_decorator.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
std::unique_ptr<Node> hcenter(Element child) {
|
||||
return hbox(filler(), std::move(child), filler());
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> vcenter(Element child) {
|
||||
return vbox(filler(), std::move(child), filler());
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> center(Element child) {
|
||||
return hcenter(vcenter(std::move(child)));
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> align_right(Element child) {
|
||||
return hbox(filler(), std::move(child));
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
40
src/ftxui/dom/dbox.cpp
Normal file
40
src/ftxui/dom/dbox.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
class DBox : public Node {
|
||||
public:
|
||||
DBox(Elements children) : Node(std::move(children)) {}
|
||||
~DBox() {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min.x = 0;
|
||||
requirement_.min.y = 0;
|
||||
requirement_.flex.x = 1;
|
||||
requirement_.flex.y = 0;
|
||||
for (auto& child : children) {
|
||||
child->ComputeRequirement();
|
||||
requirement_.min.x = std::max(requirement_.min.x, child->requirement().min.x);
|
||||
requirement_.min.y = std::max(requirement_.min.y, child->requirement().min.y);
|
||||
|
||||
if (requirement_.selection < child->requirement().selection) {
|
||||
requirement_.selection = child->requirement().selection;
|
||||
requirement_.selected_box = child->requirement().selected_box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetBox(Box box) override {
|
||||
Node::SetBox(box);
|
||||
|
||||
for (auto& child : children)
|
||||
child->SetBox(box);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> dbox(Elements children) {
|
||||
return std::make_unique<DBox>(std::move(children));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
27
src/ftxui/dom/dim.cpp
Normal file
27
src/ftxui/dom/dim.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "ftxui/dom/node_decorator.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
using ftxui::Screen;
|
||||
|
||||
class Dim : public NodeDecorator {
|
||||
public:
|
||||
Dim(Elements children) : NodeDecorator(std::move(children)) {}
|
||||
~Dim() override {}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
Node::Render(screen);
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
screen.PixelAt(x,y).dim = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> dim(Element child) {
|
||||
return std::make_unique<Dim>(unpack(std::move(child)));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
58
src/ftxui/dom/flex.cpp
Normal file
58
src/ftxui/dom/flex.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
class Flex : public Node {
|
||||
public:
|
||||
Flex() {}
|
||||
Flex(Element child) : Node(unpack(std::move(child))) {}
|
||||
~Flex() override {}
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min.x = 0;
|
||||
requirement_.min.y = 0;
|
||||
if (!children.empty()) {
|
||||
children[0]->ComputeRequirement();
|
||||
requirement_ = children[0]->requirement();
|
||||
}
|
||||
requirement_.flex.x = 1;
|
||||
requirement_.flex.y = 1;
|
||||
}
|
||||
|
||||
void SetBox(Box box) override {
|
||||
if (children.empty())
|
||||
return;
|
||||
children[0]->SetBox(box);
|
||||
}
|
||||
};
|
||||
|
||||
class NotFlex : public Flex {
|
||||
public:
|
||||
NotFlex() {}
|
||||
NotFlex(Element child) : Flex(std::move(child)) {}
|
||||
~NotFlex() override {}
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min.x = 0;
|
||||
requirement_.min.y = 0;
|
||||
if (!children.empty()) {
|
||||
children[0]->ComputeRequirement();
|
||||
requirement_ = children[0]->requirement();
|
||||
}
|
||||
requirement_.flex.x = 0;
|
||||
requirement_.flex.y = 0;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> filler() {
|
||||
return std::make_unique<Flex>();
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> flex(Element child) {
|
||||
return std::make_unique<Flex>(std::move(child));
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> notflex(Element child) {
|
||||
return std::make_unique<NotFlex>(std::move(child));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
121
src/ftxui/dom/frame.cpp
Normal file
121
src/ftxui/dom/frame.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/util/autoreset.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
using namespace ftxui;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class Select : public Node {
|
||||
public:
|
||||
Select(std::vector<std::unique_ptr<Node>> children)
|
||||
: Node(std::move(children)) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
Node::ComputeRequirement();
|
||||
requirement_ = children[0]->requirement();
|
||||
auto& selected_box = requirement_.selected_box;
|
||||
selected_box.x_min = 0;
|
||||
selected_box.y_min = 0;
|
||||
selected_box.x_max = requirement_.min.x;
|
||||
selected_box.y_max = requirement_.min.y;
|
||||
requirement_.selection = Requirement::SELECTED;
|
||||
};
|
||||
|
||||
void SetBox(Box box) override {
|
||||
box_ = box;
|
||||
children[0]->SetBox(box);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> select(Element child) {
|
||||
return std::make_unique<Select>(unpack(std::move(child)));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class Focus : public Select {
|
||||
public:
|
||||
Focus(std::vector<std::unique_ptr<Node>> children)
|
||||
: Select(std::move(children)) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
Select::ComputeRequirement();
|
||||
requirement_.selection = Requirement::FOCUSED;
|
||||
};
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> focus(Element child) {
|
||||
return std::make_unique<Focus>(unpack(std::move(child)));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class Frame : public Node {
|
||||
public:
|
||||
Frame(std::vector<std::unique_ptr<Node>> children)
|
||||
: Node(std::move(children)) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
Node::ComputeRequirement();
|
||||
requirement_ = children[0]->requirement();
|
||||
}
|
||||
|
||||
void SetBox(Box box) override {
|
||||
Node::SetBox(box);
|
||||
|
||||
int external_dimx = box.x_max - box.x_min;
|
||||
int external_dimy = box.y_max - box.y_min;
|
||||
|
||||
int internal_dimx = std::max(requirement_.min.x, external_dimx);
|
||||
int internal_dimy = std::max(requirement_.min.y, external_dimy);
|
||||
|
||||
auto& selected_box = requirement_.selected_box;
|
||||
int focused_dimx = selected_box.x_max - selected_box.x_min;
|
||||
int focused_dimy = selected_box.y_max - selected_box.y_min;
|
||||
int dx = selected_box.x_min - external_dimx / 2 + focused_dimx / 2;
|
||||
int dy = selected_box.y_min - external_dimy / 2 + focused_dimy / 2;
|
||||
dx = std::max(0, std::min(internal_dimx - external_dimx - 1, dx));
|
||||
dy = std::max(0, std::min(internal_dimy - external_dimy - 1, dy));
|
||||
|
||||
Box children_box = box;
|
||||
children_box.x_min = box.x_min - dx;
|
||||
children_box.y_min = box.y_min - dy;
|
||||
children_box.x_max = box.x_min + internal_dimx - dx;
|
||||
children_box.y_max = box.y_min + internal_dimy - dy;
|
||||
children[0]->SetBox(children_box);
|
||||
|
||||
// int dx = box.x_max - box.x_min;
|
||||
// int dy = box.y_max - box.y_min;
|
||||
// int cdx = std::min(children[0].requirement().min.x
|
||||
|
||||
// Box children_box;
|
||||
// children_box.x_min =
|
||||
// if (box.x_max - box.x_min >= children[0].requirement().min.x && //
|
||||
|
||||
// box.y_max - box.y_min >= children[0].requirement().min.y) {
|
||||
// children_[0]->SetBox(box);
|
||||
// dx = 0;
|
||||
// dy = 0;
|
||||
// return;
|
||||
//}
|
||||
|
||||
// Box children_box;
|
||||
// children_box.x_min = box.x_min;
|
||||
// children_box.y_min = box.x_min;
|
||||
}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
AutoReset<Box> stencil(&screen.stencil,
|
||||
Box::Intersection(box_, screen.stencil));
|
||||
children[0]->Render(screen);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> frame(Element child) {
|
||||
return std::make_unique<Frame>(unpack(std::move(child)));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
39
src/ftxui/dom/gauge.cpp
Normal file
39
src/ftxui/dom/gauge.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
using namespace ftxui;
|
||||
|
||||
static wchar_t charset[] = L" ▏▎▍▌▋▊▉█";
|
||||
|
||||
class Gauge : public Node {
|
||||
public:
|
||||
Gauge(float progress) : progress_(progress) {}
|
||||
~Gauge() override {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.flex.x = 1;
|
||||
requirement_.min.y = 1;
|
||||
}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
float y = box_.y_min;
|
||||
float limit = box_.x_min + progress_ * (box_.x_max - box_.x_min + 1);
|
||||
int limit_int = limit;
|
||||
int x = box_.x_min;
|
||||
while (x < limit_int)
|
||||
screen.at(x++, y) = charset[9];
|
||||
screen.at(x++, y) = charset[int(9*(limit-limit_int))];
|
||||
while (x <= box_.x_max)
|
||||
screen.at(x++, y) = charset[0];
|
||||
}
|
||||
private:
|
||||
float progress_;
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> gauge(float progress) {
|
||||
return std::make_unique<Gauge>(progress);
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
45
src/ftxui/dom/graph.cpp
Normal file
45
src/ftxui/dom/graph.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
const wchar_t charset[] = L" ▗▐▖▄▟▌▙█";
|
||||
|
||||
class Graph : public Node {
|
||||
public:
|
||||
Graph(GraphFunction graph_function) : graph_function_(graph_function) {}
|
||||
~Graph() override {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.flex.x = 1;
|
||||
requirement_.flex.y = 1;
|
||||
requirement_.min.x = 1;
|
||||
requirement_.min.y = 1;
|
||||
}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
int width = (box_.x_max - box_.x_min + 1) * 2;
|
||||
int height = (box_.y_max - box_.y_min + 1) * 2;
|
||||
auto data = graph_function_(width, height);
|
||||
int i = 0;
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
int height_1 = 2 * box_.y_max - data[i++];
|
||||
int height_2 = 2 * box_.y_max - data[i++];
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
int yy = 2 * y;
|
||||
int i_1 = yy < height_1 ? 0 : yy == height_1 ? 3 : 6;
|
||||
int i_2 = yy < height_2 ? 0 : yy == height_2 ? 1 : 2;
|
||||
wchar_t pix = charset[i_1 + i_2];
|
||||
screen.at(x, y) = pix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
GraphFunction graph_function_;
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> graph(GraphFunction graph_function) {
|
||||
return std::make_unique<Graph>(graph_function);
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
72
src/ftxui/dom/hbox.cpp
Normal file
72
src/ftxui/dom/hbox.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
class HBox : public Node {
|
||||
public:
|
||||
HBox(Elements children) : Node(std::move(children)) {}
|
||||
~HBox() {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min.x = 0;
|
||||
requirement_.min.y = 0;
|
||||
requirement_.flex.x = 1;
|
||||
requirement_.flex.y = 0;
|
||||
for (auto& child : children) {
|
||||
child->ComputeRequirement();
|
||||
if (requirement_.selection < child->requirement().selection) {
|
||||
requirement_.selection = child->requirement().selection;
|
||||
requirement_.selected_box = child->requirement().selected_box;
|
||||
requirement_.selected_box.x_min += requirement_.min.x;
|
||||
requirement_.selected_box.x_max += requirement_.min.x;
|
||||
}
|
||||
requirement_.min.x += child->requirement().min.x;
|
||||
requirement_.min.y =
|
||||
std::max(requirement_.min.y, child->requirement().min.y);
|
||||
}
|
||||
}
|
||||
|
||||
void SetBox(Box box) override {
|
||||
Node::SetBox(box);
|
||||
|
||||
int flex_sum = 0;
|
||||
for (auto& child : children)
|
||||
flex_sum += child->requirement().flex.x;
|
||||
|
||||
int space = box.x_max - box.x_min + 1;
|
||||
int extra_space = space - requirement_.min.x;
|
||||
|
||||
int remaining_flex = flex_sum;
|
||||
int remaining_extra_space = extra_space;
|
||||
|
||||
int x = box.x_min;
|
||||
for (auto& child : children) {
|
||||
if (x > box.x_max)
|
||||
break;
|
||||
|
||||
Box child_box = box;
|
||||
child_box.x_min = x;
|
||||
|
||||
child_box.x_max = x + child->requirement().min.x - 1;
|
||||
|
||||
if (child->requirement().flex.x && remaining_extra_space > 0) {
|
||||
int added_space = remaining_extra_space * child->requirement().flex.x /
|
||||
remaining_flex;
|
||||
remaining_extra_space -= added_space;
|
||||
remaining_flex -= child->requirement().flex.x;
|
||||
child_box.x_max += added_space;
|
||||
}
|
||||
child_box.x_max = std::min(child_box.x_max, box.x_max);
|
||||
|
||||
child->SetBox(child_box);
|
||||
x = child_box.x_max + 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> hbox(Elements children) {
|
||||
return std::make_unique<HBox>(std::move(children));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
59
src/ftxui/dom/hflow.cpp
Normal file
59
src/ftxui/dom/hflow.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
class HFlow : public Node {
|
||||
public:
|
||||
HFlow(Elements children) : Node(std::move(children)) {}
|
||||
~HFlow() {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min.x = 0;
|
||||
requirement_.min.y = 0;
|
||||
requirement_.flex.x = 1;
|
||||
requirement_.flex.y = 1;
|
||||
for(auto& child : children)
|
||||
child->ComputeRequirement();
|
||||
}
|
||||
|
||||
void SetBox(Box box) override {
|
||||
Node::SetBox(box);
|
||||
|
||||
// The position of the first component.
|
||||
int x = box.x_min;
|
||||
int y = box.y_min;
|
||||
int y_next = y; // The position of next row of elements.
|
||||
|
||||
for (auto& child : children) {
|
||||
Requirement requirement = child->requirement();
|
||||
|
||||
// Does it fit the end of the row?
|
||||
if (x + requirement.min.x > box.x_max) {
|
||||
// No? Use the next row.
|
||||
x = box.x_min;
|
||||
y = y_next;
|
||||
}
|
||||
|
||||
// Does the current row big enough to contain the element?
|
||||
if (y + requirement.min.y > box.y_max + 1)
|
||||
break; // No? Ignore the element.
|
||||
|
||||
Box children_box;
|
||||
children_box.x_min = x;
|
||||
children_box.x_max = x + requirement.min.x - 1;
|
||||
children_box.y_min = y;
|
||||
children_box.y_max = y + requirement.min.y - 1;
|
||||
child->SetBox(children_box);
|
||||
|
||||
x = x + requirement.min.x;
|
||||
y_next = std::max(y_next, y + requirement.min.y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> hflow(Elements children) {
|
||||
return std::make_unique<HFlow>(std::move(children));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
27
src/ftxui/dom/inverted.cpp
Normal file
27
src/ftxui/dom/inverted.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "ftxui/dom/node_decorator.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
using ftxui::Screen;
|
||||
|
||||
class Inverted : public NodeDecorator {
|
||||
public:
|
||||
Inverted(Elements children) : NodeDecorator(std::move(children)) {}
|
||||
~Inverted() override {}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
Node::Render(screen);
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
screen.PixelAt(x,y).inverted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> inverted(Element child) {
|
||||
return std::make_unique<Inverted>(unpack(std::move(child)));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
47
src/ftxui/dom/node.cpp
Normal file
47
src/ftxui/dom/node.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
using ftxui::Screen;
|
||||
|
||||
Node::Node() {}
|
||||
Node::Node(std::vector<std::unique_ptr<Node>> children)
|
||||
: children(std::move(children)) {}
|
||||
Node::~Node() {}
|
||||
|
||||
void Node::ComputeRequirement() {
|
||||
for(auto& child : children)
|
||||
child->ComputeRequirement();
|
||||
}
|
||||
|
||||
void Node::SetBox(Box box) {
|
||||
box_ = box;
|
||||
}
|
||||
|
||||
void Node::Render(Screen& screen) {
|
||||
for(auto& child : children)
|
||||
child->Render(screen);
|
||||
}
|
||||
|
||||
void Render(Screen& screen, Node* node) {
|
||||
// Step 1: Find what dimension this elements wants to be.
|
||||
node->ComputeRequirement();
|
||||
|
||||
Box box;
|
||||
box.x_min = 0;
|
||||
box.y_min = 0;
|
||||
box.x_max = screen.dimx() - 1;
|
||||
box.y_max = screen.dimy() - 1;
|
||||
|
||||
// Step 2: Assign a dimension to the element.
|
||||
node->SetBox(box);
|
||||
screen.stencil = box;
|
||||
|
||||
// Step 3: Draw the element.
|
||||
node->Render(screen);
|
||||
|
||||
// Step 4: Apply shaders
|
||||
screen.ApplyShader();
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
15
src/ftxui/dom/node_decorator.cpp
Normal file
15
src/ftxui/dom/node_decorator.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "ftxui/dom/node_decorator.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
void NodeDecorator::ComputeRequirement() {
|
||||
Node::ComputeRequirement();
|
||||
requirement_ = children[0]->requirement();
|
||||
}
|
||||
|
||||
void NodeDecorator::SetBox(Box box) {
|
||||
Node::SetBox(box);
|
||||
children[0]->SetBox(box);
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
20
src/ftxui/dom/node_decorator.hpp
Normal file
20
src/ftxui/dom/node_decorator.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef FTXUI_DOM_NODE_DECORATOR_H_
|
||||
#define FTXUI_DOM_NODE_DECORATOR_H_
|
||||
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
// Helper class.
|
||||
class NodeDecorator : public Node {
|
||||
public:
|
||||
NodeDecorator(Elements children) : Node(std::move(children)) {}
|
||||
~NodeDecorator() override {}
|
||||
void ComputeRequirement() override;
|
||||
void SetBox(Box box) override;
|
||||
};
|
||||
|
||||
}; // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_DOM_NODE_DECORATOR_H_ */
|
16
src/ftxui/dom/paragraph.cpp
Normal file
16
src/ftxui/dom/paragraph.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <sstream>
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Elements paragraph(std::wstring the_text) {
|
||||
Elements output;
|
||||
std::wstringstream ss(the_text);
|
||||
std::wstring word;
|
||||
while (std::getline(ss, word, L' ')) {
|
||||
output.push_back(text(word + L' '));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
56
src/ftxui/dom/separator.cpp
Normal file
56
src/ftxui/dom/separator.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
using ftxui::Screen;
|
||||
|
||||
class Separator : public Node {
|
||||
public:
|
||||
Separator() {}
|
||||
~Separator() override {}
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min.x = 1;
|
||||
requirement_.min.y = 1;
|
||||
}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
bool is_column = (box_.x_max == box_.x_min);
|
||||
bool is_line = (box_.y_min == box_.y_max);
|
||||
|
||||
wchar_t c = U'+';
|
||||
if (is_line && !is_column)
|
||||
c = U'─';
|
||||
else
|
||||
c = U'│';
|
||||
|
||||
Pixel p;
|
||||
p.character = c;
|
||||
RenderWithPixel(screen, p);
|
||||
}
|
||||
|
||||
void RenderWithPixel(Screen& screen, Pixel pixel) {
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
screen.PixelAt(x, y) = pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SeparatorWithPixel : public Separator {
|
||||
public:
|
||||
SeparatorWithPixel(Pixel p) : p(p) {}
|
||||
~SeparatorWithPixel() override {}
|
||||
void Render(Screen& screen) override { RenderWithPixel(screen, p); }
|
||||
Pixel p;
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> separator() {
|
||||
return std::make_unique<Separator>();
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> separator(Pixel pixel) {
|
||||
return std::make_unique<SeparatorWithPixel>(pixel);
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
77
src/ftxui/dom/size.cpp
Normal file
77
src/ftxui/dom/size.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
#include "ftxui/dom/node.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
class Size : public Node {
|
||||
public:
|
||||
Size(Element child, Direction direction, Constraint constraint, size_t value)
|
||||
: Node(unpack(std::move(child))),
|
||||
direction_(direction),
|
||||
constraint_(constraint),
|
||||
value_(value) {}
|
||||
|
||||
~Size() override {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
Node::ComputeRequirement();
|
||||
requirement_ = children[0]->requirement();
|
||||
|
||||
auto& value = direction_ == WIDTH ? requirement_.min.x : requirement_.min.y;
|
||||
|
||||
switch (constraint_) {
|
||||
case LESS_THAN:
|
||||
value = std::min(value, value_);
|
||||
break;
|
||||
case EQUAL:
|
||||
value = value_;
|
||||
break;
|
||||
case GREATER_THAN:
|
||||
value = std::max(value, value_);
|
||||
break;
|
||||
}
|
||||
|
||||
if (direction_ == WIDTH)
|
||||
requirement_.flex.x = 0;
|
||||
else
|
||||
requirement_.flex.y = 0;
|
||||
}
|
||||
|
||||
void SetBox(Box box) override {
|
||||
Node::SetBox(box);
|
||||
|
||||
if (direction_ == WIDTH) {
|
||||
switch(constraint_) {
|
||||
case LESS_THAN:
|
||||
case EQUAL:
|
||||
box.x_max = std::min(box.x_min + value_ + 1, box.x_max);
|
||||
break;
|
||||
case GREATER_THAN:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(constraint_) {
|
||||
case LESS_THAN:
|
||||
case EQUAL:
|
||||
box.y_max = std::min(box.y_min + value_ + 1, box.y_max);
|
||||
break;
|
||||
case GREATER_THAN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
children[0]->SetBox(box);
|
||||
}
|
||||
|
||||
private:
|
||||
Direction direction_;
|
||||
Constraint constraint_;
|
||||
int value_;
|
||||
};
|
||||
|
||||
Decorator size(Direction direction, Constraint constraint, int value) {
|
||||
return [=](Element e) {
|
||||
return std::make_unique<Size>(std::move(e), direction, constraint, value);
|
||||
};
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
279
src/ftxui/dom/spinner.cpp
Normal file
279
src/ftxui/dom/spinner.cpp
Normal file
@@ -0,0 +1,279 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
using namespace ftxui;
|
||||
|
||||
static const std::vector<std::vector<std::vector<std::wstring>>> elements = {
|
||||
{
|
||||
{L"Replaced by the gauge"},
|
||||
},
|
||||
{
|
||||
{L". "},
|
||||
{L".. "},
|
||||
{L"..."},
|
||||
},
|
||||
{
|
||||
{L"|"},
|
||||
{L"/"},
|
||||
{L"-"},
|
||||
{L"\\"},
|
||||
},
|
||||
{
|
||||
{L"+"},
|
||||
{L"x"},
|
||||
},
|
||||
{
|
||||
{L"| "},
|
||||
{L"|| "},
|
||||
{L"|||"},
|
||||
},
|
||||
{
|
||||
{L"←"},
|
||||
{L"↖"},
|
||||
{L"↑"},
|
||||
{L"↗"},
|
||||
{L"→"},
|
||||
{L"↘"},
|
||||
{L"↓"},
|
||||
{L"↙"},
|
||||
},
|
||||
{
|
||||
{L"▁"},
|
||||
{L"▂"},
|
||||
{L"▃"},
|
||||
{L"▄"},
|
||||
{L"▅"},
|
||||
{L"▆"},
|
||||
{L"▇"},
|
||||
{L"█"},
|
||||
{L"▇"},
|
||||
{L"▆"},
|
||||
{L"▅"},
|
||||
{L"▄"},
|
||||
{L"▃"},
|
||||
{L"▁"},
|
||||
},
|
||||
{
|
||||
{L"▉"},
|
||||
{L"▊"},
|
||||
{L"▋"},
|
||||
{L"▌"},
|
||||
{L"▍"},
|
||||
{L"▎"},
|
||||
{L"▏"},
|
||||
{L"▎"},
|
||||
{L"▍"},
|
||||
{L"▌"},
|
||||
{L"▋"},
|
||||
{L"▊"},
|
||||
},
|
||||
{
|
||||
{L"▖"},
|
||||
{L"▘"},
|
||||
{L"▝"},
|
||||
{L"▗"},
|
||||
},
|
||||
{
|
||||
{L"◢"},
|
||||
{L"◣"},
|
||||
{L"◤"},
|
||||
{L"◥"},
|
||||
},
|
||||
{
|
||||
{L"◰"},
|
||||
{L"◳"},
|
||||
{L"◲"},
|
||||
{L"◱"},
|
||||
},
|
||||
{
|
||||
{L"◴"},
|
||||
{L"◷"},
|
||||
{L"◶"},
|
||||
{L"◵"},
|
||||
},
|
||||
{
|
||||
{L"◐"},
|
||||
{L"◓"},
|
||||
{L"◑"},
|
||||
{L"◒"},
|
||||
},
|
||||
{
|
||||
{L"◡"},
|
||||
{L"⊙"},
|
||||
{L"◠"},
|
||||
},
|
||||
{
|
||||
{L"⠁"},
|
||||
{L"⠂"},
|
||||
{L"⠄"},
|
||||
{L"⡀"},
|
||||
{L"⢀"},
|
||||
{L"⠠"},
|
||||
{L"⠐"},
|
||||
{L"⠈"},
|
||||
},
|
||||
{
|
||||
{L"⠋"},
|
||||
{L"⠙"},
|
||||
{L"⠹"},
|
||||
{L"⠸"},
|
||||
{L"⠼"},
|
||||
{L"⠴"},
|
||||
{L"⠦"},
|
||||
{L"⠧"},
|
||||
{L"⠇"},
|
||||
{L"⠏"},
|
||||
},
|
||||
{
|
||||
{L"(*----------)"},
|
||||
{L"(-*---------)"},
|
||||
{L"(--*--------)"},
|
||||
{L"(---*-------)"},
|
||||
{L"(----*------)"},
|
||||
{L"(-----*-----)"},
|
||||
{L"(------*----)"},
|
||||
{L"(-------*---)"},
|
||||
{L"(--------*--)"},
|
||||
{L"(---------*-)"},
|
||||
{L"(----------*)"},
|
||||
{L"(---------*-)"},
|
||||
{L"(--------*--)"},
|
||||
{L"(-------*---)"},
|
||||
{L"(------*----)"},
|
||||
{L"(-----*-----)"},
|
||||
{L"(----*------)"},
|
||||
{L"(---*-------)"},
|
||||
{L"(--*--------)"},
|
||||
{L"(-*---------)"},
|
||||
},
|
||||
{
|
||||
{L"[ ]"},
|
||||
{L"[= ]"},
|
||||
{L"[== ]"},
|
||||
{L"[=== ]"},
|
||||
{L"[==== ]"},
|
||||
{L"[===== ]"},
|
||||
{L"[======]"},
|
||||
{L"[===== ]"},
|
||||
{L"[==== ]"},
|
||||
{L"[=== ]"},
|
||||
{L"[== ]"},
|
||||
{L"[= ]"},
|
||||
},
|
||||
{
|
||||
{L"[ ]"},
|
||||
{L"[= ]"},
|
||||
{L"[== ]"},
|
||||
{L"[=== ]"},
|
||||
{L"[==== ]"},
|
||||
{L"[===== ]"},
|
||||
{L"[======]"},
|
||||
{L"[ =====]"},
|
||||
{L"[ ====]"},
|
||||
{L"[ ===]"},
|
||||
{L"[ ==]"},
|
||||
{L"[ =]"},
|
||||
},
|
||||
{
|
||||
{L"[== ]"},
|
||||
{L"[== ]"},
|
||||
{L"[== ]"},
|
||||
{L"[== ]"},
|
||||
{L"[== ]"},
|
||||
{L" [== ]"},
|
||||
{L"[ == ]"},
|
||||
{L"[ == ]"},
|
||||
{L"[ ==]"},
|
||||
{L"[ ==]"},
|
||||
{L"[ ==]"},
|
||||
{L"[ ==]"},
|
||||
{L"[ ==]"},
|
||||
{L"[ ==] "},
|
||||
{L"[ == ]"},
|
||||
{L"[ == ]"},
|
||||
},
|
||||
{
|
||||
{
|
||||
L" ─╮",
|
||||
L" │",
|
||||
L" ",
|
||||
},
|
||||
{
|
||||
L" ╮",
|
||||
L" │",
|
||||
L" ╯",
|
||||
},
|
||||
{
|
||||
L" ",
|
||||
L" │",
|
||||
L" ─╯",
|
||||
},
|
||||
{
|
||||
L" ",
|
||||
L" ",
|
||||
L"╰─╯",
|
||||
},
|
||||
{
|
||||
L" ",
|
||||
L"│ ",
|
||||
L"╰─ ",
|
||||
},
|
||||
{
|
||||
L"╭ ",
|
||||
L"│ ",
|
||||
L"╰ ",
|
||||
},
|
||||
{
|
||||
L"╭─ ",
|
||||
L"│ ",
|
||||
L" ",
|
||||
},
|
||||
{
|
||||
L"╭─╮",
|
||||
L" ",
|
||||
L" ",
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
L" /\\O ",
|
||||
L" /\\/ ",
|
||||
L" /\\ ",
|
||||
L" / \\ ",
|
||||
L"LOL LOL",
|
||||
},
|
||||
{
|
||||
L" _O ",
|
||||
L" //|_ ",
|
||||
L" | ",
|
||||
L" /| ",
|
||||
L" LLOL ",
|
||||
},
|
||||
{
|
||||
L" O ",
|
||||
L" /_ ",
|
||||
L" |\\ ",
|
||||
L" / | ",
|
||||
L" LOLLOL ",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> spinner(int c, size_t index) {
|
||||
if (c == 0) {
|
||||
index %= 40;
|
||||
if (index > 20)
|
||||
index = 40-index;
|
||||
return gauge(index * 0.05);
|
||||
}
|
||||
c %= elements.size();
|
||||
index %= elements[c].size();
|
||||
std::vector<Element> lines;
|
||||
for(const auto& it : elements[c][index])
|
||||
lines.push_back(text(it));
|
||||
return vbox(std::move(lines));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
37
src/ftxui/dom/text.cpp
Normal file
37
src/ftxui/dom/text.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
using ftxui::Screen;
|
||||
|
||||
class Text : public Node {
|
||||
public:
|
||||
Text(std::wstring text) : Node(), text_(text) {}
|
||||
~Text() {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min.x = text_.size();
|
||||
requirement_.min.y = 1;
|
||||
}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
int x = box_.x_min;
|
||||
int y = box_.y_min;
|
||||
if (y > box_.y_max)
|
||||
return;
|
||||
for (wchar_t c : text_) {
|
||||
if (x > box_.x_max)
|
||||
return;
|
||||
screen.at(x++, y) = c;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::wstring text_;
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> text(std::wstring text) {
|
||||
return std::make_unique<Text>(text);
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
27
src/ftxui/dom/underlined.cpp
Normal file
27
src/ftxui/dom/underlined.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "ftxui/dom/node_decorator.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
using ftxui::Screen;
|
||||
|
||||
class Underlined : public NodeDecorator {
|
||||
public:
|
||||
Underlined(Elements children) : NodeDecorator(std::move(children)) {}
|
||||
~Underlined() override {}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
Node::Render(screen);
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
screen.PixelAt(x, y).underlined = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> underlined(Element child) {
|
||||
return std::make_unique<Underlined>(unpack(std::move(child)));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
33
src/ftxui/dom/util.cpp
Normal file
33
src/ftxui/dom/util.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Element nothing(Element element) {
|
||||
return element;
|
||||
}
|
||||
|
||||
Decorator compose(Decorator a, Decorator b) {
|
||||
return [
|
||||
a = std::move(a),
|
||||
b = std::move(b)
|
||||
](Element element) {
|
||||
return b(a(std::move(element)));
|
||||
};
|
||||
}
|
||||
|
||||
Decorator operator|(Decorator a, Decorator b) {
|
||||
return compose(a, b);
|
||||
}
|
||||
|
||||
Elements operator|(Elements es, Decorator d) {
|
||||
Elements output;
|
||||
for (auto& it : es)
|
||||
output.push_back(std::move(it) | d);
|
||||
return output;
|
||||
}
|
||||
|
||||
Element operator|(Element e, Decorator d) {
|
||||
return d(std::move(e));
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
72
src/ftxui/dom/vbox.cpp
Normal file
72
src/ftxui/dom/vbox.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
class VBox : public Node {
|
||||
public:
|
||||
VBox(Elements children) : Node(std::move(children)) {}
|
||||
~VBox() {}
|
||||
|
||||
void ComputeRequirement() {
|
||||
requirement_.min.x = 0;
|
||||
requirement_.min.y = 0;
|
||||
requirement_.flex.x = 0;
|
||||
requirement_.flex.y = 1;
|
||||
for (auto& child : children) {
|
||||
child->ComputeRequirement();
|
||||
if (requirement_.selection < child->requirement().selection) {
|
||||
requirement_.selection = child->requirement().selection;
|
||||
requirement_.selected_box = child->requirement().selected_box;
|
||||
requirement_.selected_box.y_min += requirement_.min.y;
|
||||
requirement_.selected_box.y_max += requirement_.min.y;
|
||||
}
|
||||
requirement_.min.y += child->requirement().min.y;
|
||||
requirement_.min.x =
|
||||
std::max(requirement_.min.x, child->requirement().min.x);
|
||||
}
|
||||
}
|
||||
|
||||
void SetBox(Box box) {
|
||||
Node::SetBox(box);
|
||||
|
||||
int flex_sum = 0;
|
||||
for (auto& child : children)
|
||||
flex_sum += child->requirement().flex.y;
|
||||
|
||||
int space = box.y_max - box.y_min + 1;
|
||||
int extra_space = space - requirement_.min.y;
|
||||
|
||||
int remaining_flex = flex_sum;
|
||||
int remaining_extra_space = extra_space;
|
||||
|
||||
int y = box.y_min;
|
||||
for (auto& child : children) {
|
||||
if (y > box.y_max)
|
||||
break;
|
||||
|
||||
Box child_box = box;
|
||||
child_box.y_min = y;
|
||||
|
||||
child_box.y_max = y + child->requirement().min.y - 1;
|
||||
|
||||
if (child->requirement().flex.y && remaining_extra_space > 0) {
|
||||
int added_space = remaining_extra_space * child->requirement().flex.y /
|
||||
remaining_flex;
|
||||
remaining_extra_space -= added_space;
|
||||
remaining_flex -= child->requirement().flex.y;
|
||||
child_box.y_max += added_space;
|
||||
}
|
||||
child_box.y_max = std::min(child_box.y_max, box.y_max);
|
||||
|
||||
child->SetBox(child_box);
|
||||
y = child_box.y_max + 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Node> vbox(Elements children) {
|
||||
return std::make_unique<VBox>(std::move(children));
|
||||
}
|
||||
|
||||
}; // namespace ftxui
|
14
src/ftxui/screen/box.cpp
Normal file
14
src/ftxui/screen/box.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "ftxui/screen/box.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace ftxui {
|
||||
// static
|
||||
Box Box::Intersection(Box a, Box b) {
|
||||
return Box{
|
||||
std::max(a.x_min, b.x_min),
|
||||
std::min(a.x_max, b.x_max),
|
||||
std::max(a.y_min, b.y_min),
|
||||
std::min(a.y_max, b.y_max),
|
||||
};
|
||||
}
|
||||
} // namespace ftxui
|
172
src/ftxui/screen/screen.cpp
Normal file
172
src/ftxui/screen/screen.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#include "ftxui/screen/screen.hpp"
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/screen/string.hpp"
|
||||
#include "ftxui/screen/terminal.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
namespace {
|
||||
static const wchar_t* BOLD_SET = L"\e[1m";
|
||||
static const wchar_t* BOLD_RESET = L"\e[22m"; // Can't use 21 here.
|
||||
|
||||
static const wchar_t* DIM_SET = L"\e[2m";
|
||||
static const wchar_t* DIM_RESET = L"\e[22m";
|
||||
|
||||
static const wchar_t* UNDERLINED_SET = L"\e[4m";
|
||||
static const wchar_t* UNDERLINED_RESET = L"\e[24m";
|
||||
|
||||
static const wchar_t* BLINK_SET = L"\e[5m";
|
||||
static const wchar_t* BLINK_RESET = L"\e[25m";
|
||||
|
||||
static const wchar_t* INVERTED_SET = L"\e[7m";
|
||||
static const wchar_t* INVERTED_RESET = L"\e[27m";
|
||||
|
||||
static const char* MOVE_LEFT = "\r";
|
||||
static const char* MOVE_UP = "\e[1A";
|
||||
static const char* CLEAR_LINE = "\e[2K";
|
||||
|
||||
bool In(const Box& stencil, int x, int y) {
|
||||
return stencil.x_min <= x && x <= stencil.x_max && //
|
||||
stencil.y_min <= y && y <= stencil.y_max;
|
||||
}
|
||||
|
||||
Pixel dev_null_pixel;
|
||||
|
||||
} // namespace
|
||||
|
||||
Dimension Dimension::Fixed(int v) {
|
||||
return Dimension{v, v};
|
||||
}
|
||||
|
||||
Dimension Dimension::Fit(std::unique_ptr<Node>& e) {
|
||||
e->ComputeRequirement();
|
||||
Terminal::Dimensions size = Terminal::Size();
|
||||
return Dimension{std::min(e->requirement().min.x, size.dimx),
|
||||
std::min(e->requirement().min.y, size.dimy)};
|
||||
}
|
||||
|
||||
Dimension Dimension::Full() {
|
||||
Terminal::Dimensions size = Terminal::Size();
|
||||
return Dimension{size.dimx, size.dimy};
|
||||
}
|
||||
|
||||
// static
|
||||
Screen Screen::Create(Dimension width, Dimension height) {
|
||||
return Screen(width.dimx, height.dimy);
|
||||
}
|
||||
|
||||
// static
|
||||
Screen Screen::Create(Dimension dimension) {
|
||||
return Screen(dimension.dimx, dimension.dimy);
|
||||
}
|
||||
|
||||
Screen::Screen(int dimx, int dimy)
|
||||
: stencil({0, dimx - 1, 0, dimy - 1}),
|
||||
dimx_(dimx),
|
||||
dimy_(dimy),
|
||||
pixels_(dimy, std::vector<Pixel>(dimx)) {}
|
||||
|
||||
void UpdatePixelStyle(std::wstringstream& ss, Pixel& previous, Pixel& next) {
|
||||
if (next.bold != previous.bold)
|
||||
ss << (next.bold ? BOLD_SET : BOLD_RESET);
|
||||
|
||||
if (next.dim != previous.dim)
|
||||
ss << (next.dim ? DIM_SET : DIM_RESET);
|
||||
|
||||
if (next.underlined != previous.underlined)
|
||||
ss << (next.underlined ? UNDERLINED_SET : UNDERLINED_RESET);
|
||||
|
||||
if (next.blink != previous.blink)
|
||||
ss << (next.blink ? BLINK_SET : BLINK_RESET);
|
||||
|
||||
if (next.inverted != previous.inverted)
|
||||
ss << (next.inverted ? INVERTED_SET : INVERTED_RESET);
|
||||
|
||||
if (next.foreground_color != previous.foreground_color ||
|
||||
next.background_color != previous.background_color) {
|
||||
ss << L"\e[" + to_wstring(std::to_string((uint8_t)next.foreground_color)) + L"m";
|
||||
ss << L"\e[" + to_wstring(std::to_string(10 + (uint8_t)next.background_color)) + L"m";
|
||||
}
|
||||
|
||||
previous = next;
|
||||
}
|
||||
|
||||
std::string Screen::ToString() {
|
||||
std::wstringstream ss;
|
||||
|
||||
Pixel previous_pixel;
|
||||
|
||||
for (int y = 0; y < dimy_; ++y) {
|
||||
if (y != 0)
|
||||
ss << '\n';
|
||||
for (int x = 0; x < dimx_; ++x) {
|
||||
UpdatePixelStyle(ss, previous_pixel, pixels_[y][x]);
|
||||
ss << pixels_[y][x].character;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Pixel final_pixel;
|
||||
UpdatePixelStyle(ss, previous_pixel, final_pixel);
|
||||
|
||||
return to_string(ss.str());
|
||||
}
|
||||
|
||||
wchar_t& Screen::at(int x, int y) {
|
||||
return PixelAt(x,y).character;
|
||||
}
|
||||
|
||||
Pixel& Screen::PixelAt(int x, int y) {
|
||||
return In(stencil, x, y) ? pixels_[y][x] : dev_null_pixel;
|
||||
}
|
||||
|
||||
std::string Screen::ResetPosition() {
|
||||
std::stringstream ss;
|
||||
ss << MOVE_LEFT << CLEAR_LINE;
|
||||
for (int y = 1; y < dimy_; ++y) {
|
||||
ss << MOVE_UP << CLEAR_LINE;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void Screen::Clear() {
|
||||
pixels_ = std::vector<std::vector<Pixel>>(dimy_,
|
||||
std::vector<Pixel>(dimx_, Pixel()));
|
||||
}
|
||||
|
||||
void Screen::ApplyShader() {
|
||||
|
||||
// Merge box characters togethers.
|
||||
for(int y = 1; y<dimy_; ++y) {
|
||||
for(int x = 1; x<dimx_; ++x) {
|
||||
wchar_t& left = at(x - 1, y);
|
||||
wchar_t& top = at(x, y - 1);
|
||||
wchar_t& cur = at(x, y);
|
||||
|
||||
// Left vs current
|
||||
if (cur== U'│' && left == U'─')
|
||||
cur= U'┤';
|
||||
if (cur== U'─' && left == U'│')
|
||||
left = U'├';
|
||||
if (cur== U'├' && left == U'─')
|
||||
cur= U'┼';
|
||||
if (cur== U'─' && left == U'┤')
|
||||
left = U'┼';
|
||||
|
||||
// Top vs current
|
||||
if (cur== U'─' && top == U'│')
|
||||
cur= U'┴';
|
||||
if (cur== U'│' && top == U'─')
|
||||
top = U'┬';
|
||||
if (cur== U'┬' && top == U'│')
|
||||
cur= U'┼';
|
||||
if (cur== U'│' && top == U'┴')
|
||||
top = U'┼';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}; // namespace ftxui
|
14
src/ftxui/screen/string.cpp
Normal file
14
src/ftxui/screen/string.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "ftxui/screen/string.hpp"
|
||||
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
|
||||
std::string to_string(const std::wstring& s) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.to_bytes(s);
|
||||
}
|
||||
|
||||
std::wstring to_wstring(const std::string& s) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.from_bytes(s);
|
||||
}
|
21
src/ftxui/screen/terminal.cpp
Normal file
21
src/ftxui/screen/terminal.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <iostream>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ftxui/screen/terminal.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Terminal::Dimensions Terminal::Size() {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
return Dimensions{80,43};
|
||||
#else
|
||||
winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
std::cerr << w.ws_col << "," << w.ws_row << std::endl;
|
||||
return Dimensions{w.ws_col, w.ws_row};
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
18
src/ftxui/screen/terminal.hpp
Normal file
18
src/ftxui/screen/terminal.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef FTXUI_CORE_TERMINAL_HPP
|
||||
#define FTXUI_CORE_TERMINAL_HPP
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
class Terminal {
|
||||
public:
|
||||
struct Dimensions {
|
||||
int dimx;
|
||||
int dimy;
|
||||
};
|
||||
|
||||
static Dimensions Size();
|
||||
};
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_CORE_TERMINAL_HPP */
|
Reference in New Issue
Block a user