Implement flexbox (#277)

This implement the flexbox elements, following the HTML one.

Built from them, there is also the following elements:
- `paragraph`
- `paragraphAlignLeft`
- `paragraphAlignRight`
- `paragraphAlignCenter`
- `paragraphAlignJustify`

This is a breaking change.
This commit is contained in:
Arthur Sonzogni
2021-12-11 17:58:25 +01:00
committed by GitHub
parent f7c6bf91a7
commit 602392c43d
70 changed files with 2163 additions and 300 deletions

View File

@@ -1,5 +1,5 @@
#include <stddef.h> // for size_t
#include <algorithm> // for find_if, max
#include <algorithm> // for find_if
#include <cassert> // for assert
#include <iterator> // for begin, end
#include <utility> // for move
@@ -7,7 +7,7 @@
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
#include "ftxui/component/component.hpp"
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/component_base.hpp" // for ComponentBase, Components
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for text, Element

View File

@@ -6,7 +6,7 @@
#include "ftxui/component/component.hpp" // for Horizontal, Vertical, Tab
#include "ftxui/component/component_base.hpp" // for Components, Component, ComponentBase
#include "ftxui/component/event.hpp" // for Event, Event::Tab, Event::TabReverse, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp
#include "ftxui/component/event.hpp" // for Event, Event::Tab, Event::TabReverse, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp, Event::End, Event::Home, Event::PageDown, Event::PageUp
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::WheelDown, Mouse::WheelUp
#include "ftxui/dom/elements.hpp" // for text, Elements, operator|, reflect, Element, hbox, vbox
#include "ftxui/screen/box.hpp" // for Box
@@ -102,11 +102,11 @@ class VerticalContainer : public ContainerBase {
if (event == Event::ArrowDown || event == Event::Character('j'))
MoveSelector(+1);
if (event == Event::PageUp) {
for(int i = 0; i<box_.y_max - box_.y_min; ++i)
for (int i = 0; i < box_.y_max - box_.y_min; ++i)
MoveSelector(-1);
}
if (event == Event::PageDown) {
for(int i = 0; i<box_.y_max - box_.y_min; ++i)
for (int i = 0; i < box_.y_max - box_.y_min; ++i)
MoveSelector(1);
}
if (event == Event::Home) {

View File

@@ -1,7 +1,7 @@
#include <cmath>
#include <memory> // for __shared_ptr_access
#include <string> // for string
#include <utility> // for move
#include <algorithm> // for max, min
#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

View File

@@ -6,7 +6,7 @@
#include <iostream> // for cout, ostream, basic_ostream, operator<<, endl, flush
#include <stack> // for stack
#include <thread> // for thread
#include <utility> // for move
#include <utility> // for swap, move
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
@@ -18,7 +18,7 @@
#include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputParser
#include "ftxui/dom/node.hpp" // for Node, Render
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/terminal.hpp" // for Terminal::Dimensions, Terminal
#include "ftxui/screen/terminal.hpp" // for Size, Dimensions
#if defined(_WIN32)
#define DEFINE_CONSOLEV2_PROPERTIES
@@ -32,8 +32,8 @@
#endif
#else
#include <sys/select.h> // for select, FD_ISSET, FD_SET, FD_ZERO, fd_set
#include <termios.h> // for tcsetattr, termios, tcgetattr, TCSANOW, cc_t, ECHO, ICANON, VMIN, VTIME
#include <unistd.h> // for STDIN_FILENO, read
#include <termios.h> // for tcsetattr, tcgetattr, cc_t
#include <unistd.h> // for STDIN_FILENO, read
#endif
// Quick exit is missing in standard CLang headers

View File

@@ -1,9 +1,9 @@
#include <benchmark/benchmark.h>
#include "ftxui/dom/elements.hpp" // for separator, gauge, operator|, text, Element, blink, inverted, hbox, vbox, border
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/screen.hpp" // for Screen
#include "ftxui/dom/flexbox_config.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
using namespace ftxui;

View File

@@ -23,7 +23,6 @@ class Blink : public NodeDecorator {
}
};
/// @brief The text drawn alternates in between visible and hidden.
/// @ingroup dom
Element blink(Element child) {

View File

@@ -1,12 +1,12 @@
#include <algorithm> // for max
#include <iterator> // for begin, end
#include <memory> // for allocator, make_shared, __shared_ptr_access
#include <string> // for basic_string, string
#include <string> // for string, basic_string
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, Elements, border, borderWith, window
#include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, BorderStyle, ROUNDED, Elements, DOUBLE, EMPTY, HEAVY, LIGHT, border, borderDouble, borderEmpty, borderHeavy, borderLight, borderRounded, borderStyled, borderWith, window
#include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Pixel, Screen

View File

@@ -25,4 +25,4 @@ void Compute(std::vector<Element>* elements, int target_size);
// 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.
// the LICENSE file.line.

View File

@@ -1,9 +1,9 @@
#include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for Element, unpack, filler, flex, flex_grow, flex_shrink, notflex, xflex, xflex_grow, xflex_shrink, yflex, yflex_grow, yflex_shrink
#include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node.hpp" // for Elements, Node
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box

239
src/ftxui/dom/flexbox.cpp Normal file
View File

@@ -0,0 +1,239 @@
#include <stddef.h> // for size_t
#include <algorithm> // for min, max
#include <memory> // for __shared_ptr_access, shared_ptr, allocator_traits<>::value_type, make_shared
#include <utility> // for move, swap
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for Element, Elements, flexbox, hflow, vflow
#include "ftxui/dom/flexbox_config.hpp" // for FlexboxConfig, FlexboxConfig::Direction, FlexboxConfig::Direction::Column, FlexboxConfig::AlignContent, FlexboxConfig::Direction::ColumnInversed, FlexboxConfig::Direction::Row, FlexboxConfig::JustifyContent, FlexboxConfig::Wrap, FlexboxConfig::AlignContent::FlexStart, FlexboxConfig::Direction::RowInversed, FlexboxConfig::JustifyContent::FlexStart, FlexboxConfig::Wrap::Wrap
#include "ftxui/dom/flexbox_helper.hpp" // for Block, Global, Compute
#include "ftxui/dom/node.hpp" // for Node, Elements, Node::Status
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
namespace ftxui {
namespace {
void Normalize(FlexboxConfig::Direction& direction) {
switch (direction) {
case FlexboxConfig::Direction::Row:
case FlexboxConfig::Direction::RowInversed: {
direction = FlexboxConfig::Direction::Row;
} break;
case FlexboxConfig::Direction::Column:
case FlexboxConfig::Direction::ColumnInversed: {
direction = FlexboxConfig::Direction::Column;
} break;
}
}
void Normalize(FlexboxConfig::AlignContent& align_content) {
align_content = FlexboxConfig::AlignContent::FlexStart;
}
void Normalize(FlexboxConfig::JustifyContent& justify_content) {
justify_content = FlexboxConfig::JustifyContent::FlexStart;
}
void Normalize(FlexboxConfig::Wrap& wrap) {
wrap = FlexboxConfig::Wrap::Wrap;
}
FlexboxConfig Normalize(FlexboxConfig config) {
Normalize(config.direction);
Normalize(config.wrap);
Normalize(config.justify_content);
Normalize(config.align_content);
return config;
}
class Flexbox : public Node {
public:
Flexbox(Elements children, FlexboxConfig config)
: Node(std::move(children)),
config_(config),
config_normalized_(Normalize(config)) {
requirement_.flex_grow_x = 1;
requirement_.flex_grow_y = 0;
if (IsColumnOriented())
std::swap(requirement_.flex_grow_x, requirement_.flex_grow_y);
}
bool IsColumnOriented() {
return config_.direction == FlexboxConfig::Direction::Column ||
config_.direction == FlexboxConfig::Direction::ColumnInversed;
}
void Layout(flexbox_helper::Global& global,
bool compute_requirement = false) {
for (auto& child : children_) {
flexbox_helper::Block block;
block.min_size_x = child->requirement().min_x;
block.min_size_y = child->requirement().min_y;
if (!compute_requirement) {
block.flex_grow_x = child->requirement().flex_grow_x;
block.flex_grow_y = child->requirement().flex_grow_y;
block.flex_shrink_x = child->requirement().flex_shrink_x;
block.flex_shrink_y = child->requirement().flex_shrink_y;
}
global.blocks.push_back(block);
}
flexbox_helper::Compute(global);
}
void ComputeRequirement() override {
for (auto& child : children_)
child->ComputeRequirement();
flexbox_helper::Global global;
global.config = config_normalized_;
if (IsColumnOriented()) {
global.size_x = 100000;
global.size_y = asked_;
} else {
global.size_x = asked_;
global.size_y = 100000;
}
Layout(global, true);
if (global.blocks.size() == 0) {
requirement_.min_x = 0;
requirement_.min_y = 0;
return;
}
Box box;
box.x_min = global.blocks[0].x;
box.y_min = global.blocks[0].y;
box.x_max = global.blocks[0].x + global.blocks[0].dim_x;
box.y_max = global.blocks[0].y + global.blocks[0].dim_y;
for (auto& b : global.blocks) {
box.x_min = std::min(box.x_min, b.x);
box.y_min = std::min(box.y_min, b.y);
box.x_max = std::max(box.x_max, b.x + b.dim_x);
box.y_max = std::max(box.y_max, b.y + b.dim_y);
}
requirement_.min_x = box.x_max - box.x_min;
requirement_.min_y = box.y_max - box.y_min;
}
void SetBox(Box box) override {
Node::SetBox(box);
asked_ = std::min(asked_, IsColumnOriented() ? box.y_max - box.y_min + 1
: box.x_max - box.x_min + 1);
flexbox_helper::Global global;
global.config = config_;
global.size_x = box.x_max - box.x_min + 1;
global.size_y = box.y_max - box.y_min + 1;
Layout(global);
need_iteration_ = false;
for (size_t i = 0; i < children_.size(); ++i) {
auto& child = children_[i];
auto& b = global.blocks[i];
Box children_box;
children_box.x_min = box.x_min + b.x;
children_box.y_min = box.y_min + b.y;
children_box.x_max = box.x_min + b.x + b.dim_x - 1;
children_box.y_max = box.y_min + b.y + b.dim_y - 1;
Box intersection = Box::Intersection(children_box, box);
child->SetBox(intersection);
need_iteration_ |= (intersection != children_box);
}
}
void Check(Status* status) override {
for (auto& child : children_)
child->Check(status);
if (status->iteration == 0) {
asked_ = 6000;
need_iteration_ = true;
}
status->need_iteration |= need_iteration_;
}
int asked_ = 6000;
bool need_iteration_ = true;
const FlexboxConfig config_;
const FlexboxConfig config_normalized_;
};
} // namespace
/// @brief A container displaying elements on row/columns and capable of
/// wrapping on the next column/row when full.
/// @param children The elements in the container
/// @param config The option
/// @return The container.
///
/// #### Example
///
/// ```cpp
/// flexbox({
/// text("element 1"),
/// text("element 2"),
/// text("element 3"),
/// }, FlexboxConfig()
// .Set(FlexboxConfig::Direction::Column)
// .Set(FlexboxConfig::Wrap::WrapInversed)
// .SetGapMainAxis(1)
// .SetGapCrossAxis(1)
// )
/// ```
Element flexbox(Elements children, FlexboxConfig config) {
return std::make_shared<Flexbox>(std::move(children), std::move(config));
}
/// @brief A container displaying elements in rows from left to right. When
/// filled, it starts on a new row below.
/// @param children The elements in the container
/// @return The container.
///
/// #### Example
///
/// ```cpp
/// hflow({
/// text("element 1"),
/// text("element 2"),
/// text("element 3"),
/// });
/// ```
Element hflow(Elements children) {
return flexbox(std::move(children), FlexboxConfig());
}
/// @brief A container displaying elements in rows from top to bottom. When
/// filled, it starts on a new columns on the right.
/// filled, it starts on a new row.
/// is full, it starts a new row.
/// @param children The elements in the container
/// @return The container.
///
/// #### Example
///
/// ```cpp
/// vflow({
/// text("element 1"),
/// text("element 2"),
/// text("element 3"),
/// });
/// ```
Element vflow(Elements children) {
return flexbox(std::move(children),
FlexboxConfig().Set(FlexboxConfig::Direction::Column));
}
} // namespace ftxui
// 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

@@ -0,0 +1,40 @@
#include "ftxui/dom/flexbox_config.hpp"
namespace ftxui {
FlexboxConfig& FlexboxConfig::Set(FlexboxConfig::Direction d) {
this->direction = d;
return *this;
}
FlexboxConfig& FlexboxConfig::Set(FlexboxConfig::Wrap w) {
this->wrap = w;
return *this;
}
FlexboxConfig& FlexboxConfig::Set(FlexboxConfig::JustifyContent j) {
this->justify_content = j;
return *this;
}
FlexboxConfig& FlexboxConfig::Set(FlexboxConfig::AlignItems a) {
this->align_items = a;
return *this;
}
FlexboxConfig& FlexboxConfig::Set(FlexboxConfig::AlignContent a) {
this->align_content = a;
return *this;
}
FlexboxConfig& FlexboxConfig::SetGap(int x, int y) {
this->gap_x = x;
this->gap_y = y;
return *this;
}
} // namespace ftxui
// 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

@@ -0,0 +1,325 @@
#include "ftxui/dom/flexbox_helper.hpp"
#include <stddef.h> // for size_t
#include <algorithm> // for min, max
#include <memory> // for allocator_traits<>::value_type
#include <utility> // for swap, move
#include "ftxui/dom/box_helper.hpp" // for Element, Compute
namespace ftxui {
namespace flexbox_helper {
namespace {
void SymmetryXY(FlexboxConfig& c) {
std::swap(c.gap_x, c.gap_y);
switch (c.direction) {
case FlexboxConfig::Direction::Row:
c.direction = FlexboxConfig::Direction::Column;
break;
case FlexboxConfig::Direction::RowInversed:
c.direction = FlexboxConfig::Direction::ColumnInversed;
break;
case FlexboxConfig::Direction::Column:
c.direction = FlexboxConfig::Direction::Row;
break;
case FlexboxConfig::Direction::ColumnInversed:
c.direction = FlexboxConfig::Direction::RowInversed;
break;
}
}
void SymmetryX(FlexboxConfig& c) {
switch (c.direction) {
case FlexboxConfig::Direction::Row:
c.direction = FlexboxConfig::Direction::RowInversed;
break;
case FlexboxConfig::Direction::RowInversed:
c.direction = FlexboxConfig::Direction::Row;
break;
default:
break;
}
}
void SymmetryY(FlexboxConfig& c) {
switch (c.wrap) {
case FlexboxConfig::Wrap::NoWrap:
break;
case FlexboxConfig::Wrap::Wrap:
c.wrap = FlexboxConfig::Wrap::WrapInversed;
break;
case FlexboxConfig::Wrap::WrapInversed:
c.wrap = FlexboxConfig::Wrap::Wrap;
break;
}
}
void SymmetryXY(Global& g) {
SymmetryXY(g.config);
std::swap(g.size_x, g.size_y);
for (auto& b : g.blocks) {
std::swap(b.min_size_x, b.min_size_y);
std::swap(b.flex_grow_x, b.flex_grow_y);
std::swap(b.flex_shrink_x, b.flex_shrink_y);
std::swap(b.x, b.y);
std::swap(b.dim_x, b.dim_y);
}
}
void SymmetryX(Global& g) {
SymmetryX(g.config);
for (auto& b : g.blocks) {
b.x = g.size_x - b.x - b.dim_x;
}
}
void SymmetryY(Global& g) {
SymmetryY(g.config);
for (auto& b : g.blocks) {
b.y = g.size_y - b.y - b.dim_y;
}
}
struct Line {
std::vector<Block*> blocks;
};
void SetX(Global& global, std::vector<Line> lines) {
for (auto& line : lines) {
std::vector<box_helper::Element> elements;
for (auto* block : line.blocks) {
box_helper::Element element;
element.min_size = block->min_size_x;
element.flex_grow =
block->flex_grow_x || global.config.justify_content ==
FlexboxConfig::JustifyContent::Stretch;
element.flex_shrink = block->flex_shrink_x;
elements.push_back(element);
}
box_helper::Compute(
&elements,
global.size_x - global.config.gap_x * (line.blocks.size() - 1));
int x = 0;
for (size_t i = 0; i < line.blocks.size(); ++i) {
line.blocks[i]->dim_x = elements[i].size;
line.blocks[i]->x = x;
x += elements[i].size;
x += global.config.gap_x;
}
}
}
void SetY(Global& g, std::vector<Line> lines) {
std::vector<box_helper::Element> elements;
for (auto& line : lines) {
box_helper::Element element;
element.flex_shrink = line.blocks.front()->flex_shrink_y;
element.flex_grow = line.blocks.front()->flex_grow_y;
for (auto* block : line.blocks) {
element.min_size = std::max(element.min_size, block->min_size_y);
element.flex_shrink = std::min(element.flex_shrink, block->flex_shrink_y);
element.flex_grow = std::min(element.flex_grow, block->flex_grow_y);
}
elements.push_back(element);
}
// box_helper::Compute(&elements, g.size_y);
box_helper::Compute(&elements, 10000);
// [Align-content]
std::vector<int> ys(elements.size());
int y = 0;
for (size_t i = 0; i < elements.size(); ++i) {
ys[i] = y;
y += elements[i].size;
y += g.config.gap_y;
}
int remaining_space = std::max(0, g.size_y - y);
switch (g.config.align_content) {
case FlexboxConfig::AlignContent::FlexStart: {
} break;
case FlexboxConfig::AlignContent::FlexEnd: {
for (size_t i = 0; i < ys.size(); ++i)
ys[i] += remaining_space;
} break;
case FlexboxConfig::AlignContent::Center: {
for (size_t i = 0; i < ys.size(); ++i)
ys[i] += remaining_space / 2;
} break;
case FlexboxConfig::AlignContent::Stretch: {
for (int i = ys.size() - 1; i >= 0; --i) {
int shifted = remaining_space * (i + 0) / (i + 1);
ys[i] += shifted;
int consumed = remaining_space - shifted;
elements[i].size += consumed;
remaining_space -= consumed;
}
} break;
case FlexboxConfig::AlignContent::SpaceBetween: {
for (int i = ys.size() - 1; i >= 1; --i) {
ys[i] += remaining_space;
remaining_space = remaining_space * (i - 1) / i;
}
} break;
case FlexboxConfig::AlignContent::SpaceAround: {
for (int i = ys.size() - 1; i >= 0; --i) {
ys[i] += remaining_space * (2 * i + 1) / (2 * i + 2);
remaining_space = remaining_space * (2 * i) / (2 * i + 2);
}
} break;
case FlexboxConfig::AlignContent::SpaceEvenly: {
for (int i = ys.size() - 1; i >= 0; --i) {
ys[i] += remaining_space * (i + 1) / (i + 2);
remaining_space = remaining_space * (i + 1) / (i + 2);
}
} break;
}
// [Align items]
for (size_t i = 0; i < lines.size(); ++i) {
auto& element = elements[i];
for (auto* block : lines[i].blocks) {
bool stretch =
block->flex_grow_y ||
g.config.align_content == FlexboxConfig::AlignContent::Stretch;
int size =
stretch ? element.size : std::min(element.size, block->min_size_y);
switch (g.config.align_items) {
case FlexboxConfig::AlignItems::FlexStart: {
block->y = ys[i];
block->dim_y = size;
} break;
case FlexboxConfig::AlignItems::Center: {
block->y = ys[i] + (element.size - size) / 2;
block->dim_y = size;
} break;
case FlexboxConfig::AlignItems::FlexEnd: {
block->y = ys[i] + element.size - size;
block->dim_y = size;
} break;
case FlexboxConfig::AlignItems::Stretch: {
block->y = ys[i];
block->dim_y = element.size;
} break;
}
}
}
}
void JustifyContent(Global& g, std::vector<Line> lines) {
for (auto& line : lines) {
Block* last = line.blocks.back();
int remaining_space = g.size_x - last->x - last->dim_x;
switch (g.config.justify_content) {
case FlexboxConfig::JustifyContent::FlexStart:
case FlexboxConfig::JustifyContent::Stretch:
break;
case FlexboxConfig::JustifyContent::FlexEnd: {
for (auto* block : line.blocks)
block->x += remaining_space;
} break;
case FlexboxConfig::JustifyContent::Center: {
for (auto* block : line.blocks)
block->x += remaining_space / 2;
} break;
case FlexboxConfig::JustifyContent::SpaceBetween: {
for (int i = line.blocks.size() - 1; i >= 1; --i) {
line.blocks[i]->x += remaining_space;
remaining_space = remaining_space * (i - 1) / i;
}
} break;
case FlexboxConfig::JustifyContent::SpaceAround: {
for (int i = line.blocks.size() - 1; i >= 0; --i) {
line.blocks[i]->x += remaining_space * (2 * i + 1) / (2 * i + 2);
remaining_space = remaining_space * (2 * i) / (2 * i + 2);
}
} break;
case FlexboxConfig::JustifyContent::SpaceEvenly: {
for (int i = line.blocks.size() - 1; i >= 0; --i) {
line.blocks[i]->x += remaining_space * (i + 1) / (i + 2);
remaining_space = remaining_space * (i + 1) / (i + 2);
}
} break;
}
}
}
} // namespace
void Compute(Global& global) {
if (global.config.direction == FlexboxConfig::Direction::Column ||
global.config.direction == FlexboxConfig::Direction::ColumnInversed) {
SymmetryXY(global);
Compute(global);
SymmetryXY(global);
return;
}
if (global.config.direction == FlexboxConfig::Direction::RowInversed) {
SymmetryX(global);
Compute(global);
SymmetryX(global);
return;
}
if (global.config.wrap == FlexboxConfig::Wrap::WrapInversed) {
SymmetryY(global);
Compute(global);
SymmetryY(global);
return;
}
// Step 1: Lay out every elements into rows:
std::vector<Line> lines;
{
Line line;
int x = 0;
for (auto& block : global.blocks) {
// Does it fit the end of the row?
// No? Then we need to start a new one:
if (x + block.min_size_x > global.size_x) {
x = 0;
if (!line.blocks.empty())
lines.push_back(std::move(line));
line = Line();
}
block.line = lines.size();
block.line_position = line.blocks.size();
line.blocks.push_back(&block);
x += block.min_size_x + global.config.gap_x;
}
if (!line.blocks.empty())
lines.push_back(std::move(line));
}
// Step 2: Set positions on the X axis.
SetX(global, lines);
JustifyContent(global, lines); // Distribute remaining space.
// Step 3: Set positions on the Y axis.
SetY(global, lines);
}
} // namespace flexbox_helper
} // 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,45 @@
#ifndef FTXUI_DOM_FLEXBOX_HELPER_HPP
#define FTXUI_DOM_FLEXBOX_HELPER_HPP
#include <vector>
#include "ftxui/dom/flexbox_config.hpp"
namespace ftxui {
namespace flexbox_helper {
struct Block {
// Input:
int min_size_x = 0;
int min_size_y = 0;
int flex_grow_x = 0;
int flex_grow_y = 0;
int flex_shrink_x = 0;
int flex_shrink_y = 0;
// Output:
int line;
int line_position;
int x = 0;
int y = 0;
int dim_x = 0;
int dim_y = 0;
bool overflow = false;
};
struct Global {
std::vector<Block> blocks;
FlexboxConfig config;
int size_x;
int size_y;
};
void Compute(Global& global);
} // namespace flexbox_helper
} // namespace ftxui
#endif /* end of include guard: FTXUI_DOM_FLEXBOX_HELPER_HPP*/
// 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

@@ -0,0 +1,233 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <memory> // for allocator_traits<>::value_type
#include "ftxui/dom/flexbox_helper.hpp"
#include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, TEST
using namespace ftxui;
using namespace ftxui;
TEST(FlexboxHelperTest, BasicRow) {
flexbox_helper::Block block_10_5;
block_10_5.min_size_x = 10;
block_10_5.min_size_y = 5;
flexbox_helper::Global g;
g.blocks = {
block_10_5, block_10_5, block_10_5, block_10_5, block_10_5,
};
g.size_x = 32;
g.size_y = 16;
g.config = FlexboxConfig().Set(FlexboxConfig::Direction::Row);
flexbox_helper::Compute(g);
EXPECT_EQ(g.blocks.size(), 5u);
EXPECT_EQ(g.blocks[0].line, 0);
EXPECT_EQ(g.blocks[1].line, 0);
EXPECT_EQ(g.blocks[2].line, 0);
EXPECT_EQ(g.blocks[3].line, 1);
EXPECT_EQ(g.blocks[4].line, 1);
EXPECT_EQ(g.blocks[0].line_position, 0);
EXPECT_EQ(g.blocks[1].line_position, 1);
EXPECT_EQ(g.blocks[2].line_position, 2);
EXPECT_EQ(g.blocks[3].line_position, 0);
EXPECT_EQ(g.blocks[4].line_position, 1);
EXPECT_EQ(g.blocks[0].x, 0);
EXPECT_EQ(g.blocks[0].y, 0);
EXPECT_EQ(g.blocks[0].dim_x, 10);
EXPECT_EQ(g.blocks[0].dim_y, 5);
EXPECT_EQ(g.blocks[1].x, 10);
EXPECT_EQ(g.blocks[1].y, 0);
EXPECT_EQ(g.blocks[1].dim_x, 10);
EXPECT_EQ(g.blocks[1].dim_y, 5);
EXPECT_EQ(g.blocks[2].x, 20);
EXPECT_EQ(g.blocks[2].y, 0);
EXPECT_EQ(g.blocks[2].dim_x, 10);
EXPECT_EQ(g.blocks[2].dim_y, 5);
EXPECT_EQ(g.blocks[3].x, 0);
EXPECT_EQ(g.blocks[3].y, 5);
EXPECT_EQ(g.blocks[3].dim_x, 10);
EXPECT_EQ(g.blocks[3].dim_y, 5);
EXPECT_EQ(g.blocks[4].x, 10);
EXPECT_EQ(g.blocks[4].y, 5);
EXPECT_EQ(g.blocks[4].dim_x, 10);
EXPECT_EQ(g.blocks[4].dim_y, 5);
}
TEST(FlexboxHelperTest, BasicColumn) {
flexbox_helper::Block block_10_5;
block_10_5.min_size_x = 10;
block_10_5.min_size_y = 5;
flexbox_helper::Global g;
g.blocks = {
block_10_5, block_10_5, block_10_5, block_10_5, block_10_5,
};
g.size_x = 32;
g.size_y = 16;
g.config = FlexboxConfig().Set(FlexboxConfig::Direction::Column);
flexbox_helper::Compute(g);
EXPECT_EQ(g.blocks.size(), 5u);
EXPECT_EQ(g.blocks[0].line, 0);
EXPECT_EQ(g.blocks[1].line, 0);
EXPECT_EQ(g.blocks[2].line, 0);
EXPECT_EQ(g.blocks[3].line, 1);
EXPECT_EQ(g.blocks[4].line, 1);
EXPECT_EQ(g.blocks[0].line_position, 0);
EXPECT_EQ(g.blocks[1].line_position, 1);
EXPECT_EQ(g.blocks[2].line_position, 2);
EXPECT_EQ(g.blocks[3].line_position, 0);
EXPECT_EQ(g.blocks[4].line_position, 1);
EXPECT_EQ(g.blocks[0].x, 0);
EXPECT_EQ(g.blocks[0].y, 0);
EXPECT_EQ(g.blocks[0].dim_x, 10);
EXPECT_EQ(g.blocks[0].dim_y, 5);
EXPECT_EQ(g.blocks[1].x, 0);
EXPECT_EQ(g.blocks[1].y, 5);
EXPECT_EQ(g.blocks[1].dim_x, 10);
EXPECT_EQ(g.blocks[1].dim_y, 5);
EXPECT_EQ(g.blocks[2].x, 0);
EXPECT_EQ(g.blocks[2].y, 10);
EXPECT_EQ(g.blocks[2].dim_x, 10);
EXPECT_EQ(g.blocks[2].dim_y, 5);
EXPECT_EQ(g.blocks[3].x, 10);
EXPECT_EQ(g.blocks[3].y, 0);
EXPECT_EQ(g.blocks[3].dim_x, 10);
EXPECT_EQ(g.blocks[3].dim_y, 5);
EXPECT_EQ(g.blocks[4].x, 10);
EXPECT_EQ(g.blocks[4].y, 5);
EXPECT_EQ(g.blocks[4].dim_x, 10);
EXPECT_EQ(g.blocks[4].dim_y, 5);
}
TEST(FlexboxHelperTest, BasicRowInversed) {
flexbox_helper::Block block_10_5;
block_10_5.min_size_x = 10;
block_10_5.min_size_y = 5;
flexbox_helper::Global g;
g.blocks = {
block_10_5, block_10_5, block_10_5, block_10_5, block_10_5,
};
g.size_x = 32;
g.size_y = 16;
g.config = FlexboxConfig().Set(FlexboxConfig::Direction::RowInversed);
flexbox_helper::Compute(g);
EXPECT_EQ(g.blocks.size(), 5u);
EXPECT_EQ(g.blocks[0].line, 0);
EXPECT_EQ(g.blocks[1].line, 0);
EXPECT_EQ(g.blocks[2].line, 0);
EXPECT_EQ(g.blocks[3].line, 1);
EXPECT_EQ(g.blocks[4].line, 1);
EXPECT_EQ(g.blocks[0].line_position, 0);
EXPECT_EQ(g.blocks[1].line_position, 1);
EXPECT_EQ(g.blocks[2].line_position, 2);
EXPECT_EQ(g.blocks[3].line_position, 0);
EXPECT_EQ(g.blocks[4].line_position, 1);
EXPECT_EQ(g.blocks[0].x, 22);
EXPECT_EQ(g.blocks[0].y, 0);
EXPECT_EQ(g.blocks[0].dim_x, 10);
EXPECT_EQ(g.blocks[0].dim_y, 5);
EXPECT_EQ(g.blocks[1].x, 12);
EXPECT_EQ(g.blocks[1].y, 0);
EXPECT_EQ(g.blocks[1].dim_x, 10);
EXPECT_EQ(g.blocks[1].dim_y, 5);
EXPECT_EQ(g.blocks[2].x, 2);
EXPECT_EQ(g.blocks[2].y, 0);
EXPECT_EQ(g.blocks[2].dim_x, 10);
EXPECT_EQ(g.blocks[2].dim_y, 5);
EXPECT_EQ(g.blocks[3].x, 22);
EXPECT_EQ(g.blocks[3].y, 5);
EXPECT_EQ(g.blocks[3].dim_x, 10);
EXPECT_EQ(g.blocks[3].dim_y, 5);
EXPECT_EQ(g.blocks[4].x, 12);
EXPECT_EQ(g.blocks[4].y, 5);
EXPECT_EQ(g.blocks[4].dim_x, 10);
EXPECT_EQ(g.blocks[4].dim_y, 5);
}
TEST(FlexboxHelperTest, BasicColumnInversed) {
flexbox_helper::Block block_10_5;
block_10_5.min_size_x = 10;
block_10_5.min_size_y = 5;
flexbox_helper::Global g;
g.blocks = {
block_10_5, block_10_5, block_10_5, block_10_5, block_10_5,
};
g.size_x = 32;
g.size_y = 16;
g.config = FlexboxConfig().Set(FlexboxConfig::Direction::ColumnInversed);
flexbox_helper::Compute(g);
EXPECT_EQ(g.blocks.size(), 5u);
EXPECT_EQ(g.blocks[0].line, 0);
EXPECT_EQ(g.blocks[1].line, 0);
EXPECT_EQ(g.blocks[2].line, 0);
EXPECT_EQ(g.blocks[3].line, 1);
EXPECT_EQ(g.blocks[4].line, 1);
EXPECT_EQ(g.blocks[0].line_position, 0);
EXPECT_EQ(g.blocks[1].line_position, 1);
EXPECT_EQ(g.blocks[2].line_position, 2);
EXPECT_EQ(g.blocks[3].line_position, 0);
EXPECT_EQ(g.blocks[4].line_position, 1);
EXPECT_EQ(g.blocks[0].x, 0);
EXPECT_EQ(g.blocks[0].y, 11);
EXPECT_EQ(g.blocks[0].dim_x, 10);
EXPECT_EQ(g.blocks[0].dim_y, 5);
EXPECT_EQ(g.blocks[1].x, 0);
EXPECT_EQ(g.blocks[1].y, 6);
EXPECT_EQ(g.blocks[1].dim_x, 10);
EXPECT_EQ(g.blocks[1].dim_y, 5);
EXPECT_EQ(g.blocks[2].x, 0);
EXPECT_EQ(g.blocks[2].y, 1);
EXPECT_EQ(g.blocks[2].dim_x, 10);
EXPECT_EQ(g.blocks[2].dim_y, 5);
EXPECT_EQ(g.blocks[3].x, 10);
EXPECT_EQ(g.blocks[3].y, 11);
EXPECT_EQ(g.blocks[3].dim_x, 10);
EXPECT_EQ(g.blocks[3].dim_y, 5);
EXPECT_EQ(g.blocks[4].x, 10);
EXPECT_EQ(g.blocks[4].y, 6);
EXPECT_EQ(g.blocks[4].dim_x, 10);
EXPECT_EQ(g.blocks[4].dim_y, 5);
}
// 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

@@ -0,0 +1,437 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <string> // for allocator
#include "ftxui/dom/elements.hpp" // for text, flexbox
#include "ftxui/dom/flexbox_config.hpp" // for FlexboxConfig, FlexboxConfig::Direction, FlexboxConfig::AlignContent, FlexboxConfig::JustifyContent, FlexboxConfig::Direction::Column, FlexboxConfig::AlignItems, FlexboxConfig::JustifyContent::SpaceAround, FlexboxConfig::AlignContent::Center, FlexboxConfig::AlignContent::FlexEnd, FlexboxConfig::AlignContent::SpaceAround, FlexboxConfig::AlignContent::SpaceBetween, FlexboxConfig::AlignContent::SpaceEvenly, FlexboxConfig::AlignItems::Center, FlexboxConfig::AlignItems::FlexEnd, FlexboxConfig::Direction::ColumnInversed, FlexboxConfig::Direction::Row, FlexboxConfig::Direction::RowInversed, FlexboxConfig::JustifyContent::Center, FlexboxConfig::JustifyContent::SpaceBetween, ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
using namespace ftxui;
TEST(FlexboxTest, BasicRow) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
},
FlexboxConfig().Set(FlexboxConfig::Direction::Row));
Screen screen(7, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aaabbb \r\n"
"cccc \r\n"
"dddd \r\n"
" ");
}
TEST(FlexboxTest, BasicRowInversed) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
},
FlexboxConfig().Set(FlexboxConfig::Direction::RowInversed));
Screen screen(7, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" bbbaaa\r\n"
" cccc\r\n"
" dddd\r\n"
" ");
}
TEST(FlexboxTest, BasicColumn) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
text("e"),
},
FlexboxConfig().Set(FlexboxConfig::Direction::Column));
Screen screen(8, 3);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aaa dddd\r\n"
"bbb e \r\n"
"cccc ");
}
TEST(FlexboxTest, BasicColumnInversed) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
text("e"),
},
FlexboxConfig().Set(FlexboxConfig::Direction::ColumnInversed));
Screen screen(8, 3);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"cccc \r\n"
"bbb e \r\n"
"aaa dddd");
}
TEST(FlexboxTest, JustifyContentCenter) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
text("e"),
},
FlexboxConfig().Set(FlexboxConfig::JustifyContent::Center));
Screen screen(7, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aaabbb \r\n"
" cccc \r\n"
" dddde \r\n"
" ");
}
TEST(FlexboxTest, JustifyContentSpaceBetween) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
text("e"),
},
FlexboxConfig().Set(FlexboxConfig::JustifyContent::SpaceBetween));
Screen screen(7, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aaa bbb\r\n"
"cccc \r\n"
"dddd e\r\n"
" ");
}
TEST(FlexboxTest, JustifyContentSpaceAround) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("dddddddddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::JustifyContent::SpaceAround));
Screen screen(15, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" aa bb ccc \r\n"
"ddddddddddddee \r\n"
" ff ggg \r\n"
" ");
}
TEST(FlexboxTest, JustifyContentSpaceEvenly) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("dddddddddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::JustifyContent::SpaceAround));
Screen screen(15, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" aa bb ccc \r\n"
"ddddddddddddee \r\n"
" ff ggg \r\n"
" ");
}
TEST(FlexboxTest, AlignItemsFlexEnd) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig()
.Set(FlexboxConfig::Direction::Column)
.Set(FlexboxConfig::AlignItems::FlexEnd));
Screen screen(15, 5);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" aa ff \r\n"
" bbggg \r\n"
" ccc \r\n"
"ddddd \r\n"
" ee ");
}
TEST(FlexboxTest, AlignItemsCenter) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig()
.Set(FlexboxConfig::Direction::Column)
.Set(FlexboxConfig::AlignItems::Center));
Screen screen(15, 5);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" aa ff \r\n"
" bb ggg \r\n"
" ccc \r\n"
"ddddd \r\n"
" ee ");
}
TEST(FlexboxTest, AlignContentFlexEnd) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::AlignContent::FlexEnd));
Screen screen(10, 5);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" \r\n"
" \r\n"
"aabbccc \r\n"
"dddddeeff \r\n"
"ggg ");
}
TEST(FlexboxTest, AlignContentCenter) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::AlignContent::Center));
Screen screen(10, 5);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" \r\n"
"aabbccc \r\n"
"dddddeeff \r\n"
"ggg \r\n"
" ");
}
TEST(FlexboxTest, AlignContentSpaceBetween) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::AlignContent::SpaceBetween));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aabbb \r\n"
" \r\n"
" \r\n"
"ccc \r\n"
" \r\n"
" \r\n"
"dddddee\r\n"
" \r\n"
" \r\n"
"ffggg ");
}
TEST(FlexboxTest, AlignContentSpaceAround) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::AlignContent::SpaceAround));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aabbb \r\n"
" \r\n"
"ccc \r\n"
" \r\n"
" \r\n"
"dddddee\r\n"
" \r\n"
" \r\n"
"ffggg \r\n"
" ");
}
TEST(FlexboxTest, AlignContentSpaceEvenly) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::AlignContent::SpaceEvenly));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" \r\n"
"aabbb \r\n"
" \r\n"
"ccc \r\n"
" \r\n"
"dddddee\r\n"
" \r\n"
"ffggg \r\n"
" \r\n"
" ");
}
TEST(FlexboxTest, GapX) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().SetGap(1, 0));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aa bbb \r\n"
"ccc \r\n"
"ddddd \r\n"
"ee ff \r\n"
"ggg \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ");
}
TEST(FlexboxTest, GapX2) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().SetGap(2, 0));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aa bbb\r\n"
"ccc \r\n"
"ddddd \r\n"
"ee ff \r\n"
"ggg \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ");
}
TEST(FlexboxTest, GapY) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().SetGap(0, 1));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aabbb \r\n"
" \r\n"
"ccc \r\n"
" \r\n"
"dddddee\r\n"
" \r\n"
"ffggg \r\n"
" \r\n"
" \r\n"
" ");
}
// 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,10 +1,10 @@
#include <algorithm> // for max, min
#include <memory> // for make_shared, shared_ptr, __shared_ptr_access
#include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for Element, unpack, focus, frame, select, xframe, yframe
#include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, focus, frame, select, xframe, yframe
#include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/dom/requirement.hpp" // for Requirement, Requirement::FOCUSED, Requirement::SELECTED
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Screen, Screen::Cursor

View File

@@ -1,12 +1,12 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult
#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 gauge
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for Test, SuiteApiResolver, EXPECT_EQ
#include "ftxui/dom/elements.hpp" // for gauge
#include "ftxui/dom/flexbox_config.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
using namespace ftxui;
using namespace ftxui;

View File

@@ -5,10 +5,10 @@
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for text, operator|, Element, flex, flex_grow, Elements, flex_shrink, vtext, gridbox, vbox, border
#include "ftxui/dom/node.hpp" // for Render
#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
#include "ftxui/dom/flexbox_config.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for Test, TEST, EXPECT_EQ
using namespace ftxui;
@@ -24,6 +24,14 @@ Element cell(const char* t) {
}
} // namespace
TEST(GridboxTest, UnfilledRectangular) {
auto root = gridbox({
{text("1"), text("2"), text("3"), text("4")},
{},
{},
});
}
TEST(GridboxTest, DifferentSize) {
auto root = gridbox({
{cell("1"), cell("22"), cell("333")},

View File

@@ -4,10 +4,10 @@
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for text, operator|, Element, flex_grow, flex_shrink, hbox
#include "ftxui/dom/node.hpp" // for Render
#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
#include "ftxui/dom/flexbox_config.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
using namespace ftxui;
using namespace ftxui;

View File

@@ -1,83 +0,0 @@
#include <algorithm> // for max
#include <memory> // for __shared_ptr_access, make_shared, shared_ptr
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for Element, Elements, hflow
#include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
namespace ftxui {
class HFlow : public Node {
public:
HFlow(Elements children) : Node(std::move(children)) {}
void ComputeRequirement() override {
requirement_.min_x = 1;
requirement_.min_y = 1;
requirement_.flex_grow_x = 1;
requirement_.flex_grow_y = 1;
requirement_.flex_shrink_x = 0;
requirement_.flex_shrink_y = 0;
for (auto& child : children_)
child->ComputeRequirement();
}
void SetBox(Box box) override {
Node::SetBox(box);
// The position of the first component.
int x = box.x_min;
int y = box.y_min;
int y_next = y; // The position of next row of elements.
for (auto& child : children_) {
Requirement requirement = child->requirement();
// Does it fit the end of the row?
if (x + requirement.min_x > box.x_max) {
// No? Use the next row.
x = box.x_min;
y = y_next;
}
// Does the current row big enough to contain the element?
if (y + requirement.min_y > box.y_max + 1)
break; // No? Ignore the element.
Box children_box;
children_box.x_min = x;
children_box.x_max = x + requirement.min_x - 1;
children_box.y_min = y;
children_box.y_max = y + requirement.min_y - 1;
child->SetBox(children_box);
x = x + requirement.min_x;
y_next = std::max(y_next, y + requirement.min_y);
}
}
};
/// @brief A container displaying elements horizontally one by one.
/// @param children The elements in the container
/// @return The container.
///
/// #### Example
///
/// ```cpp
/// hbox({
/// text("Left"),
/// text("Right"),
/// });
/// ```
Element hflow(Elements children) {
return std::make_shared<HFlow>(std::move(children));
}
} // namespace ftxui
// 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

@@ -1,7 +1,7 @@
#include <utility>
#include <utility> // for move
#include "ftxui/dom/node.hpp"
#include "ftxui/screen/screen.hpp"
#include "ftxui/screen/screen.hpp" // for Screen
namespace ftxui {
@@ -29,6 +29,12 @@ void Node::Render(Screen& screen) {
child->Render(screen);
}
void Node::Check(Status* status) {
for (auto& child : children_)
child->Check(status);
status->need_iteration |= (status->iteration == 0);
}
/// @brief Display an element on a ftxui::Screen.
/// @ingroup dom
void Render(Screen& screen, const Element& element) {
@@ -38,20 +44,29 @@ void Render(Screen& screen, const Element& element) {
/// @brief Display an element on a ftxui::Screen.
/// @ingroup dom
void Render(Screen& screen, Node* node) {
// Step 1: Find what dimension this elements wants to be.
node->ComputeRequirement();
Box box;
box.x_min = 0;
box.y_min = 0;
box.x_max = screen.dimx() - 1;
box.y_max = screen.dimy() - 1;
// Step 2: Assign a dimension to the element.
node->SetBox(box);
screen.stencil = box;
Node::Status status;
node->Check(&status);
while (status.need_iteration && status.iteration < 20) {
// Step 1: Find what dimension this elements wants to be.
node->ComputeRequirement();
// Step 2: Assign a dimension to the element.
node->SetBox(box);
// Check if the element needs another iteration of the layout algorithm.
status.need_iteration = false;
status.iteration++;
node->Check(&status);
}
// Step 3: Draw the element.
screen.stencil = box;
node->Render(screen);
// Step 4: Apply shaders

View File

@@ -1,5 +1,5 @@
#include <memory> // for __shared_ptr_access
#include <vector> // for __alloc_traits<>::value_type, vector
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/node_decorator.hpp"
#include "ftxui/dom/requirement.hpp" // for Requirement

View File

@@ -1,38 +1,71 @@
#include <sstream> // for basic_istream, wstringstream
#include <string> // for allocator, char_traits, getline, operator+, wstring, basic_string
#include <sstream> // for basic_istream, stringstream
#include <string> // for string, allocator, getline
#include <utility> // for move
#include "ftxui/dom/deprecated.hpp" // for text, paragraph
#include "ftxui/dom/elements.hpp" // for Elements
#include "ftxui/dom/elements.hpp" // for flexbox, Element, text, Elements, operator|, xflex, paragraph, paragraphAlignCenter, paragraphAlignJustify, paragraphAlignLeft, paragraphAlignRight
#include "ftxui/dom/flexbox_config.hpp" // for FlexboxConfig, FlexboxConfig::JustifyContent, FlexboxConfig::JustifyContent::Center, FlexboxConfig::JustifyContent::FlexEnd, FlexboxConfig::JustifyContent::SpaceBetween
namespace ftxui {
/// @brief Return a vector of ftxui::text for every word of the string. This is
/// useful combined with ftxui::hflow.
/// @param the_text The string to be splitted.
/// @ingroup dom
/// @see hflow.
Elements paragraph(std::wstring the_text) {
Elements output;
std::wstringstream ss(the_text);
std::wstring word;
while (std::getline(ss, word, L' '))
output.push_back(text(word + L' '));
return output;
}
/// @brief Return a vector of ftxui::text for every word of the string. This is
/// useful combined with ftxui::hflow.
/// @param the_text The string to be splitted.
/// @ingroup dom
/// @see hflow.
Elements paragraph(std::string the_text) {
namespace {
Elements Split(std::string the_text) {
Elements output;
std::stringstream ss(the_text);
std::string word;
while (std::getline(ss, word, ' '))
output.push_back(text(word + ' '));
output.push_back(text(word));
return output;
}
} // namespace
/// @brief Return an element drawing the paragraph on multiple lines.
/// @ingroup dom
/// @see flexbox.
Element paragraph(std::string the_text) {
return paragraphAlignLeft(std::move(the_text));
}
/// @brief Return an element drawing the paragraph on multiple lines, aligned on
/// the left.
/// @ingroup dom
/// @see flexbox.
Element paragraphAlignLeft(std::string the_text) {
static const auto config = FlexboxConfig().SetGap(1, 0);
return flexbox(Split(std::move(the_text)), config);
}
/// @brief Return an element drawing the paragraph on multiple lines, aligned on
/// the right.
/// @ingroup dom
/// @see flexbox.
Element paragraphAlignRight(std::string the_text) {
static const auto config =
FlexboxConfig().SetGap(1, 0).Set(FlexboxConfig::JustifyContent::FlexEnd);
return flexbox(Split(std::move(the_text)), config);
}
/// @brief Return an element drawing the paragraph on multiple lines, aligned on
/// the center.
/// @ingroup dom
/// @see flexbox.
Element paragraphAlignCenter(std::string the_text) {
static const auto config =
FlexboxConfig().SetGap(1, 0).Set(FlexboxConfig::JustifyContent::Center);
return flexbox(Split(std::move(the_text)), config);
}
/// @brief Return an element drawing the paragraph on multiple lines, aligned
/// using a justified alignment.
/// the center.
/// @ingroup dom
/// @see flexbox.
Element paragraphAlignJustify(std::string the_text) {
static const auto config = FlexboxConfig().SetGap(1, 0).Set(
FlexboxConfig::JustifyContent::SpaceBetween);
Elements words = Split(std::move(the_text));
words.push_back(text("") | xflex);
return flexbox(std::move(words), config);
}
} // namespace ftxui

View File

@@ -2,10 +2,10 @@
#include <algorithm> // for min, max
#include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move
#include <vector> // for __alloc_traits<>::value_type, vector
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for Constraint, Direction, EQUAL, GREATER_THAN, LESS_THAN, WIDTH, unpack, Decorator, Element, size
#include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box

View File

@@ -1,6 +1,6 @@
#include <stddef.h> // for size_t
#include <memory> // for allocator, allocator_traits<>::value_type
#include <string> // for string
#include <string> // for basic_string, string
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type

View File

@@ -3,9 +3,9 @@
#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/flexbox_config.hpp" // for ftxui
#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

View File

@@ -2,11 +2,11 @@
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <string> // for allocator, string
#include "ftxui/dom/elements.hpp" // for text, operator|, border, Element
#include "ftxui/dom/node.hpp" // for Render
#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
#include "ftxui/dom/elements.hpp" // for text, operator|, border, Element
#include "ftxui/dom/flexbox_config.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
using namespace ftxui;

View File

@@ -1,12 +1,13 @@
#include <algorithm> // for min
#include <functional> // for function
#include <memory> // for __shared_ptr_access
#include <memory> // for __shared_ptr_access, make_unique
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for Element, Decorator, Elements, operator|, Fit, nothing
#include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/elements.hpp" // for Element, Decorator, Elements, operator|, Fit, emptyElement, nothing
#include "ftxui/dom/node.hpp" // for Node, Node::Status
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Full
#include "ftxui/screen/terminal.hpp" // for Dimensions
@@ -69,10 +70,39 @@ Element operator|(Element element, Decorator decorator) {
/// @see Fixed
/// @see Full
Dimensions Dimension::Fit(Element& e) {
e->ComputeRequirement();
Dimensions size = Dimension::Full();
return {std::min(e->requirement().min_x, size.dimx),
std::min(e->requirement().min_y, size.dimy)};
Dimensions fullsize = Dimension::Full();
Box box;
box.x_min = 0;
box.y_min = 0;
box.x_max = fullsize.dimx;
box.y_max = fullsize.dimy;
Node::Status status;
e->Check(&status);
while (status.need_iteration && status.iteration < 20) {
e->ComputeRequirement();
// Don't give the element more space than it needs:
box.x_max = std::min(box.x_max, e->requirement().min_x);
box.y_max = std::min(box.y_max, e->requirement().min_y);
e->SetBox(box);
status.need_iteration = false;
status.iteration++;
e->Check(&status);
if (!status.need_iteration)
break;
// Increase the size of the box until it fits, but not more than the with of
// the terminal emulator:
box.x_max = std::min(e->requirement().min_x, fullsize.dimx);
box.y_max = std::min(e->requirement().min_y, fullsize.dimy);
}
return {
box.x_max,
box.y_max,
};
}
/// An element of size 0x0 drawing nothing.

View File

@@ -5,10 +5,10 @@
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for vtext, operator|, Element, flex_grow, flex_shrink, vbox
#include "ftxui/dom/node.hpp" // for Render
#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
#include "ftxui/dom/flexbox_config.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
using namespace ftxui;

View File

@@ -24,6 +24,19 @@ bool Box::Contain(int x, int y) {
y_max >= y;
}
/// @return whether |other| is the same as |this|
/// @ingroup screen
bool Box::operator==(const Box& other) const {
return (x_min == other.x_min) && (x_max == other.x_max) &&
(y_min == other.y_min) && (y_max == other.y_max);
}
/// @return whether |other| and |this| are different.
/// @ingroup screen
bool Box::operator!=(const Box& other) const {
return !operator==(other);
}
} // namespace ftxui
// Copyright 2020 Arthur Sonzogni. All rights reserved.