feat: Multiple border style. (#202)

This commit is contained in:
Arthur Sonzogni 2021-09-12 00:36:59 +02:00 committed by GitHub
parent 4d50dadb41
commit b5c3b17b3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 506 additions and 40 deletions

View File

@ -1,6 +1,7 @@
set(DIRECTORY_LIB dom)
example(border)
example(border_style)
example(color_gallery)
example(dbox)
example(gauge)
@ -10,6 +11,7 @@ example(html_like)
example(package_manager)
example(paragraph)
example(separator)
example(separator_style)
example(size)
example(spinner)
example(style_blink)

View File

@ -0,0 +1,28 @@
#include <ftxui/dom/elements.hpp> // for text, operator|, vbox, border, Element, Fit, hbox
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <iostream>
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto document = vbox({
text("borderLight") | borderLight,
text("borderHeavy") | borderHeavy,
text("borderDouble") | borderDouble,
text("borderRounded") | borderRounded,
});
auto screen =
Screen::Create(Dimension::Fit(document), Dimension::Fit(document));
Render(screen, document);
screen.Print();
std::cout << std::endl;
}
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@ -11,9 +11,9 @@ int main(int argc, const char* argv[]) {
text("left-column"),
separator(),
vbox({
center(text("right-top")) | flex,
center(text("top")) | flex,
separator(),
center(text("bottom-bottom")),
center(text("bottom")),
}) | flex,
separator(),
text("right-column"),

View File

@ -0,0 +1,41 @@
#include <ftxui/dom/elements.hpp> // for text, operator|, vbox, border, Element, Fit, hbox
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <iostream>
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto document = vbox({
vbox({
text("separatorLight"),
separatorLight(),
hbox(text("left"), separatorLight(), text("right")),
}) | borderLight,
vbox({
text("separatorHeavy"),
separatorHeavy(),
hbox(text("left"), separatorHeavy(), text("right")),
}) | borderHeavy,
vbox({
text("separatorDouble"),
separatorDouble(),
hbox(text("left"), separatorDouble(), text("right")),
}) | borderDouble,
});
auto screen =
Screen::Create(Dimension::Fit(document), Dimension::Fit(document));
Render(screen, document);
screen.Print();
std::cout << std::endl;
}
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@ -17,6 +17,8 @@ using Elements = std::vector<Element>;
using Decorator = std::function<Element(Element)>;
using GraphFunction = std::function<std::vector<int>(int, int)>;
enum BorderStyle { LIGHT, HEAVY, DOUBLE, ROUNDED };
// Pipe elements into decorator togethers.
// For instance the next lines are equivalents:
// -> text("ftxui") | bold | underlined
@ -29,9 +31,18 @@ Decorator operator|(Decorator, Decorator);
Element text(std::string text);
Element vtext(std::string text);
Element separator(void);
Element separatorLight();
Element separatorHeavy();
Element separatorDouble();
Element separatorStyled(BorderStyle);
Element separator(Pixel);
Element gauge(float ratio);
Element border(Element);
Element borderLight(Element);
Element borderHeavy(Element);
Element borderDouble(Element);
Element borderRounded(Element);
Decorator borderStyled(BorderStyle);
Decorator borderWith(Pixel);
Element window(Element title, Element content);
Element spinner(int charset_index, size_t image_index);

View File

@ -13,8 +13,12 @@
namespace ftxui {
static std::string simple_border_charset[] = {"", "", "", "", "",
"", "", "", "", ""};
static std::string simple_border_charset[6][6] = {
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
};
// For reference, here is the charset for normal border:
// {"┌", "┐", "└", "┘", "─", "│", "┬", "┴", "┤", "├"};
@ -23,10 +27,10 @@ static std::string simple_border_charset[] = {"╭", "╮", "╰", "╯", "─",
class Border : public Node {
public:
Border(Elements children)
Border(Elements children, BorderStyle style)
: Node(std::move(children)),
charset(std::begin(simple_border_charset),
std::end(simple_border_charset)) {}
charset(std::begin(simple_border_charset[style]),
std::end(simple_border_charset[style])) {}
Border(Elements children, Pixel pixel)
: Node(std::move(children)), charset_pixel(10, pixel) {}
@ -113,8 +117,14 @@ class Border : public Node {
}
}
};
/// @brief Draw a border around the element.
/// @ingroup dom
/// @see border
/// @see borderLight
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
///
/// Add a border around an element
///
@ -136,7 +146,158 @@ class Border : public Node {
/// └───────────┘
/// ```
Element border(Element child) {
return std::make_shared<Border>(unpack(std::move(child)));
return std::make_shared<Border>(unpack(std::move(child)), ROUNDED);
}
/// @brief Same as border but with a constant Pixel around the element.
/// @ingroup dom
/// @see border
Decorator borderWith(Pixel pixel) {
return [pixel](Element child) {
return std::make_shared<Border>(unpack(std::move(child)), pixel);
};
}
/// @brief Same as border but with different styles.
/// @ingroup dom
/// @see border
Decorator borderStyled(BorderStyle style) {
return [style](Element child) {
return std::make_shared<Border>(unpack(std::move(child)), style);
};
}
/// @brief Draw a light border around the element.
/// @ingroup dom
/// @see border
/// @see borderLight
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderStyled
/// @see borderWith
///
/// Add a border around an element
///
/// ### Example
///
/// ```cpp
/// // Use 'borderLight' as a function...
/// Element document = borderLight(text("The element"));
///
/// // ...Or as a 'pipe'.
/// Element document = text("The element") | borderLight;
/// ```
///
/// ### Output
///
/// ```bash
/// ┌──────────────┐
/// │The element │
/// └──────────────┘
/// ```
Element borderLight(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), LIGHT);
}
/// @brief Draw a heavy border around the element.
/// @ingroup dom
/// @see border
/// @see borderLight
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderStyled
/// @see borderWith
///
/// Add a border around an element
///
/// ### Example
///
/// ```cpp
/// // Use 'borderHeavy' as a function...
/// Element document = borderHeavy(text("The element"));
///
/// // ...Or as a 'pipe'.
/// Element document = text("The element") | borderHeavy;
/// ```
///
/// ### Output
///
/// ```bash
/// ┏━━━━━━━━━━━━━━┓
/// ┃The element ┃
/// ┗━━━━━━━━━━━━━━┛
/// ```
Element borderHeavy(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), HEAVY);
}
/// @brief Draw a double border around the element.
/// @ingroup dom
/// @see border
/// @see borderLight
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderStyled
/// @see borderWith
///
/// Add a border around an element
///
/// ### Example
///
/// ```cpp
/// // Use 'borderDouble' as a function...
/// Element document = borderDouble(text("The element"));
///
/// // ...Or as a 'pipe'.
/// Element document = text("The element") | borderDouble;
/// ```
///
/// ### Output
///
/// ```bash
/// ╔══════════════╗
/// ║The element ║
/// ╚══════════════╝
/// ```
Element borderDouble(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), DOUBLE);
}
/// @brief Draw a rounded border around the element.
/// @ingroup dom
/// @see border
/// @see borderLight
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderStyled
/// @see borderWith
///
/// Add a border around an element
///
/// ### Example
///
/// ```cpp
/// // Use 'borderRounded' as a function...
/// Element document = borderRounded(text("The element"));
///
/// // ...Or as a 'pipe'.
/// Element document = text("The element") | borderRounded;
/// ```
///
/// ### Output
///
/// ```bash
/// ╭──────────────╮
/// │The element │
/// ╰──────────────╯
/// ```
Element borderRounded(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), ROUNDED);
}
/// @brief Draw window with a title and a border around the element.
@ -161,18 +322,9 @@ Element border(Element child) {
/// └───────┘
/// ```
Element window(Element title, Element content) {
return std::make_shared<Border>(unpack(std::move(content), std::move(title)));
return std::make_shared<Border>(unpack(std::move(content), std::move(title)),
ROUNDED);
}
/// @brief Same as border but with a constant Pixel around the element.
/// @ingroup dom
/// @see border
Decorator borderWith(Pixel pixel) {
return [pixel](Element child) {
return std::make_shared<Border>(unpack(std::move(child)), pixel);
};
}
} // namespace ftxui
// Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -11,8 +11,17 @@ namespace ftxui {
using ftxui::Screen;
const std::string charset[][2] = {
{"", ""},
{"", ""},
{"", ""},
{"", ""},
};
class Separator : public Node {
public:
Separator(BorderStyle style) : style_(style) {}
void ComputeRequirement() override {
requirement_.min_x = 1;
requirement_.min_y = 1;
@ -22,11 +31,7 @@ class Separator : public Node {
bool is_column = (box_.x_max == box_.x_min);
bool is_line = (box_.y_min == box_.y_max);
std::string c = "+";
if (is_line && !is_column)
c = "";
else
c = "";
const std::string c = charset[style_][is_line && !is_column];
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
@ -34,11 +39,13 @@ class Separator : public Node {
}
}
}
BorderStyle style_;
};
class SeparatorWithPixel : public Separator {
public:
SeparatorWithPixel(Pixel pixel) : pixel_(pixel) {}
SeparatorWithPixel(Pixel pixel) : Separator(LIGHT), pixel_(pixel) {}
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) {
@ -52,11 +59,21 @@ class SeparatorWithPixel : public Separator {
};
Element separator() {
return std::make_shared<Separator>();
return std::make_shared<Separator>(LIGHT);
}
Element separator(Pixel pixel) {
return std::make_shared<SeparatorWithPixel>(pixel);
Element separatorStyled(BorderStyle style) {
return std::make_shared<Separator>(style);
}
Element separatorLight() {
return std::make_shared<Separator>(LIGHT);
}
Element separatorHeavy() {
return std::make_shared<Separator>(HEAVY);
}
Element separatorDouble() {
return std::make_shared<Separator>(DOUBLE);
}
} // namespace ftxui

View File

@ -1,4 +1,5 @@
#include <iostream> // for operator<<, stringstream, basic_ostream, flush, cout, ostream
#include <map>
#include <memory> // for allocator
#include <sstream> // IWYU pragma: keep
@ -88,6 +89,228 @@ void UpdatePixelStyle(std::stringstream& ss,
previous = next;
}
struct TileEncoding {
unsigned int left : 2;
unsigned int top : 2;
unsigned int right : 2;
unsigned int down : 2;
unsigned int round : 1;
bool operator<(const TileEncoding& other) const {
union Converter {
TileEncoding input;
uint16_t output = 0;
};
Converter a, b;
a.input = *this;
b.input = other;
return a.output < b.output;
}
};
// clang-format off
const std::map<std::string, TileEncoding> tile_encoding = {
{"", {1, 0, 1, 0, 0}},
{"", {2, 0, 2, 0, 0}},
{"", {0, 1, 0, 1, 0}},
{"", {0, 2, 0, 2, 0}},
{"", {0, 0, 1, 1, 0}},
{"", {0, 0, 2, 1, 0}},
{"", {0, 0, 1, 2, 0}},
{"", {0, 0, 2, 2, 0}},
{"", {1, 0, 0, 1, 0}},
{"", {2, 0, 0, 1, 0}},
{"", {1, 0, 0, 2, 0}},
{"", {2, 0, 0, 2, 0}},
{"", {0, 1, 1, 0, 0}},
{"", {0, 1, 2, 0, 0}},
{"", {0, 2, 1, 0, 0}},
{"", {0, 2, 2, 0, 0}},
{"", {1, 1, 0, 0, 0}},
{"", {2, 1, 0, 0, 0}},
{"", {1, 2, 0, 0, 0}},
{"", {2, 2, 0, 0, 0}},
{"", {0, 1, 1, 1, 0}},
{"", {0, 1, 2, 1, 0}},
{"", {0, 2, 1, 1, 0}},
{"", {0, 1, 1, 2, 0}},
{"", {0, 2, 1, 2, 0}},
{"", {0, 2, 2, 1, 0}},
{"", {0, 1, 2, 2, 0}},
{"", {0, 2, 2, 2, 0}},
{"", {1, 1, 0, 1, 0}},
{"", {2, 1, 0, 1, 0}},
{"", {1, 2, 0, 1, 0}},
{"", {1, 1, 0, 2, 0}},
{"", {1, 2, 0, 2, 0}},
{"", {2, 2, 0, 1, 0}},
{"", {2, 1, 0, 2, 0}},
{"", {2, 2, 0, 2, 0}},
{"", {1, 0, 1, 1, 0}},
{"", {2, 0, 1, 1, 0}},
{"", {1, 0, 2, 1, 0}},
{"", {2, 0, 2, 1, 0}},
{"", {1, 0, 1, 2, 0}},
{"", {2, 0, 1, 2, 0}},
{"", {1, 0, 2, 2, 0}},
{"", {2, 0, 2, 2, 0}},
{"", {1, 1, 1, 0, 0}},
{"", {2, 1, 1, 0, 0}},
{"", {1, 1, 2, 0, 0}},
{"", {2, 1, 2, 0, 0}},
{"", {1, 2, 1, 0, 0}},
{"", {2, 2, 1, 0, 0}},
{"", {1, 2, 2, 0, 0}},
{"", {2, 2, 2, 0, 0}},
{"", {1, 1, 1, 1, 0}},
{"", {2, 1, 1, 1, 0}},
{"", {1, 1, 2, 1, 0}},
{"", {2, 1, 2, 1, 0}},
{"", {1, 2, 1, 1, 0}},
{"", {1, 1, 1, 2, 0}},
{"", {1, 2, 1, 2, 0}},
{"", {2, 2, 1, 1, 0}},
{"", {1, 2, 2, 1, 0}},
{"", {2, 1, 1, 2, 0}},
{"", {1, 1, 2, 2, 0}},
{"", {2, 2, 2, 1, 0}},
{"", {2, 1, 2, 2, 0}},
{"", {2, 2, 1, 2, 0}},
{"", {1, 2, 2, 2, 0}},
{"", {2, 2, 2, 2, 0}},
{"", {3, 0, 3, 0, 0}},
{"", {0, 3, 0, 3, 0}},
{"", {0, 0, 3, 1, 0}},
{"", {0, 0, 1, 3, 0}},
{"", {0, 0, 3, 3, 0}},
{"", {3, 0, 0, 1, 0}},
{"", {1, 0, 0, 3, 0}},
{"", {3, 0, 0, 3, 0}},
{"", {0, 1, 3, 0, 0}},
{"", {0, 3, 1, 0, 0}},
{"", {0, 3, 3, 0, 0}},
{"", {3, 1, 0, 0, 0}},
{"", {1, 3, 0, 0, 0}},
{"", {3, 3, 0, 0, 0}},
{"", {0, 1, 3, 1, 0}},
{"", {0, 3, 1, 3, 0}},
{"", {0, 3, 3, 3, 0}},
{"", {3, 1, 0, 1, 0}},
{"", {1, 3, 0, 3, 0}},
{"", {3, 3, 0, 3, 0}},
{"", {3, 0, 3, 1, 0}},
{"", {1, 0, 1, 3, 0}},
{"", {3, 0, 3, 3, 0}},
{"", {3, 1, 3, 0, 0}},
{"", {1, 3, 1, 0, 0}},
{"", {3, 3, 3, 0, 0}},
{"", {3, 1, 3, 1, 0}},
{"", {1, 3, 1, 3, 0}},
{"", {3, 3, 3, 3, 0}},
{"", {0, 0, 1, 1, 1}},
{"", {1, 0, 0, 1, 1}},
{"", {1, 1, 0, 0, 1}},
{"", {0, 1, 1, 0, 1}},
{"", {1, 0, 0, 0, 0}},
{"", {0, 1, 0, 0, 0}},
{"", {0, 0, 1, 0, 0}},
{"", {0, 0, 0, 1, 0}},
{"", {2, 0, 0, 0, 0}},
{"", {0, 2, 0, 0, 0}},
{"", {0, 0, 2, 0, 0}},
{"", {0, 0, 0, 2, 0}},
{"", {1, 0, 2, 0, 0}},
{"", {0, 1, 0, 2, 0}},
{"", {2, 0, 1, 0, 0}},
{"", {0, 2, 0, 1, 0}},
};
// clang-format on
template <class A, class B>
const std::map<B, A> InvertMap(const std::map<A, B> input) {
std::map<B, A> output;
for (const auto& it : input)
output[it.second] = it.first;
return output;
}
const std::map<TileEncoding, std::string> tile_encoding_inverse =
InvertMap(tile_encoding);
void UpgradeLeftRight(std::string& left, std::string& right) {
const auto it_left = tile_encoding.find(left);
if (it_left == tile_encoding.end())
return;
const auto it_right = tile_encoding.find(right);
if (it_right == tile_encoding.end())
return;
if (it_left->second.right == 0 && it_right->second.left != 0) {
TileEncoding encoding_left = it_left->second;
encoding_left.right = it_right->second.left;
const auto it_left_upgrade = tile_encoding_inverse.find(encoding_left);
if (it_left_upgrade != tile_encoding_inverse.end())
left = it_left_upgrade->second;
}
if (it_right->second.left == 0 && it_left->second.right != 0) {
TileEncoding encoding_right = it_right->second;
encoding_right.left = it_left->second.right;
const auto it_right_upgrade = tile_encoding_inverse.find(encoding_right);
if (it_right_upgrade != tile_encoding_inverse.end())
right = it_right_upgrade->second;
}
}
void UpgradeTopDown(std::string& top, std::string& down) {
const auto it_top = tile_encoding.find(top);
if (it_top == tile_encoding.end())
return;
const auto it_down = tile_encoding.find(down);
if (it_down == tile_encoding.end())
return;
if (it_top->second.down == 0 && it_down->second.top != 0) {
TileEncoding encoding_top = it_top->second;
encoding_top.down = it_down->second.top;
const auto it_top_down = tile_encoding_inverse.find(encoding_top);
if (it_top_down != tile_encoding_inverse.end())
top = it_top_down->second;
}
if (it_down->second.top == 0 && it_top->second.down != 0) {
TileEncoding encoding_down = it_down->second;
encoding_down.top = it_top->second.down;
const auto it_down_top = tile_encoding_inverse.find(encoding_down);
if (it_down_top != tile_encoding_inverse.end())
down = it_down_top->second;
}
}
} // namespace
/// A fixed dimension.
@ -234,21 +457,13 @@ void Screen::ApplyShader() {
// Left vs current.
std::string& left = pixels_[y][x-1].character;
if (left.size() == 3u) {
if (cur == "" && left == "") cur = "";
if (cur == "" && left == "") cur = "";
if (cur == "" && left == "") left = "";
if (cur == "" && left == "") left = "";
}
if (left.size() == 3u)
UpgradeLeftRight(left, cur);
// Top vs current.
std::string& top = pixels_[y-1][x].character;
if (top.size() == 3u) {
if (cur == "" && top == "") cur = "";
if (cur == "" && top == "") cur = "";
if (cur == "" && top == "") top = "";
if (cur == "" && top == "") top = "";
}
if (top.size() == 3u)
UpgradeTopDown(top, cur);
}
}
}