This class allows rendering stylized table.
This commit is contained in:
Arthur Sonzogni 2021-10-15 23:04:11 +02:00 committed by GitHub
parent 7298636e7c
commit 026a005753
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1507 additions and 119 deletions

View File

@ -19,11 +19,15 @@ Added:
- Fix the automated merge of borders.
### Dom
- `vscroll_indicator`. Show a scrollbar indicator on the right.
- `Table()` class to build stylised table.
See https://github.com/ArthurSonzogni/FTXUI/discussions/228
- `vscroll_indicator`. Show a scrollbar indicator on the right.
- `separatorEmpty`. A separator drawing nothing.
- `separatorFixed`. A separator drawing the provided character.
### Component
- `Maybe`: Display an component conditionnally based on a boolean.
- `Dropdown`: A dropdown select list.
- `Maybe`: Display an component conditionnally based on a boolean.
- `Dropdown`: A dropdown select list.
0.9 (2021-09-26)
----------------

View File

@ -69,6 +69,7 @@ add_library(dom
src/ftxui/dom/separator.cpp
src/ftxui/dom/size.cpp
src/ftxui/dom/spinner.cpp
src/ftxui/dom/table.cpp
src/ftxui/dom/text.cpp
src/ftxui/dom/underlined.cpp
src/ftxui/dom/util.cpp

View File

@ -20,6 +20,7 @@ add_executable(tests
src/ftxui/component/terminal_input_parser_test.cpp
src/ftxui/component/toggle_test.cpp
src/ftxui/dom/gauge_test.cpp
src/ftxui/dom/table_test.cpp
src/ftxui/dom/gridbox_test.cpp
src/ftxui/dom/hbox_test.cpp
src/ftxui/dom/text_test.cpp

View File

@ -1,11 +1,8 @@
#include <functional> // for function
#include <iostream> // for basic_ostream::operator<<, operator<<, endl, basic_ostream, basic_ostream<>::__ostream_type, cout, ostream
#include <string> // for string, basic_string, allocator
#include <string> // for basic_string, string, allocator
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Menu
#include "ftxui/component/component_options.hpp" // for MenuOption
#include "ftxui/component/component.hpp" // for Dropdown, Horizontal, Vertical
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
int main(int argc, const char* argv[]) {
@ -14,13 +11,13 @@ int main(int argc, const char* argv[]) {
std::vector<std::string> entries = {
"tribute", "clearance", "ally", "bend", "electronics",
"module", "era", "cultural", "sniff", "nationalism",
"negotiation", "deliver", "figure", "east",
"tribute", "clearance", "ally", "bend", "electronics",
"module", "era", "cultural", "sniff", "nationalism",
"negotiation", "deliver", "figure", "east",
"tribute", "clearance", "ally", "bend", "electronics",
"module", "era", "cultural", "sniff", "nationalism",
"negotiation", "deliver", "figure", "east",
"negotiation", "deliver", "figure", "east", "tribute",
"clearance", "ally", "bend", "electronics", "module",
"era", "cultural", "sniff", "nationalism", "negotiation",
"deliver", "figure", "east", "tribute", "clearance",
"ally", "bend", "electronics", "module", "era",
"cultural", "sniff", "nationalism", "negotiation", "deliver",
"figure", "east",
};
int selected_1 = 0;

View File

@ -1,12 +1,12 @@
#include <functional> // for function
#include <iostream> // for basic_ostream::operator<<, operator<<, endl, basic_ostream, basic_ostream<>::__ostream_type, cout, ostream
#include <memory> // for shared_ptr, __shared_ptr_access
#include <string> // for string, basic_string, allocator
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Menu
#include "ftxui/component/component_options.hpp" // for MenuOption
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/component/component.hpp" // for Checkbox, Maybe, Radiobox, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for Element, operator|, border
using namespace ftxui;
Component Border(Component child) {
@ -14,7 +14,6 @@ Component Border(Component child) {
}
int main(int argc, const char* argv[]) {
std::vector<std::string> entries = {
"entry 1",
"entry 2",

View File

@ -3,9 +3,13 @@ set(DIRECTORY_LIB dom)
example(border)
example(border_style)
example(color_gallery)
example(color_info_palette256)
example(color_truecolor_HSV)
example(color_truecolor_RGB)
example(dbox)
example(gauge)
example(graph)
example(gridbox)
example(hflow)
example(html_like)
example(package_manager)
@ -17,13 +21,10 @@ example(spinner)
example(style_blink)
example(style_bold)
example(style_color)
example(color_truecolor_RGB)
example(color_truecolor_HSV)
example(color_info_palette256)
example(style_dim)
example(gridbox)
example(style_gallery)
example(style_inverted)
example(style_underlined)
example(table)
example(vbox_hbox)
example(window)

66
examples/dom/table.cpp Normal file
View File

@ -0,0 +1,66 @@
#include <ftxui/dom/elements.hpp> // for color, Fit, LIGHT, align_right, bold, DOUBLE
#include <ftxui/dom/table.hpp> // for Table, TableSelection
#include <ftxui/screen/screen.hpp> // for Screen
#include <iostream> // for endl, cout, ostream
#include <string> // for basic_string, allocator, string
#include <vector> // for vector
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/color.hpp" // for Color, Color::Blue, Color::Cyan, Color::White
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto table = Table({
{"Version", "Marketing name", "Release date", "API level", "Runtime"},
{"2.3", "Gingerbread", "February 9 2011", "10", "Dalvik 1.4.0"},
{"4.0", "Ice Cream Sandwich", "October 19 2011", "15", "Dalvik"},
{"4.1", "Jelly Bean", "July 9 2012", "16", "Dalvik"},
{"4.2", "Jelly Bean", "November 13 2012", "17", "Dalvik"},
{"4.3", "Jelly Bean", "July 24 2013", "18", "Dalvik"},
{"4.4", "KitKat", "October 31 2013", "19", "Dalvik and ART"},
{"5.0", "Lollipop", "November 3 2014", "21", "ART"},
{"5.1", "Lollipop", "March 9 2015", "22", "ART"},
{"6.0", "Marshmallow", "October 5 2015", "23", "ART"},
{"7.0", "Nougat", "August 22 2016", "24", "ART"},
{"7.1", "Nougat", "October 4 2016", "25", "ART"},
{"8.0", "Oreo", "August 21 2017", "26", "ART"},
{"8.1", "Oreo", "December 5 2017", "27", "ART"},
{"9", "Pie", "August 6 2018", "28", "ART"},
{"10", "10", "September 3 2019", "29", "ART"},
{"11", "11", "September 8 2020", "30", "ART"},
});
table.SelectAll().Border(LIGHT);
// Add border around the first column.
table.SelectColumn(0).Border(LIGHT);
// Make first row bold with a double border.
table.SelectRow(0).Decorate(bold);
table.SelectRow(0).SeparatorVertical(LIGHT);
table.SelectRow(0).Border(DOUBLE);
// Align right the "Release date" column.
table.SelectColumn(2).DecorateCells(align_right);
// Select row from the second to the last.
auto content = table.SelectRows(1, -1);
// Alternate in between 3 colors.
content.DecorateCellsAlternateRow(color(Color::Blue), 3, 0);
content.DecorateCellsAlternateRow(color(Color::Cyan), 3, 1);
content.DecorateCellsAlternateRow(color(Color::White), 3, 2);
auto document = table.Render();
auto screen = Screen::Create(Dimension::Fit(document));
Render(screen, document);
screen.Print();
std::cout << std::endl;
return 0;
}
// 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

@ -75,6 +75,7 @@ struct Event {
//--- State section ----------------------------------------------------------
ScreenInteractive* screen_ = nullptr;
private:
friend ComponentBase;
friend ScreenInteractive;

View File

@ -17,7 +17,7 @@ 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 };
enum BorderStyle { LIGHT, HEAVY, DOUBLE, ROUNDED, EMPTY };
// Pipe elements into decorator togethers.
// For instance the next lines are equivalents:
@ -34,20 +34,24 @@ Element separator(void);
Element separatorLight();
Element separatorHeavy();
Element separatorDouble();
Element separatorEmpty();
Element separatorStyled(BorderStyle);
Element separator(Pixel);
Element separatorCharacter(std::string);
Element gauge(float ratio);
Element border(Element);
Element borderLight(Element);
Element borderHeavy(Element);
Element borderDouble(Element);
Element borderRounded(Element);
Element borderEmpty(Element);
Decorator borderStyled(BorderStyle);
Decorator borderWith(Pixel);
Element window(Element title, Element content);
Element spinner(int charset_index, size_t image_index);
Elements paragraph(std::string text); // Use inside hflow(). Split by space.
Element graph(GraphFunction);
Element emptyElement();
// -- Decorator ---
Element bold(Element);

View File

@ -0,0 +1,93 @@
#ifndef FTXUI_DOM_TABLE
#define FTXUI_DOM_TABLE
#include <memory>
#include <string> // for string
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for BorderStyle, LIGHT, Element, Decorator
namespace ftxui {
// Usage:
//
// Initialization:
// ---------------
//
// auto table = Table({
// {"X", "Y"},
// {"-1", "1"},
// {"+0", "0"},
// {"+1", "1"},
// });
//
// table.SelectAll().Border(LIGHT);
//
// table.SelectRow(1).Border(DOUBLE);
// table.SelectRow(1).SeparatorInternal(Light);
//
// std::move(table).Element();
class Table;
class TableSelection;
class Table {
public:
Table(std::vector<std::vector<std::string>>);
TableSelection SelectAll();
TableSelection SelectCell(int column, int row);
TableSelection SelectRow(int row_index);
TableSelection SelectRows(int row_min, int row_max);
TableSelection SelectColumn(int column_index);
TableSelection SelectColumns(int column_min, int column_max);
TableSelection SelectRectangle(int column_min,
int column_max,
int row_min,
int row_max);
Element Render();
private:
friend TableSelection;
std::vector<std::vector<Element>> elements_;
int input_dim_x_;
int input_dim_y_;
int dim_x_;
int dim_y_;
};
class TableSelection {
public:
void Decorate(Decorator);
void DecorateAlternateRow(Decorator, int modulo = 2, int shift = 0);
void DecorateAlternateColumn(Decorator, int modulo = 2, int shift = 0);
void DecorateCells(Decorator);
void DecorateCellsAlternateColumn(Decorator, int modulo = 2, int shift = 0);
void DecorateCellsAlternateRow(Decorator, int modulo = 2, int shift = 0);
void Border(BorderStyle border = LIGHT);
void BorderLeft(BorderStyle border = LIGHT);
void BorderRight(BorderStyle border = LIGHT);
void BorderTop(BorderStyle border = LIGHT);
void BorderBottom(BorderStyle border = LIGHT);
void Separator(BorderStyle border = LIGHT);
void SeparatorVertical(BorderStyle border = LIGHT);
void SeparatorHorizontal(BorderStyle border = LIGHT);
private:
friend Table;
Table* table_;
int x_min_;
int x_max_;
int y_min_;
int y_max_;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_DOM_TABLE */
// Copyright 2021 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

@ -1,6 +1,12 @@
#include "ftxui/component/component.hpp"
#include "ftxui/component/component_base.hpp"
#include "ftxui/component/event.hpp"
#include <memory> // for __shared_ptr_access
#include <string> // for string
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Maybe, Checkbox, Make, Radiobox, Vertical, Dropdown
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/component_options.hpp" // for CheckboxOption
#include "ftxui/dom/elements.hpp" // for operator|, Element, border, filler, separator, size, vbox, frame, vscroll_indicator, HEIGHT, LESS_THAN
#include "ftxui/util/ref.hpp" // for ConstStringListRef
namespace ftxui {
@ -52,3 +58,7 @@ Component Dropdown(ConstStringListRef entries, int* selected) {
}
} // namespace ftxui
// Copyright 2021 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

@ -1,13 +1,18 @@
#include "ftxui/component/component.hpp"
#include "ftxui/component/component_base.hpp"
#include "ftxui/component/event.hpp"
#include <memory> // for make_unique, __shared_ptr_access, __shared_ptr_access<>::element_type, shared_ptr
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Make, Maybe
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/dom/elements.hpp" // for Element
#include "ftxui/dom/node.hpp" // for Node
namespace ftxui {
Component Maybe(Component child, bool* show) {
class Impl : public ComponentBase {
public:
Impl(bool* show): show_(show) {}
Impl(bool* show) : show_(show) {}
private:
Element Render() override {
@ -29,3 +34,7 @@ Component Maybe(Component child, bool* show) {
}
} // namespace ftxui
// Copyright 2021 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

@ -24,3 +24,7 @@ Component Maybe(Component child, bool* show) {
};
return Make<Impl>(std::move(child), show);
}
// Copyright 2021 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

@ -14,10 +14,11 @@
namespace ftxui {
static std::string simple_border_charset[6][6] = {
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{" ", " ", " ", " ", " ", " "}, //
};
// For reference, here is the charset for normal border:
@ -124,6 +125,7 @@ class Border : public Node {
/// @see borderLight
/// @see borderDouble
/// @see borderHeavy
/// @see borderEmpty
/// @see borderRounded
///
/// Add a border around an element
@ -174,6 +176,7 @@ Decorator borderStyled(BorderStyle style) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@ -207,6 +210,7 @@ Element borderLight(Element child) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@ -240,6 +244,7 @@ Element borderHeavy(Element child) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@ -273,6 +278,7 @@ Element borderDouble(Element child) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@ -299,6 +305,40 @@ Element borderRounded(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), ROUNDED);
}
/// @brief Draw an empty border around the element.
/// @ingroup dom
/// @see border
/// @see borderLight
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @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 borderEmpty(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), EMPTY);
}
/// @brief Draw window with a title and a border around the element.
/// @param title The title of the window.
/// @param content The element to be wrapped.

View File

@ -10,7 +10,7 @@ namespace ftxui {
/// @return The centered element.
/// @ingroup dom
Element hcenter(Element child) {
return hbox(filler(), std::move(child), filler()) | xflex_grow;
return hbox(filler(), std::move(child), filler());
}
/// @brief Center an element vertically.
@ -18,7 +18,7 @@ Element hcenter(Element child) {
/// @return The centered element.
/// @ingroup dom
Element vcenter(Element child) {
return vbox(filler(), std::move(child), filler()) | yflex_grow;
return vbox(filler(), std::move(child), filler());
}
/// @brief Center an element horizontally and vertically.
@ -26,7 +26,7 @@ Element vcenter(Element child) {
/// @return The centered element.
/// @ingroup dom
Element center(Element child) {
return hcenter(vcenter(std::move(child))) | flex_grow;
return hcenter(vcenter(std::move(child)));
}
/// @brief Align an element on the right side.
@ -34,7 +34,7 @@ Element center(Element child) {
/// @return The right aligned element.
/// @ingroup dom
Element align_right(Element child) {
return hbox(filler(), std::move(child)) | flex_grow;
return hbox(filler(), std::move(child));
}
} // namespace ftxui

View File

@ -1,10 +1,15 @@
#include <algorithm> // For std::max
#include <algorithm> // for max
#include <memory> // for make_shared, __shared_ptr_access
#include <string> // for string
#include <utility> // for move
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp"
#include "ftxui/dom/node.hpp"
#include "ftxui/dom/elements.hpp" // for Element, vscroll_indicator
#include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp"
#include "ftxui/screen/screen.hpp"
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
namespace ftxui {
@ -58,3 +63,7 @@ Element vscroll_indicator(Element child) {
}
} // namespace ftxui
// Copyright 2021 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

@ -12,15 +12,36 @@ namespace ftxui {
using ftxui::Screen;
const std::string charset[][2] = {
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""}, //
{"", ""}, //
{"", ""}, //
{"", ""}, //
{" ", " "}, //
};
class Separator : public Node {
public:
Separator(BorderStyle style) : style_(style) {}
Separator(std::string value) : value_(value) {}
void ComputeRequirement() override {
requirement_.min_x = 1;
requirement_.min_y = 1;
}
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).character = value_;
}
}
}
std::string value_;
};
class SeparatorAuto : public Node {
public:
SeparatorAuto(BorderStyle style) : style_(style) {}
void ComputeRequirement() override {
requirement_.min_x = 1;
@ -43,9 +64,9 @@ class Separator : public Node {
BorderStyle style_;
};
class SeparatorWithPixel : public Separator {
class SeparatorWithPixel : public SeparatorAuto {
public:
SeparatorWithPixel(Pixel pixel) : Separator(LIGHT), pixel_(pixel) {}
SeparatorWithPixel(Pixel pixel) : SeparatorAuto(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) {
@ -58,152 +79,258 @@ class SeparatorWithPixel : public Separator {
Pixel pixel_;
};
/// @brief Draw a vertical or horizontal separator in between two elements.
/// @brief Draw a vertical or horizontal separation in between two other
/// elements.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorHeavy
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("Up"),
/// text("up"),
/// separator(),
/// text("Down"),
/// })
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// Up
/// up
/// ────
/// Down
/// down
/// ```
Element separator() {
return std::make_shared<Separator>(LIGHT);
return std::make_shared<SeparatorAuto>(LIGHT);
}
/// @brief Draw a vertical or horizontal separator in between two elements.
/// @brief Draw a vertical or horizontal separation in between two other
/// elements.
/// @param style the style of the separator.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorHeavy
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("Up"),
/// separatorStyled(BorderStyle::LIGHT),
/// text("Down"),
/// })
/// text("up"),
/// separatorStyled(DOUBLE),
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// Up
/// ────
/// Down
/// up
/// ════
/// down
/// ```
Element separatorStyled(BorderStyle style) {
return std::make_shared<Separator>(style);
return std::make_shared<SeparatorAuto>(style);
}
/// @brief Draw a vertical or horizontal light separator in between two
/// elements.
/// @brief Draw a vertical or horizontal separation in between two other
/// elements, using the LIGHT style.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorHeavy
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("Up"),
/// text("up"),
/// separatorLight(),
/// text("Down"),
/// })
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// Up
/// up
/// ────
/// Down
/// down
/// ```
Element separatorLight() {
return std::make_shared<Separator>(LIGHT);
return std::make_shared<SeparatorAuto>(LIGHT);
}
/// @brief Draw a vertical or horizontal heavy separator in between two
/// elements.
/// @brief Draw a vertical or horizontal separation in between two other
/// elements, using the HEAVY style.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorHeavy
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("Up"),
/// text("up"),
/// separatorHeavy(),
/// text("Down"),
/// })
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// Up
/// up
/// ━━━━
/// Down
/// down
/// ```
Element separatorHeavy() {
return std::make_shared<Separator>(HEAVY);
return std::make_shared<SeparatorAuto>(HEAVY);
}
/// @brief Draw a vertical or horizontal double separator in between two
/// elements.
/// @brief Draw a vertical or horizontal separation in between two other
/// elements, using the DOUBLE style.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorHeavy
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("Up"),
/// text("up"),
/// separatorDouble(),
/// text("Down"),
/// })
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// Up
/// up
/// ════
/// Down
/// down
/// ```
Element separatorDouble() {
return std::make_shared<Separator>(DOUBLE);
return std::make_shared<SeparatorAuto>(DOUBLE);
}
/// @brief Draw a vertical or horizontal separation in between two other
/// elements, using the EMPTY style.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("up"),
/// separator(),
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// up
///
/// down
/// ```
Element separatorEmpty() {
return std::make_shared<SeparatorAuto>(EMPTY);
}
/// @brief Draw a vertical or horizontal separation in between two other
/// elements.
/// @param value the character to fill the separator area.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("up"),
/// separator(),
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// up
/// ────
/// down
/// ```
Element separatorCharacter(std::string value) {
return std::make_shared<Separator>(value);
}
/// @brief Draw a separator in between two element filled with a given pixel.

295
src/ftxui/dom/table.cpp Normal file
View File

@ -0,0 +1,295 @@
#include "ftxui/dom/table.hpp"
#include <algorithm> // for max
#include <memory> // for allocator, shared_ptr, allocator_traits<>::value_type
#include <utility> // for move, swap
#include "ftxui/dom/elements.hpp" // for Element, operator|, text, separatorCharacter, Elements, BorderStyle, Decorator, emptyElement, size, gridbox, EQUAL, flex, flex_shrink, HEIGHT, WIDTH
namespace ftxui {
namespace {
bool IsCell(int x, int y) {
return x % 2 == 1 && y % 2 == 1;
}
static std::string charset[6][6] = {
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{" ", " ", " ", " ", " ", " "}, //
};
int Wrap(int input, int modulo) {
input %= modulo;
input += modulo;
input %= modulo;
return input;
}
void Order(int& a, int& b) {
if (a >= b)
std::swap(a, b);
}
} // namespace
Table::Table(std::vector<std::vector<std::string>> input) {
input_dim_y_ = input.size();
input_dim_x_ = 0;
for (auto& row : input)
input_dim_x_ = std::max(input_dim_x_, (int)row.size());
dim_y_ = 2 * input_dim_y_ + 1;
dim_x_ = 2 * input_dim_x_ + 1;
// Reserve space.
elements_.resize(dim_y_);
for (int y = 0; y < dim_y_; ++y)
elements_[y].resize(dim_x_);
// Transfert elements_ from |input| toward |elements_|.
{
int y = 1;
for (auto& row : input) {
int x = 1;
for (auto& cell : row) {
elements_[y][x] = text(cell);
x += 2;
}
y += 2;
}
}
// Add empty element for the border.
for (int y = 0; y < dim_y_; ++y) {
for (int x = 0; x < dim_x_; ++x) {
auto& element = elements_[y][x];
if (IsCell(x, y)) {
if (!element)
element = emptyElement();
continue;
}
element = emptyElement();
}
}
}
TableSelection Table::SelectRow(int index) {
return SelectRectangle(0, -1, index, index);
}
TableSelection Table::SelectRows(int row_min, int row_max) {
return SelectRectangle(0, -1, row_min, row_max);
}
TableSelection Table::SelectColumn(int index) {
return SelectRectangle(index, index, 0, -1);
}
TableSelection Table::SelectColumns(int column_min, int column_max) {
return SelectRectangle(column_min, column_max, 0, -1);
}
TableSelection Table::SelectCell(int column, int row) {
return SelectRectangle(column, column, row, row);
}
TableSelection Table::SelectRectangle(int column_min,
int column_max,
int row_min,
int row_max) {
column_min = Wrap(column_min, input_dim_x_);
column_max = Wrap(column_max, input_dim_x_);
Order(column_min, column_max);
row_min = Wrap(row_min, input_dim_y_);
row_max = Wrap(row_max, input_dim_y_);
Order(row_min, row_max);
TableSelection output;
output.table_ = this;
output.x_min_ = 2 * column_min;
output.x_max_ = 2 * column_max + 2;
output.y_min_ = 2 * row_min;
output.y_max_ = 2 * row_max + 2;
return output;
}
TableSelection Table::SelectAll() {
TableSelection output;
output.table_ = this;
output.x_min_ = 0;
output.x_max_ = dim_x_ - 1;
output.y_min_ = 0;
output.y_max_ = dim_y_ - 1;
return output;
}
Element Table::Render() {
for (int y = 0; y < dim_y_; ++y) {
for (int x = 0; x < dim_x_; ++x) {
auto& it = elements_[y][x];
// Line
if ((x + y) % 2 == 1) {
it = std::move(it) | flex;
continue;
}
// Cells
if ((x % 2) == 1 && (y % 2) == 1) {
it = std::move(it) | flex_shrink;
continue;
}
// Corners
it = std::move(it) | size(WIDTH, EQUAL, 0) | size(HEIGHT, EQUAL, 0);
}
}
return gridbox(std::move(elements_));
}
void TableSelection::Decorate(Decorator decorator) {
for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
void TableSelection::DecorateCells(Decorator decorator) {
for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
if (y % 2 && x % 2) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
}
void TableSelection::DecorateAlternateColumn(Decorator decorator,
int modulo,
int shift) {
for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
if (y % 2 && (x / 2) % modulo == shift) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
}
void TableSelection::DecorateAlternateRow(Decorator decorator,
int modulo,
int shift) {
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
if (y % 2 && (y / 2) % modulo == shift) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
}
void TableSelection::DecorateCellsAlternateColumn(Decorator decorator,
int modulo,
int shift) {
for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
if (y % 2 && x % 2 && ((x / 2) % modulo == shift)) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
}
void TableSelection::DecorateCellsAlternateRow(Decorator decorator,
int modulo,
int shift) {
for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
if (y % 2 && x % 2 && ((y / 2) % modulo == shift)) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
}
void TableSelection::Border(BorderStyle style) {
BorderLeft(style);
BorderRight(style);
BorderTop(style);
BorderBottom(style);
table_->elements_[y_min_][x_min_] = text(charset[style][0]);
table_->elements_[y_min_][x_max_] = text(charset[style][1]);
table_->elements_[y_max_][x_min_] = text(charset[style][2]);
table_->elements_[y_max_][x_max_] = text(charset[style][3]);
}
void TableSelection::Separator(BorderStyle style) {
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
if (y % 2 == 0 || x % 2 == 0) {
Element& e = table_->elements_[y][x];
e = (y % 2) ? separatorCharacter(charset[style][5])
: separatorCharacter(charset[style][4]);
}
}
}
}
void TableSelection::SeparatorVertical(BorderStyle style) {
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
if (x % 2 == 0) {
table_->elements_[y][x] = text(charset[style][5]);
}
}
}
}
void TableSelection::SeparatorHorizontal(BorderStyle style) {
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
if (y % 2 == 0) {
table_->elements_[y][x] = text(charset[style][4]);
}
}
}
}
void TableSelection::BorderLeft(BorderStyle style) {
for (int y = y_min_; y <= y_max_; y++)
table_->elements_[y][x_min_] = separatorCharacter(charset[style][5]);
}
void TableSelection::BorderRight(BorderStyle style) {
for (int y = y_min_; y <= y_max_; y++)
table_->elements_[y][x_max_] = separatorCharacter(charset[style][5]);
}
void TableSelection::BorderTop(BorderStyle style) {
for (int x = x_min_; x <= x_max_; x++)
table_->elements_[y_min_][x] = separatorCharacter(charset[style][4]);
}
void TableSelection::BorderBottom(BorderStyle style) {
for (int x = x_min_; x <= x_max_; x++)
table_->elements_[y_max_][x] = separatorCharacter(charset[style][4]);
}
} // namespace ftxui
// Copyright 2021 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

@ -0,0 +1,716 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <memory> // for allocator
#include "ftxui/dom/elements.hpp" // for LIGHT, flex, center, EMPTY, DOUBLE
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/dom/table.hpp"
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
using namespace ftxui;
TEST(TableTest, Empty) {
auto table = Table({});
Screen screen(5, 5);
Render(screen, table.Render());
EXPECT_EQ(
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, Basic) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"abcd \r\n"
"efgh \r\n"
"ijkl \r\n"
"mnop \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorVerticalEmpty) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorVertical(EMPTY);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a b c d \r\n"
"e f g h \r\n"
"i j k l \r\n"
"m n o p \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorHorizontalEmpty) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorHorizontal(EMPTY);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"abcd \r\n"
" \r\n"
"efgh \r\n"
" \r\n"
"ijkl \r\n"
" \r\n"
"mnop \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorHorizontalLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorHorizontal(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"abcd \r\n"
"──── \r\n"
"efgh \r\n"
"──── \r\n"
"ijkl \r\n"
"──── \r\n"
"mnop \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorVerticalLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorVertical(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a│b│c│d \r\n"
"e│f│g│h \r\n"
"i│j│k│l \r\n"
"m│n│o│p \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Separator(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a│b│c│d \r\n"
"─┼─┼─┼─ \r\n"
"e│f│g│h \r\n"
"─┼─┼─┼─ \r\n"
"i│j│k│l \r\n"
"─┼─┼─┼─ \r\n"
"m│n│o│p \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorVerticalHorizontalLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorVertical(LIGHT);
table.SelectAll().SeparatorHorizontal(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a│b│c│d \r\n"
"─┼─┼─┼─ \r\n"
"e│f│g│h \r\n"
"─┼─┼─┼─ \r\n"
"i│j│k│l \r\n"
"─┼─┼─┼─ \r\n"
"m│n│o│p \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorHorizontalVerticalLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorHorizontal(LIGHT);
table.SelectAll().SeparatorVertical(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a│b│c│d \r\n"
"─┼─┼─┼─ \r\n"
"e│f│g│h \r\n"
"─┼─┼─┼─ \r\n"
"i│j│k│l \r\n"
"─┼─┼─┼─ \r\n"
"m│n│o│p \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, BorderLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌────┐ \r\n"
"│abcd│ \r\n"
"│efgh│ \r\n"
"│ijkl│ \r\n"
"│mnop│ \r\n"
"└────┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, BorderSeparatorLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬─┬─┬─┐ \r\n"
"│a│b│c│d│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│e│f│g│h│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│i│j│k│l│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│m│n│o│p│ \r\n"
"└─┴─┴─┴─┘ \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectRow) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRow(1).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" abcd \r\n"
"┌────┐ \r\n"
"│efgh│ \r\n"
"└────┘ \r\n"
" ijkl \r\n"
" mnop \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectRowNegative) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRow(-2).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" abcd \r\n"
" efgh \r\n"
"┌────┐ \r\n"
"│ijkl│ \r\n"
"└────┘ \r\n"
" mnop \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectColumn) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectColumn(1).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌─┐ \r\n"
"a│b│cd \r\n"
"e│f│gh \r\n"
"i│j│kl \r\n"
"m│n│op \r\n"
" └─┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectColumnNegative) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectColumn(-2).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌─┐ \r\n"
"ab│c│d \r\n"
"ef│g│h \r\n"
"ij│k│l \r\n"
"mn│o│p \r\n"
" └─┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, CrossingBorders) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRow(1).Border(LIGHT);
table.SelectColumn(1).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌─┐ \r\n"
" a│b│cd \r\n"
"┌─┼─┼──┐ \r\n"
"│e│f│gh│ \r\n"
"└─┼─┼──┘ \r\n"
" i│j│kl \r\n"
" m│n│op \r\n"
" └─┘ \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, CrossingBordersLightAndDouble) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRow(1).Border(LIGHT);
table.SelectColumn(1).Border(DOUBLE);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ╔═╗ \r\n"
" a║b║cd \r\n"
"┌─╫─╫──┐ \r\n"
"│e║f║gh│ \r\n"
"└─╫─╫──┘ \r\n"
" i║j║kl \r\n"
" m║n║op \r\n"
" ╚═╝ \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectColumns) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectColumns(1, 2).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌──┐ \r\n"
"a│bc│d \r\n"
"e│fg│h \r\n"
"i│jk│l \r\n"
"m│no│p \r\n"
" └──┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectRows) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRows(1, 2).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" abcd \r\n"
"┌────┐ \r\n"
"│efgh│ \r\n"
"│ijkl│ \r\n"
"└────┘ \r\n"
" mnop \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectRectangle) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRectangle(1, 2, 1, 2).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a bc d \r\n"
" ┌──┐ \r\n"
"e│fg│h \r\n"
"i│jk│l \r\n"
" └──┘ \r\n"
"m no p \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectColumnsNegative) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectColumns(1, -1).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌───┐ \r\n"
"a│bcd│ \r\n"
"e│fgh│ \r\n"
"i│jkl│ \r\n"
"m│nop│ \r\n"
" └───┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectInverted) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectColumns(-1, 1).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌───┐ \r\n"
"a│bcd│ \r\n"
"e│fgh│ \r\n"
"i│jkl│ \r\n"
"m│nop│ \r\n"
" └───┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, ColumnFlex) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectColumn(1).Decorate(flex);
Screen screen(20, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬────────────┬─┬─┐\r\n"
"│a│b │c│d│\r\n"
"├─┼────────────┼─┼─┤\r\n"
"│e│f │g│h│\r\n"
"├─┼────────────┼─┼─┤\r\n"
"│i│j │k│l│\r\n"
"├─┼────────────┼─┼─┤\r\n"
"│m│n │o│p│\r\n"
"└─┴────────────┴─┴─┘\r\n"
" ",
screen.ToString());
}
TEST(TableTest, ColumnFlexCenter) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectColumn(1).Decorate(flex);
table.SelectColumn(1).DecorateCells(center);
Screen screen(20, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬─┬─┬─┐ \r\n"
"│a│b│c│d│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│e│f│g│h│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│i│j│k│l│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│m│n│o│p│ \r\n"
"└─┴─┴─┴─┘ \r\n"
" ",
screen.ToString());
}
TEST(TableTest, ColumnCenter) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectColumn(1).DecorateCells(center);
Screen screen(20, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬─┬─┬─┐ \r\n"
"│a│b│c│d│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│e│f│g│h│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│i│j│k│l│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│m│n│o│p│ \r\n"
"└─┴─┴─┴─┘ \r\n"
" ",
screen.ToString());
}
TEST(TableTest, ColumnFlexTwo) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectColumn(1).Decorate(flex);
table.SelectColumn(3).Decorate(flex);
Screen screen(20, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬──────┬─┬───────┐\r\n"
"│a│b │c│d │\r\n"
"├─┼──────┼─┼───────┤\r\n"
"│e│f │g│h │\r\n"
"├─┼──────┼─┼───────┤\r\n"
"│i│j │k│l │\r\n"
"├─┼──────┼─┼───────┤\r\n"
"│m│n │o│p │\r\n"
"└─┴──────┴─┴───────┘\r\n"
" ",
screen.ToString());
}
TEST(TableTest, RowFlex) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectRow(1).Decorate(flex);
Screen screen(10, 20);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬─┬─┬─┐ \r\n"
"│a│b│c│d│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│e│f│g│h│ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│i│j│k│l│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│m│n│o│p│ \r\n"
"└─┴─┴─┴─┘ ",
screen.ToString());
}
TEST(TableTest, RowFlexTwo) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectRow(1).Decorate(flex);
table.SelectRow(3).Decorate(flex);
Screen screen(10, 20);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬─┬─┬─┐ \r\n"
"│a│b│c│d│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│e│f│g│h│ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│i│j│k│l│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│m│n│o│p│ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"└─┴─┴─┴─┘ ",
screen.ToString());
}
// Copyright 2021 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

@ -75,6 +75,18 @@ Dimensions Dimension::Fit(Element& e) {
std::min(e->requirement().min_y, size.dimy)};
}
/// An element of size 0x0 drawing nothing.
/// @ingroup dom
Element emptyElement() {
class Impl : public Node {
void ComputeRequirement() override {
requirement_.min_x = 0;
requirement_.min_x = 0;
}
};
return std::make_unique<Impl>();
}
} // namespace ftxui
// Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -1,4 +1,3 @@
#include <stdint.h> // for uint16_t
#include <iostream> // for operator<<, stringstream, basic_ostream, flush, cout, ostream
#include <map> // for _Rb_tree_const_iterator, map, operator!=, operator==
#include <memory> // for allocator, allocator_traits<>::value_type