Add support for nxxm.

[nxxm](https://nxxm.github.io)
This commit is contained in:
ArthurSonzogni
2019-02-02 01:59:48 +01:00
parent 2eddd0fa17
commit ef0de8d873
72 changed files with 309 additions and 165 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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.

View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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

View 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

View 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_ */

View 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

View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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

View 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);
}

View 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

View 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 */