Add "frame" : scrollable area.

This commit is contained in:
Arthur Sonzogni 2019-01-19 22:06:05 +01:00
parent cb4df0b56f
commit fddcbdea65
50 changed files with 543 additions and 243 deletions

View File

@ -14,11 +14,11 @@ A C++ library for making text based user interface.
~~~cpp ~~~cpp
vbox( vbox(
hbox( hbox(
text(L"left") | frame, text(L"left") | border,
text(L"middle") | frame | flex, text(L"middle") | border | flex,
text(L"right") | frame text(L"right") | border
), ),
gauge(0.5) | frame gauge(0.5) | border
) )
~~~ ~~~

View File

@ -5,12 +5,14 @@ function(example name)
endfunction(example) endfunction(example)
example(checkbox) example(checkbox)
example(checkbox_in_frame)
example(gallery) example(gallery)
example(input) example(input)
example(menu) example(menu)
example(menu2) example(menu2)
example(menu_style) example(menu_style)
example(radiobox) example(radiobox)
example(radiobox_in_frame)
example(tab_horizontal) example(tab_horizontal)
example(tab_vertical) example(tab_vertical)
example(toggle) example(toggle)

View File

@ -22,19 +22,10 @@ class MyComponent : public Component {
box_3_.label = L"Use WebAssembly"; box_3_.label = L"Use WebAssembly";
box_3_.state = true; box_3_.state = true;
} }
Element Render() {
return
window(text(L" Checkbox "),
hbox(
container_.Render()
)
);
}
}; };
int main(int argc, const char *argv[]) { int main(int argc, const char *argv[]) {
auto screen = ScreenInteractive::FixedSize(30,5); auto screen = ScreenInteractive::TerminalOutput();
MyComponent component; MyComponent component;
screen.Loop(&component); screen.Loop(&component);
return 0; return 0;

View File

@ -0,0 +1,42 @@
#include "ftxui/component/checkbox.hpp"
#include "ftxui/component/container.hpp"
#include "ftxui/component/input.hpp"
#include "ftxui/component/menu.hpp"
#include "ftxui/component/checkbox.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/component/toggle.hpp"
#include "ftxui/screen/string.hpp"
using namespace ftxui;
class MyComponent : public Component {
public:
MyComponent() {
Add(&container);
checkbox.resize(30);
for(int i = 0; i<checkbox.size(); ++i) {
checkbox[i].label = (L"CheckBox " + to_wstring(i));
container.Add(&checkbox[i]);
}
}
Element Render() override {
Elements content;
for(auto& it : checkbox) {
content.push_back(it.Render());
}
return vbox(std::move(content)) | frame | size(20, 10) | border;
}
private:
std::vector<CheckBox> checkbox;
Container container = Container::Vertical();
};
int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::FitComponent();
MyComponent component;
screen.Loop(&component);
return 0;
}

View File

@ -69,7 +69,7 @@ class MyComponent : public Component {
Render(L"radiobox", radiobox), Render(L"radiobox", radiobox),
separator(), separator(),
Render(L"input", input) Render(L"input", input)
) | frame; ) | border | frame | size(10,10) | border;
} }
}; };

View File

@ -30,7 +30,7 @@ class MyComponent : public Component {
Element Render() override { Element Render() override {
return return
frame( border(
vbox( vbox(
hbox(text(L" input_1 : "), input_1.Render()), hbox(text(L" input_1 : "), input_1.Render()),
hbox(text(L" input_2 : "), input_2.Render()), hbox(text(L" input_2 : "), input_2.Render()),

View File

@ -34,7 +34,7 @@ class MyComponent : public Component {
Element Render() override { Element Render() override {
int sum = left_menu.selected * 10 + right_menu.selected; int sum = left_menu.selected * 10 + right_menu.selected;
return return
frame( border(
vbox( vbox(
// -------- Top panel -------------- // -------- Top panel --------------
hbox( hbox(

View File

@ -26,21 +26,21 @@ class MyComponent : public Component {
} }
menu_2.selected_style = color(Color::Blue); menu_2.selected_style = color(Color::Blue);
menu_2.active_style = bold | color(Color::Blue); menu_2.focused_style = bold | color(Color::Blue);
menu_3.selected_style = color(Color::Blue); menu_3.selected_style = color(Color::Blue);
menu_3.active_style = bgcolor(Color::Blue); menu_3.focused_style = bgcolor(Color::Blue);
menu_4.selected_style = bgcolor(Color::Blue); menu_4.selected_style = bgcolor(Color::Blue);
menu_4.active_style = bgcolor(Color::BlueLight); menu_4.focused_style = bgcolor(Color::BlueLight);
menu_5.normal_style = bgcolor(Color::Blue); menu_5.normal_style = bgcolor(Color::Blue);
menu_5.selected_style = bgcolor(Color::Yellow); menu_5.selected_style = bgcolor(Color::Yellow);
menu_5.active_style = bgcolor(Color::Red); menu_5.focused_style = bgcolor(Color::Red);
menu_6.normal_style = dim | color(Color::Blue); menu_6.normal_style = dim | color(Color::Blue);
menu_6.selected_style = color(Color::Blue); menu_6.selected_style = color(Color::Blue);
menu_6.active_style = bold | color(Color::Blue); menu_6.focused_style = bold | color(Color::Blue);
} }
std::function<void()> on_enter = [](){}; std::function<void()> on_enter = [](){};
@ -62,7 +62,7 @@ class MyComponent : public Component {
menu_4.Render() | flex, separator(), menu_4.Render() | flex, separator(),
menu_5.Render() | flex, separator(), menu_5.Render() | flex, separator(),
menu_6.Render() | flex menu_6.Render() | flex
) | frame; ) | border;
} }
}; };

View File

@ -0,0 +1,34 @@
#include "ftxui/component/checkbox.hpp"
#include "ftxui/component/container.hpp"
#include "ftxui/component/input.hpp"
#include "ftxui/component/menu.hpp"
#include "ftxui/component/radiobox.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/component/toggle.hpp"
#include "ftxui/screen/string.hpp"
using namespace ftxui;
class MyComponent : public Component {
RadioBox radiobox;
public:
MyComponent() {
for(int i = 0; i<30; ++i) {
radiobox.entries.push_back(L"RadioBox " + to_wstring(i));
}
Add(&radiobox);
}
Element Render() override {
return radiobox.Render() | frame | size(20,10) | border;
}
};
int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::FitComponent();
MyComponent component;
screen.Loop(&component);
return 0;
}

View File

@ -50,7 +50,7 @@ class MyComponent : public Component {
toggle_.Render(), toggle_.Render(),
separator(), separator(),
tab_container_.Render() tab_container_.Render()
) | frame; ) | border;
} }
private: private:

View File

@ -4,7 +4,7 @@ function(example name)
endfunction(example) endfunction(example)
example(dbox) example(dbox)
example(frame) example(border)
example(gauge) example(gauge)
example(package_manager) example(package_manager)
example(separator) example(separator)

View File

@ -15,13 +15,11 @@ int main(int argc, const char *argv[])
text(L"Line 1"), text(L"Line 1"),
text(L"Line 2"), text(L"Line 2"),
text(L"Line 3"), text(L"Line 3"),
frame( vbox(
vbox( text(L"Line 4"),
text(L"Line 4"), text(L"Line 5"),
text(L"Line 5"), text(L"Line 6")
text(L"Line 6") ) | border,
)
),
hbox( hbox(
window(text(L"frame 2"), window(text(L"frame 2"),
vbox( vbox(

View File

@ -7,20 +7,14 @@ int main(int argc, const char *argv[])
using namespace ftxui; using namespace ftxui;
auto document = auto document =
dbox( dbox(
frame( vbox(
vbox( text(L"line_1"),
text(L"line_1"), text(L"line_2"),
text(L"line_2"), text(L"line_3"),
text(L"line_3"), text(L"line_4"),
text(L"line_4"), text(L"line_5")
text(L"line_5") ) | border,
) text(L"overlay") | border | center
),
center(
frame(
text(L"overlay")
)
)
); );
auto screen = Screen::TerminalOutput(document); auto screen = Screen::TerminalOutput(document);
Render(screen, document.get()); Render(screen, document.get());

View File

@ -24,7 +24,7 @@ int main(int argc, const char *argv[])
) )
); );
} }
auto document = hbox(vbox(std::move(entries)) | frame, filler()); auto document = hbox(vbox(std::move(entries)) | border, filler());
auto screen = Screen::TerminalOutput(document); auto screen = Screen::TerminalOutput(document);
Render(screen, document.get()); Render(screen, document.get());
std::cout << reset_position << screen.ToString() << std::flush; std::cout << reset_position << screen.ToString() << std::flush;

View File

@ -1,14 +1,16 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
add_library(screen add_library(screen
src/ftxui/screen/box.cpp
src/ftxui/screen/screen.cpp src/ftxui/screen/screen.cpp
src/ftxui/screen/terminal.cpp
src/ftxui/screen/string.cpp src/ftxui/screen/string.cpp
src/ftxui/screen/terminal.cpp
) )
add_library(dom add_library(dom
src/ftxui/dom/blink.cpp src/ftxui/dom/blink.cpp
src/ftxui/dom/bold.cpp src/ftxui/dom/bold.cpp
src/ftxui/dom/border.cpp
src/ftxui/dom/color.cpp src/ftxui/dom/color.cpp
src/ftxui/dom/composite_decorator.cpp src/ftxui/dom/composite_decorator.cpp
src/ftxui/dom/dbox.cpp src/ftxui/dom/dbox.cpp

View File

@ -13,7 +13,7 @@ class Container : public Component {
public: public:
static Container Vertical(); static Container Vertical();
static Container Horizontal(); static Container Horizontal();
static Container Tab(size_t* selector); static Container Tab(int* selector);
~Container() override = default; ~Container() override = default;
@ -36,8 +36,8 @@ class Container : public Component {
Element TabRender(); Element TabRender();
RenderHandler render_handler_; RenderHandler render_handler_;
size_t selected_ = 0; int selected_ = 0;
size_t* selector_ = &selected_; int* selector_ = &selected_;
}; };
} // namespace ftxui } // namespace ftxui

View File

@ -15,9 +15,9 @@ class Menu : public Component {
// State. // State.
std::vector<std::wstring> entries = {}; std::vector<std::wstring> entries = {};
size_t selected = 0; int selected = 0;
Decorator active_style = inverted; Decorator focused_style = inverted;
Decorator selected_style = bold; Decorator selected_style = bold;
Decorator normal_style = nothing; Decorator normal_style = nothing;

View File

@ -13,9 +13,13 @@ class Toggle : public Component {
~Toggle() override = default; ~Toggle() override = default;
// State. // State.
size_t selected = 0; int selected = 0;
std::vector<std::wstring> entries = {L"On", L"Off"}; std::vector<std::wstring> entries = {L"On", L"Off"};
Decorator focused_style = inverted;
Decorator selected_style = bold;
Decorator normal_style = dim;
// Callback. // Callback.
std::function<void()> on_change = [](){}; std::function<void()> on_change = [](){};

View File

@ -1,15 +0,0 @@
#ifndef FTXUI_DOM_BOX_HPP
#define FTXUI_DOM_BOX_HPP
namespace ftxui {
struct Box {
int left;
int right;
int top;
int bottom;
};
}; // namespace ftxui
#endif /* end of include guard: FTXUI_DOM_BOX_HPP */

View File

@ -12,21 +12,11 @@ using Element = std::unique_ptr<Node>;
using Elements = std::vector<Element>; using Elements = std::vector<Element>;
using Decorator = std::function<Element(Element)>; using Decorator = std::function<Element(Element)>;
// --- Layout ---- // --- Widget ---
Element vbox(Elements);
Element hbox(Elements);
Element dbox(Elements);
// -- Flexibility --
Element filler();
Element flex(Element);
Decorator size(size_t width, size_t height);
// --- Widget --
Element text(std::wstring text); Element text(std::wstring text);
Element separator(); Element separator();
Element gauge(float ratio); Element gauge(float ratio);
Element frame(Element); Element border(Element);
Element window(Element title, Element content); Element window(Element title, Element content);
Element spinner(int charset_index, size_t image_index); Element spinner(int charset_index, size_t image_index);
@ -36,19 +26,37 @@ Element dim(Element);
Element inverted(Element); Element inverted(Element);
Element underlined(Element); Element underlined(Element);
Element blink(Element); Element blink(Element);
Decorator color(Color); Decorator color(Color);
Decorator bgcolor(Color); Decorator bgcolor(Color);
Element color(Color, Element); Element color(Color, Element);
Element bgcolor(Color, Element); Element bgcolor(Color, Element);
// --- Util --- // --- Layout ---
// Horizontal, Vertical or stacked set of elements.
Element vbox(Elements);
Element hbox(Elements);
Element dbox(Elements);
// -- Flexibility ---
// Define how to share the remaining space when not all of it is used inside a
// container.
Element filler();
Element flex(Element);
Decorator size(size_t width, size_t height);
// --- Frame ---
// A frame is a scrollable area. The internal area is potentially larger than
// the external one. The internal area is scrolled in order to make visible the
// focused element.
Element frame(Element);
Element focus(Element);
Element select(Element);
// --- Util --------------------------------------------------------------------
Element hcenter(Element); Element hcenter(Element);
Element vcenter(Element); Element vcenter(Element);
Element center(Element); Element center(Element);
Element align_right(Element); Element align_right(Element);
// --- Util ---
Element nothing(Element element); Element nothing(Element element);
// Pipe elements into decorator togethers. // Pipe elements into decorator togethers.

View File

@ -4,8 +4,8 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "ftxui/dom/box.hpp"
#include "ftxui/dom/requirement.hpp" #include "ftxui/dom/requirement.hpp"
#include "ftxui/screen/box.hpp"
#include "ftxui/screen/screen.hpp" #include "ftxui/screen/screen.hpp"
namespace ftxui { namespace ftxui {

View File

@ -1,6 +1,8 @@
#ifndef FTXUI_DOM_REQUIREMENT_HPP #ifndef FTXUI_DOM_REQUIREMENT_HPP
#define FTXUI_DOM_REQUIREMENT_HPP #define FTXUI_DOM_REQUIREMENT_HPP
#include "ftxui/screen/box.hpp"
namespace ftxui { namespace ftxui {
struct Requirement { struct Requirement {
@ -9,6 +11,14 @@ struct Requirement {
// How much flexibility is given to the component. // How much flexibility is given to the component.
struct { int x = 0; int y = 0; } flex; struct { int x = 0; int y = 0; } flex;
// Frame.
enum Selection {
NORMAL = 0,
SELECTED = 1,
FOCUSED = 2,
} selection = NORMAL;
Box selected_box;
}; };
}; // namespace ftxui }; // namespace ftxui

View File

@ -0,0 +1,17 @@
#ifndef FTXUI_SCREEN_BOX_HPP
#define FTXUI_SCREEN_BOX_HPP
namespace ftxui {
struct Box {
int x_min;
int x_max;
int y_min;
int y_max;
static Box Intersection(Box a, Box b);
};
}; // namespace ftxui
#endif /* end of include guard: FTXUI_SCREEN_BOX_HPP */

View File

@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include "ftxui/screen/color.hpp" #include "ftxui/screen/color.hpp"
#include "ftxui/screen/box.hpp"
namespace ftxui { namespace ftxui {
class Node; class Node;
@ -51,6 +52,7 @@ class Screen {
void Clear(); void Clear();
void ApplyShader(); void ApplyShader();
Box stencil;
protected: protected:
size_t dimx_; size_t dimx_;

View File

@ -0,0 +1,22 @@
#ifndef FTXUI_UTIL_AUTORESET_HPP
#define FTXUI_UTIL_AUTORESET_HPP
namespace ftxui {
template<typename T>
class AutoReset {
public:
AutoReset(T* variable, T new_value)
: variable_(variable),
previous_value_(std::move(*variable)) {
*variable_ = std::move(new_value);
}
~AutoReset() { *variable_ = std::move(previous_value_); }
private:
T* variable_;
T previous_value_;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_UTIL_AUTORESET_HPP */

View File

@ -4,8 +4,11 @@
namespace ftxui { namespace ftxui {
Element CheckBox::Render() { Element CheckBox::Render() {
auto style = Focused() ? focused_style : unfocused_style; bool is_focused = Focused();
return hbox(text(state ? checked : unchecked), text(label) | style); 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) { bool CheckBox::OnEvent(Event event) {

View File

@ -19,7 +19,7 @@ Container Container::Vertical() {
} }
// static // static
Container Container::Tab(size_t* selector) { Container Container::Tab(int* selector) {
Container container; Container container;
container.event_handler_ = &Container::TabEvent; container.event_handler_ = &Container::TabEvent;
container.render_handler_ = &Container::TabRender; container.render_handler_ = &Container::TabRender;
@ -53,7 +53,7 @@ bool Container::VerticalEvent(Event event) {
// Left pressed ? // Left pressed ?
if (event == Event::ArrowDown || event == Event::Character('j')) { if (event == Event::ArrowDown || event == Event::Character('j')) {
if (selected_ != children_.size() - 1) { if (selected_ != int(children_.size()) - 1) {
selected_++; selected_++;
return true; return true;
} }
@ -74,7 +74,7 @@ bool Container::HorizontalEvent(Event event) {
// Left pressed ? // Left pressed ?
if (event == Event::ArrowRight || event == Event::Character('l')) { if (event == Event::ArrowRight || event == Event::Character('l')) {
if (selected_ != children_.size() - 1) { if (selected_ != int(children_.size()) - 1) {
selected_++; selected_++;
return true; return true;
} }

View File

@ -26,12 +26,15 @@ Element Input::Render() {
std::wstring part_after_cursor = cursor_position < (int)content.size() - 1 std::wstring part_after_cursor = cursor_position < (int)content.size() - 1
? content.substr(cursor_position + 1) ? content.substr(cursor_position + 1)
: L""; : L"";
auto focused =
is_focused ? focus : select;
return return
hbox( hbox(
text(part_before_cursor), text(part_before_cursor),
text(part_at_cursor) | underlined, text(part_at_cursor) | underlined | focused,
text(part_after_cursor) text(part_after_cursor)
) | flex | inverted; ) | flex | inverted | frame;
} }
bool Input::OnEvent(Event event) { bool Input::OnEvent(Event event) {
std::wstring c; std::wstring c;

View File

@ -6,17 +6,14 @@ namespace ftxui {
Element Menu::Render() { Element Menu::Render() {
std::vector<Element> elements; std::vector<Element> elements;
bool focused = Focused(); bool is_focused = Focused();
for (size_t i = 0; i < entries.size(); ++i) { for (size_t i = 0; i < entries.size(); ++i) {
if (selected == i) { auto style = (selected != int(i))
if (focused) ? normal_style
elements.push_back(active_style(text(L"> " + entries[i]))); : is_focused ? focused_style : selected_style;
else auto focused = (selected != int(i)) ? nothing : is_focused ? focus : select;
elements.push_back(selected_style(text(L"> " + entries[i]))); auto icon = (selected != int(i)) ? L" " : L"> ";
} elements.push_back(text(icon + entries[i]) | style | focused);
else {
elements.push_back(normal_style(text(L" " + entries[i])));
}
} }
return vbox(std::move(elements)); return vbox(std::move(elements));
} }
@ -25,12 +22,12 @@ bool Menu::OnEvent(Event event) {
if (!Focused()) if (!Focused())
return false; return false;
size_t new_selected = selected; int new_selected = selected;
if (event == Event::ArrowUp || event == Event::Character('k')) if (event == Event::ArrowUp || event == Event::Character('k'))
new_selected--; new_selected--;
if (event == Event::ArrowDown || event == Event::Character('j')) if (event == Event::ArrowDown || event == Event::Character('j'))
new_selected++; new_selected++;
new_selected = std::max(size_t(0), std::min(entries.size()-size_t(1), new_selected)); new_selected = std::max(0, std::min(int(entries.size()) - 1, new_selected));
if (selected != new_selected) { if (selected != new_selected) {
selected = new_selected; selected = new_selected;

View File

@ -9,8 +9,12 @@ Element RadioBox::Render() {
for (size_t i = 0; i < entries.size(); ++i) { for (size_t i = 0; i < entries.size(); ++i) {
auto style = auto style =
(focused == int(i) && is_focused) ? focused_style : unfocused_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; const std::wstring& symbol = selected == int(i) ? checked : unchecked;
elements.push_back(hbox(text(symbol), text(entries[i]) | style)); elements.push_back(hbox(text(symbol), text(entries[i]) | style) |
focus_management);
} }
return vbox(std::move(elements)); return vbox(std::move(elements));
} }
@ -30,7 +34,7 @@ bool RadioBox::OnEvent(Event event) {
return true; return true;
} }
if (event == Event::Character(' ')) { if (event == Event::Character(' ') || event==Event::Return) {
selected = focused; selected = focused;
on_change(); on_change();
} }

View File

@ -3,7 +3,7 @@
namespace ftxui { namespace ftxui {
Element Toggle::Render() { Element Toggle::Render() {
auto highlight = Focused() ? inverted : bold; bool is_focused = Focused();
Elements children; Elements children;
for(size_t i = 0; i<entries.size(); ++i) { for(size_t i = 0; i<entries.size(); ++i) {
@ -12,8 +12,11 @@ Element Toggle::Render() {
children.push_back(separator()); children.push_back(separator());
// Entry. // Entry.
auto style = i == selected ? highlight : dim; auto style = (selected != int(i))
children.push_back(style(text(entries[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)); return hbox(std::move(children));
} }
@ -26,7 +29,7 @@ bool Toggle::OnEvent(Event event) {
return true; return true;
} }
if (selected < entries.size() - 1 && if (selected < int(entries.size()) - 1 &&
(event == Event::ArrowRight || event == Event::Character('l'))) { (event == Event::ArrowRight || event == Event::Character('l'))) {
selected++; selected++;
on_change(); on_change();

View File

@ -10,8 +10,8 @@ class Blink : public NodeDecorator {
void Render(Screen& screen) override { void Render(Screen& screen) override {
Node::Render(screen); Node::Render(screen);
for (int y = box_.top; y <= box_.bottom; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.left; x <= box_.right; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, y).blink = true; screen.PixelAt(x, y).blink = true;
} }
} }

View File

@ -9,8 +9,8 @@ class Bold : public NodeDecorator {
~Bold() override {} ~Bold() override {}
void Render(Screen& screen) override { void Render(Screen& screen) override {
for (int y = box_.top; y <= box_.bottom; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.left; x <= box_.right; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x,y).bold = true; screen.PixelAt(x,y).bold = true;
} }
} }

View File

@ -0,0 +1,88 @@
#include "ftxui/dom/node.hpp"
#include "ftxui/dom/elements.hpp"
namespace ftxui {
using namespace ftxui;
static wchar_t charset[] = L"┌┐└┘─│┬┴┤├";
class Border : public Node {
public:
Border(Elements children) : Node(std::move(children)) {}
~Border() override {}
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;
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);
}
};
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 boxed() {
return [](Element child) {
return border(std::move(child));
};
}
}; // namespace ftxui

View File

@ -9,8 +9,8 @@ class BgColor : public NodeDecorator {
: NodeDecorator(std::move(children)), color_(color) {} : NodeDecorator(std::move(children)), color_(color) {}
void Render(Screen& screen) override { void Render(Screen& screen) override {
for (int y = box_.top; y <= box_.bottom; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.left; x <= box_.right; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, y).background_color = color_; screen.PixelAt(x, y).background_color = color_;
} }
} }
@ -27,8 +27,8 @@ class FgColor : public NodeDecorator {
~FgColor() override {} ~FgColor() override {}
void Render(Screen& screen) override { void Render(Screen& screen) override {
for (int y = box_.top; y <= box_.bottom; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.left; x <= box_.right; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, y).foreground_color = color_; screen.PixelAt(x, y).foreground_color = color_;
} }
} }

View File

@ -17,6 +17,11 @@ class DBox : public Node {
child->ComputeRequirement(); child->ComputeRequirement();
requirement_.min.x = std::max(requirement_.min.x, child->requirement().min.x); requirement_.min.x = std::max(requirement_.min.x, child->requirement().min.x);
requirement_.min.y = std::max(requirement_.min.y, child->requirement().min.y); 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;
}
} }
} }

View File

@ -12,8 +12,8 @@ class Dim : public NodeDecorator {
void Render(Screen& screen) override { void Render(Screen& screen) override {
Node::Render(screen); Node::Render(screen);
for (int y = box_.top; y <= box_.bottom; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.left; x <= box_.right; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x,y).dim = true; screen.PixelAt(x,y).dim = true;
} }
} }

View File

@ -1,69 +1,116 @@
#include "ftxui/dom/node.hpp"
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp"
#include "ftxui/dom/node.hpp"
#include "ftxui/util/autoreset.hpp"
namespace ftxui { namespace ftxui {
using namespace ftxui; using namespace ftxui;
static wchar_t charset[] = L"┌┐└┘─│┬┴┤├"; // -----------------------------------------------------------------------------
class Frame : public Node { class Select : public Node {
public: public:
Frame(Elements children) : Node(std::move(children)) {} Select(std::vector<std::unique_ptr<Node>> children)
~Frame() override {} : 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 { void ComputeRequirement() override {
Node::ComputeRequirement(); Node::ComputeRequirement();
requirement_ = children[0]->requirement(); 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);
}
} }
void SetBox(Box box) override { void SetBox(Box box) override {
Node::SetBox(box); Node::SetBox(box);
if (children.size() == 2) {
Box title_box; int external_dimx = box.x_max - box.x_min;
title_box.left = box.left + 1; int external_dimy = box.y_max - box.y_min;
title_box.right = box.right - 1;
title_box.top = box.top; int internal_dimx = std::max(requirement_.min.x, external_dimx);
title_box.bottom = box.top; int internal_dimy = std::max(requirement_.min.y, external_dimy);
children[1]->SetBox(title_box);
} auto& selected_box = requirement_.selected_box;
box.left++; int focused_dimx = selected_box.x_max - selected_box.x_min;
box.right--; int focused_dimy = selected_box.y_max - selected_box.y_min;
box.top++; int dx = selected_box.x_min - external_dimx / 2 + focused_dimx / 2;
box.bottom--; int dy = selected_box.y_min - external_dimy / 2 + focused_dimy / 2;
children[0]->SetBox(box); 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 { void Render(Screen& screen) override {
// Draw content. AutoReset<Box> stencil(&screen.stencil,
Box::Intersection(box_, screen.stencil));
children[0]->Render(screen); children[0]->Render(screen);
// Draw the frame.
if (box_.left >= box_.right || box_.top >= box_.bottom)
return;
screen.at(box_.left, box_.top) = charset[0];
screen.at(box_.right, box_.top) = charset[1];
screen.at(box_.left, box_.bottom) = charset[2];
screen.at(box_.right, box_.bottom) = charset[3];
for(float x = box_.left + 1; x<box_.right; ++x) {
screen.at(x, box_.top) = charset[4];
screen.at(x, box_.bottom) = charset[4];
}
for(float y = box_.top + 1; y<box_.bottom; ++y) {
screen.at(box_.left, y) = charset[5];
screen.at(box_.right,y) = charset[5];
}
// Draw title.
if (children.size() == 2)
children[1]->Render(screen);
} }
}; };
@ -71,14 +118,4 @@ std::unique_ptr<Node> frame(Element child) {
return std::make_unique<Frame>(unpack(std::move(child))); return std::make_unique<Frame>(unpack(std::move(child)));
} }
std::unique_ptr<Node> window(Element title, Element content) {
return std::make_unique<Frame>(unpack(std::move(content), std::move(title)));
}
Decorator boxed() {
return [](Element child) {
return frame(std::move(child));
};
}
}; // namespace ftxui }; // namespace ftxui

View File

@ -18,14 +18,14 @@ class Gauge : public Node {
} }
void Render(Screen& screen) override { void Render(Screen& screen) override {
float y = box_.top; float y = box_.y_min;
float limit = box_.left + progress_ * (box_.right - box_.left + 1); float limit = box_.x_min + progress_ * (box_.x_max - box_.x_min + 1);
int limit_int = limit; int limit_int = limit;
int x = box_.left; int x = box_.x_min;
while (x < limit_int) while (x < limit_int)
screen.at(x++, y) = charset[9]; screen.at(x++, y) = charset[9];
screen.at(x++, y) = charset[int(9*(limit-limit_int))]; screen.at(x++, y) = charset[int(9*(limit-limit_int))];
while (x <= box_.right) while (x <= box_.x_max)
screen.at(x++, y) = charset[0]; screen.at(x++, y) = charset[0];
} }
private: private:

View File

@ -15,6 +15,12 @@ class HBox : public Node {
requirement_.flex.y = 0; requirement_.flex.y = 0;
for (auto& child : children) { for (auto& child : children) {
child->ComputeRequirement(); 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.x += child->requirement().min.x;
requirement_.min.y = requirement_.min.y =
std::max(requirement_.min.y, child->requirement().min.y); std::max(requirement_.min.y, child->requirement().min.y);
@ -28,33 +34,33 @@ class HBox : public Node {
for (auto& child : children) for (auto& child : children)
flex_sum += child->requirement().flex.x; flex_sum += child->requirement().flex.x;
int space = box.right - box.left + 1; int space = box.x_max - box.x_min + 1;
int extra_space = space - requirement_.min.x; int extra_space = space - requirement_.min.x;
int remaining_flex = flex_sum; int remaining_flex = flex_sum;
int remaining_extra_space = extra_space; int remaining_extra_space = extra_space;
int x = box.left; int x = box.x_min;
for (auto& child : children) { for (auto& child : children) {
if (x > box.right) if (x > box.x_max)
break; break;
Box child_box = box; Box child_box = box;
child_box.left = x; child_box.x_min = x;
child_box.right = x + child->requirement().min.x - 1; child_box.x_max = x + child->requirement().min.x - 1;
if (child->requirement().flex.x && remaining_extra_space > 0) { if (child->requirement().flex.x && remaining_extra_space > 0) {
int added_space = remaining_extra_space * child->requirement().flex.x / int added_space = remaining_extra_space * child->requirement().flex.x /
remaining_flex; remaining_flex;
remaining_extra_space -= added_space; remaining_extra_space -= added_space;
remaining_flex -= child->requirement().flex.x; remaining_flex -= child->requirement().flex.x;
child_box.right += added_space; child_box.x_max += added_space;
} }
child_box.right = std::min(child_box.right, box.right); child_box.x_max = std::min(child_box.x_max, box.x_max);
child->SetBox(child_box); child->SetBox(child_box);
x = child_box.right + 1; x = child_box.x_max + 1;
} }
} }
}; };

View File

@ -12,8 +12,8 @@ class Inverted : public NodeDecorator {
void Render(Screen& screen) override { void Render(Screen& screen) override {
Node::Render(screen); Node::Render(screen);
for (int y = box_.top; y <= box_.bottom; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.left; x <= box_.right; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x,y).inverted = true; screen.PixelAt(x,y).inverted = true;
} }
} }

View File

@ -28,13 +28,14 @@ void Render(Screen& screen, Node* node) {
node->ComputeRequirement(); node->ComputeRequirement();
Box box; Box box;
box.left = 0; box.x_min = 0;
box.top = 0; box.y_min = 0;
box.right = screen.dimx() - 1; box.x_max = screen.dimx() - 1;
box.bottom = screen.dimy() - 1; box.y_max = screen.dimy() - 1;
// Step 2: Assign a dimension to the element. // Step 2: Assign a dimension to the element.
node->SetBox(box); node->SetBox(box);
screen.stencil = box;
// Step 3: Draw the element. // Step 3: Draw the element.
node->Render(screen); node->Render(screen);

View File

@ -14,8 +14,8 @@ class Separator : public Node {
} }
void Render(Screen& screen) override { void Render(Screen& screen) override {
bool is_column = (box_.right == box_.left); bool is_column = (box_.x_max == box_.x_min);
bool is_line = (box_.top == box_.bottom); bool is_line = (box_.y_min == box_.y_max);
wchar_t c = U'+'; wchar_t c = U'+';
if (is_line && !is_column) if (is_line && !is_column)
@ -23,8 +23,8 @@ class Separator : public Node {
else else
c = U''; c = U'';
for (int y = box_.top; y <= box_.bottom; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.left; x <= box_.right; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.at(x, y) = c; screen.at(x, y) = c;
} }
} }

View File

@ -10,6 +10,7 @@ class Size : public Node {
~Size() override {} ~Size() override {}
void ComputeRequirement() override { void ComputeRequirement() override {
Node::ComputeRequirement(); Node::ComputeRequirement();
requirement_ = children[0]->requirement();
requirement_.min.x = width_; requirement_.min.x = width_;
requirement_.min.y = height_; requirement_.min.y = height_;
requirement_.flex.x = 0; requirement_.flex.x = 0;

View File

@ -15,12 +15,12 @@ class Text : public Node {
} }
void Render(Screen& screen) override { void Render(Screen& screen) override {
int x = box_.left; int x = box_.x_min;
int y = box_.top; int y = box_.y_min;
if (y > box_.bottom) if (y > box_.y_max)
return; return;
for (wchar_t c : text_) { for (wchar_t c : text_) {
if (x > box_.right) if (x > box_.x_max)
return; return;
screen.at(x++, y) = c; screen.at(x++, y) = c;
} }

View File

@ -12,8 +12,8 @@ class Underlined : public NodeDecorator {
void Render(Screen& screen) override { void Render(Screen& screen) override {
Node::Render(screen); Node::Render(screen);
for (int y = box_.top; y <= box_.bottom; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.left; x <= box_.right; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, y).underlined = true; screen.PixelAt(x, y).underlined = true;
} }
} }

View File

@ -15,6 +15,12 @@ class VBox : public Node {
requirement_.flex.y = 1; requirement_.flex.y = 1;
for (auto& child : children) { for (auto& child : children) {
child->ComputeRequirement(); 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.y += child->requirement().min.y;
requirement_.min.x = requirement_.min.x =
std::max(requirement_.min.x, child->requirement().min.x); std::max(requirement_.min.x, child->requirement().min.x);
@ -28,33 +34,33 @@ class VBox : public Node {
for (auto& child : children) for (auto& child : children)
flex_sum += child->requirement().flex.y; flex_sum += child->requirement().flex.y;
int space = box.bottom - box.top + 1; int space = box.y_max - box.y_min + 1;
int extra_space = space - requirement_.min.y; int extra_space = space - requirement_.min.y;
int remaining_flex = flex_sum; int remaining_flex = flex_sum;
int remaining_extra_space = extra_space; int remaining_extra_space = extra_space;
int y = box.top; int y = box.y_min;
for (auto& child : children) { for (auto& child : children) {
if (y > box.bottom) if (y > box.y_max)
break; break;
Box child_box = box; Box child_box = box;
child_box.top = y; child_box.y_min = y;
child_box.bottom = y + child->requirement().min.y - 1; child_box.y_max = y + child->requirement().min.y - 1;
if (child->requirement().flex.y && remaining_extra_space > 0) { if (child->requirement().flex.y && remaining_extra_space > 0) {
int added_space = remaining_extra_space * child->requirement().flex.y / int added_space = remaining_extra_space * child->requirement().flex.y /
remaining_flex; remaining_flex;
remaining_extra_space -= added_space; remaining_extra_space -= added_space;
remaining_flex -= child->requirement().flex.y; remaining_flex -= child->requirement().flex.y;
child_box.bottom += added_space; child_box.y_max += added_space;
} }
child_box.bottom = std::min(child_box.bottom, box.bottom); child_box.y_max = std::min(child_box.y_max, box.y_max);
child->SetBox(child_box); child->SetBox(child_box);
y = child_box.bottom + 1; y = child_box.y_max + 1;
} }
} }
}; };

View File

@ -0,0 +1,14 @@
#include "ftxui/screen/box.hpp"
#include <cmath>
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

View File

@ -7,6 +7,7 @@
namespace ftxui { namespace ftxui {
namespace {
static const wchar_t* BOLD_SET = L"\e[1m"; 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* BOLD_RESET = L"\e[22m"; // Can't use 21 here.
@ -26,8 +27,20 @@ static const char* MOVE_LEFT = "\r";
static const char* MOVE_UP = "\e[1A"; static const char* MOVE_UP = "\e[1A";
static const char* CLEAR_LINE = "\e[2K"; 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
Screen::Screen(size_t dimx, size_t dimy) Screen::Screen(size_t dimx, size_t dimy)
: dimx_(dimx), dimy_(dimy), pixels_(dimy, std::vector<Pixel>(dimx)) {} : stencil({0, int(dimx) - 1, 0, int(dimy) - 1}),
dimx_(dimx),
dimy_(dimy),
pixels_(dimy, std::vector<Pixel>(dimx)) {}
void UpdatePixelStyle(std::wstringstream& ss, Pixel& previous, Pixel& next) { void UpdatePixelStyle(std::wstringstream& ss, Pixel& previous, Pixel& next) {
if (next.bold != previous.bold) if (next.bold != previous.bold)
@ -76,11 +89,11 @@ std::string Screen::ToString() {
} }
wchar_t& Screen::at(size_t x, size_t y) { wchar_t& Screen::at(size_t x, size_t y) {
return pixels_[y][x].character; return PixelAt(x,y).character;
} }
Pixel& Screen::PixelAt(size_t x, size_t y) { Pixel& Screen::PixelAt(size_t x, size_t y) {
return pixels_[y][x]; return In(stencil, x, y) ? pixels_[y][x] : dev_null_pixel;
} }
// static // static

View File

@ -5,7 +5,7 @@
+ [Examples](#examples) + [Examples](#examples)
* [Widget.](#widget) * [Widget.](#widget)
+ [text](#text) + [text](#text)
+ [frame](#frame) + [border](#border)
+ [separator](#separator) + [separator](#separator)
+ [gauge](#gauge) + [gauge](#gauge)
* [Decorator](#decorator) * [Decorator](#decorator)
@ -27,21 +27,11 @@ All the dom element are declared in one header:
It declares the following set of elements: It declares the following set of elements:
~~~cpp ~~~cpp
// --- Layout ---- // --- Widget ---
Element vbox(Elements);
Element hbox(Elements);
Element dbox(Elements);
// -- Flexibility --
Element filler();
Element flex(Element);
Decorator size(size_t width, size_t height);
// --- Widget --
Element text(std::wstring text); Element text(std::wstring text);
Element separator(); Element separator();
Element gauge(float ratio); Element gauge(float ratio);
Element frame(Element); Element border(Element);
Element window(Element title, Element content); Element window(Element title, Element content);
Element spinner(int charset_index, size_t image_index); Element spinner(int charset_index, size_t image_index);
@ -51,19 +41,37 @@ Element dim(Element);
Element inverted(Element); Element inverted(Element);
Element underlined(Element); Element underlined(Element);
Element blink(Element); Element blink(Element);
Decorator color(Color); Decorator color(Color);
Decorator bgcolor(Color); Decorator bgcolor(Color);
Element color(Color, Element); Element color(Color, Element);
Element bgcolor(Color, Element); Element bgcolor(Color, Element);
// --- Util --- // --- Layout ---
// Horizontal, Vertical or stacked set of elements.
Element vbox(Elements);
Element hbox(Elements);
Element dbox(Elements);
// -- Flexibility ---
// Define how to share the remaining space when not all of it is used inside a
// container.
Element filler();
Element flex(Element);
Decorator size(size_t width, size_t height);
// --- Frame ---
// A frame is a scrollable area. The internal area is potentially larger than
// the external one. The internal area is scrolled in order to make visible the
// focused element.
Element frame(Element);
Element focus(Element);
Element select(Element);
// --- Util --------------------------------------------------------------------
Element hcenter(Element); Element hcenter(Element);
Element vcenter(Element); Element vcenter(Element);
Element center(Element); Element center(Element);
Element align_right(Element); Element align_right(Element);
// --- Util ---
Element nothing(Element element); Element nothing(Element element);
~~~ ~~~
@ -98,9 +106,9 @@ container.
#### Examples #### Examples
~~~cpp ~~~cpp
hbox( hbox(
text(L"left") | frame , text(L"left") | border ,
text(L"middle") | frame | flex, text(L"middle") | border | flex,
text(L"right") | frame text(L"right") | border
); );
~~~ ~~~
~~~bash ~~~bash
@ -111,9 +119,9 @@ container.
~~~cpp ~~~cpp
hbox( hbox(
text(L"left") | frame , text(L"left") | border ,
text(L"middle") | frame | flex, text(L"middle") | border | flex,
text(L"right") | frame | flex text(L"right") | border | flex
); );
~~~ ~~~
~~~bash ~~~bash
@ -134,10 +142,10 @@ The most simple widget. It display a text.
I am a piece of text. I am a piece of text.
~~~ ~~~
#### frame #### border
Add a border arround an element Add a border arround an element
~~~cpp ~~~cpp
frame(text(L"The element")) border(text(L"The element"))
~~~ ~~~
~~~bash ~~~bash
@ -152,7 +160,7 @@ Display a vertical or horizontal line to visually split the content of a
container in two. container in two.
~~~cpp ~~~cpp
frame(hbox( border(hbox(
vbox( vbox(
text(L"left top"), text(L"left top"),
text(L"left bottom") text(L"left bottom")
@ -176,7 +184,7 @@ frame(hbox(
A gauge. It can be used to represent a progress bar. A gauge. It can be used to represent a progress bar.
~~~c+ ~~~c+
frame(gauge(0.5)) border(gauge(0.5))
~~~ ~~~
~~~bash ~~~bash