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