diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bcc527..7ba4517 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,24 @@ Changelog unreleased (development) ------------------------ -# Component: -- Bugfix: Input shouldn't take focus when hovered by the mouse. +### Features: +- Support `flexbox` dom elements. This is build symmetrically to the HTML one. + All the following attributes are supported: direction, wrap, justify-content, + align-items, align-content, gap +- Add the dom elements helper based on `flexbox`: + - `paragraph` + - `paragraphAlignLeft` + - `paragraphAlignCenter` + - `paragraphAlignRight` + - `paragraphAlignJustify` +- Add the helper elements based on `flexbox`: `hflow()`, `vflow()`. + +### Breaking changes: +- The behavior of `paragraph` has been modified. It now returns en Element, + instead of a list of elements. + +### Bug +- Input shouldn't take focus when hovered by the mouse. 0.11.1 ------ diff --git a/CMakeLists.txt b/CMakeLists.txt index eccf076..936df98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ add_library(screen add_library(dom include/ftxui/dom/elements.hpp + include/ftxui/dom/flexbox_config.hpp include/ftxui/dom/node.hpp include/ftxui/dom/requirement.hpp include/ftxui/dom/take_any_args.hpp @@ -52,12 +53,15 @@ add_library(dom src/ftxui/dom/dbox.cpp src/ftxui/dom/dim.cpp src/ftxui/dom/flex.cpp + src/ftxui/dom/flexbox.cpp + src/ftxui/dom/flexbox_config.cpp + src/ftxui/dom/flexbox_helper.cpp + src/ftxui/dom/flexbox_helper.hpp src/ftxui/dom/frame.cpp src/ftxui/dom/gauge.cpp src/ftxui/dom/graph.cpp src/ftxui/dom/gridbox.cpp src/ftxui/dom/hbox.cpp - src/ftxui/dom/hflow.cpp src/ftxui/dom/inverted.cpp src/ftxui/dom/node.cpp src/ftxui/dom/node_decorator.cpp diff --git a/cmake/ftxui_test.cmake b/cmake/ftxui_test.cmake index 4f3c1b2..57f32cc 100644 --- a/cmake/ftxui_test.cmake +++ b/cmake/ftxui_test.cmake @@ -19,10 +19,12 @@ add_executable(tests src/ftxui/component/screen_interactive_test.cpp src/ftxui/component/terminal_input_parser_test.cpp src/ftxui/component/toggle_test.cpp + src/ftxui/dom/flexbox_helper_test.cpp + src/ftxui/dom/flexbox_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/table_test.cpp src/ftxui/dom/text_test.cpp src/ftxui/dom/vbox_test.cpp src/ftxui/screen/string_test.cpp diff --git a/examples/component/CMakeLists.txt b/examples/component/CMakeLists.txt index 299e45b..23ec54a 100644 --- a/examples/component/CMakeLists.txt +++ b/examples/component/CMakeLists.txt @@ -5,6 +5,7 @@ example(checkbox) example(checkbox_in_frame) example(composition) example(dropdown) +example(flexbox) example(gallery) example(homescreen) example(input) diff --git a/examples/component/checkbox.cpp b/examples/component/checkbox.cpp index 65e05f5..24eb335 100644 --- a/examples/component/checkbox.cpp +++ b/examples/component/checkbox.cpp @@ -1,6 +1,12 @@ +#include // for allocator, __shared_ptr_access +#include // for string, basic_string, operator+, to_string +#include // for vector + +#include "ftxui/component/captured_mouse.hpp" // for ftxui +#include "ftxui/component/component.hpp" // for Input, Renderer, Vertical +#include "ftxui/component/component_base.hpp" // for ComponentBase #include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive -#include "ftxui/dom/elements.hpp" -#include "ftxui/component/component.hpp" +#include "ftxui/dom/elements.hpp" // for operator|, Element, size, border, frame, vscroll_indicator, HEIGHT, LESS_THAN int main(int argc, const char* argv[]) { using namespace ftxui; @@ -19,3 +25,7 @@ int main(int argc, const char* argv[]) { auto screen = ScreenInteractive::TerminalOutput(); screen.Loop(renderer); } + +// 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. diff --git a/examples/component/flexbox.cpp b/examples/component/flexbox.cpp new file mode 100644 index 0000000..ff46adf --- /dev/null +++ b/examples/component/flexbox.cpp @@ -0,0 +1,192 @@ +#include // for size_t +#include // for shared_ptr, __shared_ptr_access, allocator +#include // for string, basic_string, to_string, operator+, char_traits +#include // for vector + +#include "ftxui/component/captured_mouse.hpp" // for ftxui +#include "ftxui/component/component.hpp" // for Radiobox, Vertical, Checkbox, Horizontal, Renderer, ResizableSplitBottom, ResizableSplitRight +#include "ftxui/component/component_base.hpp" // for ComponentBase +#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive +#include "ftxui/dom/elements.hpp" // for text, window, operator|, vbox, hbox, Element, flexbox, bgcolor, filler, flex, size, border, hcenter, color, EQUAL, bold, dim, notflex, xflex_grow, yflex_grow, HEIGHT, WIDTH +#include "ftxui/dom/flexbox_config.hpp" // for FlexboxConfig, FlexboxConfig::AlignContent, FlexboxConfig::JustifyContent, FlexboxConfig::AlignContent::Center, FlexboxConfig::AlignItems, FlexboxConfig::Direction, FlexboxConfig::JustifyContent::Center, FlexboxConfig::Wrap +#include "ftxui/screen/color.hpp" // for Color, Color::Black + +using namespace ftxui; + +int main(int argc, const char* argv[]) { + auto screen = ScreenInteractive::Fullscreen(); + + int direction_index = 0; + int wrap_index = 0; + int justify_content_index = 0; + int align_items_index = 0; + int align_content_index = 0; + + std::vector directions = { + "Row", + "RowInversed", + "Column", + "ColumnInversed", + }; + + std::vector wraps = { + "NoWrap", + "Wrap", + "WrapInversed", + }; + + std::vector justify_content = { + "FlexStart", "FlexEnd", "Center", "Stretch", + "SpaceBetween", "SpaceAround", "SpaceEvenly", + }; + + std::vector align_items = { + "FlexStart", + "FlexEnd", + "Center", + "Stretch", + }; + + std::vector align_content = { + "FlexStart", "FlexEnd", "Center", "Stretch", + "SpaceBetween", "SpaceAround", "SpaceEvenly", + }; + + auto radiobox_direction = Radiobox(&directions, &direction_index); + auto radiobox_wrap = Radiobox(&wraps, &wrap_index); + auto radiobox_justify_content = + Radiobox(&justify_content, &justify_content_index); + auto radiobox_align_items = Radiobox(&align_items, &align_items_index); + auto radiobox_align_content = Radiobox(&align_content, &align_content_index); + + bool element_xflex_grow = false; + bool element_yflex_grow = false; + bool group_xflex_grow = true; + bool group_yflex_grow = true; + auto checkbox_element_xflex_grow = + Checkbox("element |= xflex_grow", &element_xflex_grow); + auto checkbox_element_yflex_grow = + Checkbox("element |= yflex_grow", &element_yflex_grow); + auto checkbox_group_xflex_grow = + Checkbox("group |= xflex_grow", &group_xflex_grow); + auto checkbox_group_yflex_grow = + Checkbox("group |= yflex_grow", &group_yflex_grow); + + auto make_box = [&](size_t dimx, size_t dimy, size_t index) { + std::string title = std::to_string(dimx) + "x" + std::to_string(dimy); + auto element = window(text(title) | hcenter | bold, + text(std::to_string(index)) | hcenter | dim) | + size(WIDTH, EQUAL, dimx) | size(HEIGHT, EQUAL, dimy) | + bgcolor(Color::HSV(index * 25, 255, 255)) | + color(Color::Black); + if (element_xflex_grow) + element = element | xflex_grow; + if (element_yflex_grow) + element = element | yflex_grow; + return element; + }; + + auto content_renderer = Renderer([&] { + FlexboxConfig config; + config.direction = static_cast(direction_index); + config.wrap = static_cast(wrap_index); + config.justify_content = + static_cast(justify_content_index); + config.align_items = + static_cast(align_items_index); + config.align_content = + static_cast(align_content_index); + + auto group = flexbox( + { + make_box(8, 4, 0), + make_box(9, 6, 1), + make_box(11, 6, 2), + make_box(10, 4, 3), + make_box(13, 7, 4), + make_box(12, 4, 5), + make_box(12, 5, 6), + make_box(10, 4, 7), + make_box(12, 4, 8), + make_box(10, 5, 9), + }, + config); + + group = group | bgcolor(Color::Black); + + group = group | notflex; + + if (!group_xflex_grow) + group = hbox(group, filler()); + if (!group_yflex_grow) + group = vbox(group, filler()); + + group = group | flex; + return group; + }); + + auto center = FlexboxConfig() + .Set(FlexboxConfig::JustifyContent::Center) + .Set(FlexboxConfig::AlignContent::Center); + int space_right = 10; + int space_bottom = 1; + content_renderer = ResizableSplitRight( + Renderer([&] { return flexbox({text("resizable")}, center); }), + content_renderer, &space_right); + content_renderer = ResizableSplitBottom( + Renderer([&] { return flexbox({text("resizable")}, center); }), + content_renderer, &space_bottom); + + auto main_container = Container::Vertical({ + Container::Horizontal({ + radiobox_direction, + radiobox_wrap, + Container::Vertical({ + checkbox_element_xflex_grow, + checkbox_element_yflex_grow, + checkbox_group_xflex_grow, + checkbox_group_yflex_grow, + }), + }), + Container::Horizontal({ + radiobox_justify_content, + radiobox_align_items, + radiobox_align_content, + }), + content_renderer, + }); + + auto main_renderer = Renderer(main_container, [&] { + return vbox({ + vbox({hbox({ + window(text("FlexboxConfig::Direction"), + radiobox_direction->Render()), + window(text("FlexboxConfig::Wrap"), radiobox_wrap->Render()), + window(text("Misc:"), + vbox({ + checkbox_element_xflex_grow->Render(), + checkbox_element_yflex_grow->Render(), + checkbox_group_xflex_grow->Render(), + checkbox_group_yflex_grow->Render(), + })), + }), + hbox({ + window(text("FlexboxConfig::JustifyContent"), + radiobox_justify_content->Render()), + window(text("FlexboxConfig::AlignItems"), + radiobox_align_items->Render()), + window(text("FlexboxConfig::AlignContent"), + radiobox_align_content->Render()), + })}), + content_renderer->Render() | flex | border, + }); + }); + + screen.Loop(main_renderer); + + 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. diff --git a/examples/component/homescreen.cpp b/examples/component/homescreen.cpp index 87b6634..70c206b 100644 --- a/examples/component/homescreen.cpp +++ b/examples/component/homescreen.cpp @@ -1,27 +1,33 @@ +#include // for size_t #include // for array #include // for operator""s, chrono_literals #include // for sin #include // for ref, reference_wrapper, function #include // for allocator, shared_ptr, __shared_ptr_access -#include // for string, basic_string, operator+, char_traits, to_string +#include // for string, basic_string, operator+, to_string, char_traits #include // for sleep_for, thread #include // for move #include // for vector #include "ftxui/component/captured_mouse.hpp" // for ftxui -#include "ftxui/component/component.hpp" // for Checkbox, Renderer, Horizontal, Vertical, Menu, Radiobox, Tab, Toggle +#include "ftxui/component/component.hpp" // for Checkbox, Renderer, Horizontal, Vertical, Input, Menu, Radiobox, ResizableSplitLeft, Tab, Toggle #include "ftxui/component/component_base.hpp" // for ComponentBase #include "ftxui/component/component_options.hpp" // for InputOption #include "ftxui/component/event.hpp" // for Event, Event::Custom #include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive -#include "ftxui/dom/elements.hpp" // for operator|, color, bgcolor, filler, Element, size, vbox, flex, hbox, graph, separator, EQUAL, WIDTH, hcenter, bold, border, window, HEIGHT, Elements, hflow, flex_grow, frame, gauge, LESS_THAN, spinner, dim, GREATER_THAN -#include "ftxui/screen/color.hpp" // for Color, Color::BlueLight, Color::RedLight, Color::Black, Color::Blue, Color::Cyan, Color::CyanLight, Color::GrayDark, Color::GrayLight, Color::Green, Color::GreenLight, Color::Magenta, Color::MagentaLight, Color::Red, Color::White, Color::Yellow, Color::YellowLight, Color::Default +#include "ftxui/dom/elements.hpp" // for text, operator|, color, bgcolor, filler, Element, size, vbox, flex, hbox, separator, graph, EQUAL, paragraph, hcenter, WIDTH, bold, window, border, vscroll_indicator, Elements, HEIGHT, hflow, frame, flex_grow, flexbox, gauge, paragraphAlignCenter, paragraphAlignJustify, paragraphAlignLeft, paragraphAlignRight, dim, spinner, Decorator, LESS_THAN, center, yflex, GREATER_THAN +#include "ftxui/screen/color.hpp" // for Color, Color::Blue, Color::BlueLight, Color::RedLight, Color::Black, Color::Cyan, Color::CyanLight, Color::GrayDark, Color::GrayLight, Color::Green, Color::GreenLight, Color::Magenta, Color::MagentaLight, Color::Red, Color::White, Color::Yellow, Color::YellowLight, Color::Default +#include "ftxui/screen/terminal.hpp" // for Size, Dimensions using namespace ftxui; int main(int argc, const char* argv[]) { auto screen = ScreenInteractive::Fullscreen(); + // --------------------------------------------------------------------------- + // HTOP + // --------------------------------------------------------------------------- + int shift = 0; auto my_graph = [&shift](int width, int height) { @@ -92,6 +98,10 @@ int main(int argc, const char* argv[]) { flex | border; }); + // --------------------------------------------------------------------------- + // Compiler + // --------------------------------------------------------------------------- + const std::vector compiler_entries = { "gcc", "clang", @@ -248,6 +258,9 @@ int main(int argc, const char* argv[]) { flex_grow | border; }); + // --------------------------------------------------------------------------- + // Spiner + // --------------------------------------------------------------------------- auto spinner_tab_renderer = Renderer([&] { Elements entries; for (int i = 0; i < 22; ++i) { @@ -257,6 +270,9 @@ int main(int argc, const char* argv[]) { return hflow(std::move(entries)) | border; }); + // --------------------------------------------------------------------------- + // Colors + // --------------------------------------------------------------------------- auto color_tab_renderer = Renderer([] { return hbox({ vbox({ @@ -301,6 +317,9 @@ int main(int argc, const char* argv[]) { hcenter | border; }); + // --------------------------------------------------------------------------- + // Gauges + // --------------------------------------------------------------------------- auto render_gauge = [&shift](int delta) { float progress = (shift + delta) % 1000 / 1000.f; return hbox({ @@ -333,9 +352,84 @@ int main(int argc, const char* argv[]) { border; }); + // --------------------------------------------------------------------------- + // Paragraph + // --------------------------------------------------------------------------- + auto make_box = [](size_t dimx, size_t dimy) { + std::string title = std::to_string(dimx) + "x" + std::to_string(dimy); + return window(text(title) | hcenter | bold, + text("content") | hcenter | dim) | + size(WIDTH, EQUAL, dimx) | size(HEIGHT, EQUAL, dimy); + }; + + auto paragraph_renderer_left = Renderer([&] { + auto title_style = bold | bgcolor(Color::Blue) | color(Color::Black); + std::string str = + "Lorem Ipsum is simply dummy text of the printing and typesetting " + "industry. Lorem Ipsum has been the industry's standard dummy text " + "ever since the 1500s, when an unknown printer took a galley of type " + "and scrambled it to make a type specimen book."; + return vbox({ + // [ Left ] + text("Align left:") | title_style, + paragraphAlignLeft(str), + // [ Center ] + text("Align center:") | title_style, + paragraphAlignCenter(str), + // [ Right ] + text("Align right:") | title_style, + paragraphAlignRight(str), + // [ Justify] + text("Align justify:") | title_style, + paragraphAlignJustify(str), + // [ Side by side ] + text("Side by side:") | title_style, + hbox({ + paragraph(str), + separator() | color(Color::Blue), + paragraph(str), + }), + // [ Misc ] + text("Elements with different size:") | title_style, + flexbox({ + make_box(10, 5), + make_box(9, 4), + make_box(8, 4), + make_box(6, 3), + make_box(10, 5), + make_box(9, 4), + make_box(8, 4), + make_box(6, 3), + make_box(10, 5), + make_box(9, 4), + make_box(8, 4), + make_box(6, 3), + }), + }) | + // vscroll_indicator | yflex; + yflex | vscroll_indicator; + }); + + auto paragraph_renderer_right = Renderer([] { + return paragraph("<--- This vertical bar is resizable using the mouse") | + center; + }); + + int paragraph_renderer_split_position = Terminal::Size().dimx / 2; + auto paragraph_renderer_group = + ResizableSplitLeft(paragraph_renderer_left, paragraph_renderer_right, + ¶graph_renderer_split_position); + auto paragraph_renderer_group_renderer = + Renderer(paragraph_renderer_group, + [&] { return paragraph_renderer_group->Render() | border; }); + + // --------------------------------------------------------------------------- + // Tabs + // --------------------------------------------------------------------------- + int tab_index = 0; std::vector tab_entries = { - "htop", "color", "spinner", "gauge", "compiler", + "htop", "color", "spinner", "gauge", "compiler", "paragraph", }; auto tab_selection = Toggle(&tab_entries, &tab_index); auto tab_content = Container::Tab( @@ -345,6 +439,7 @@ int main(int argc, const char* argv[]) { spinner_tab_renderer, gauge_component, compiler_renderer, + paragraph_renderer_group_renderer, }, &tab_index); diff --git a/examples/dom/CMakeLists.txt b/examples/dom/CMakeLists.txt index 9508f5d..5428e21 100644 --- a/examples/dom/CMakeLists.txt +++ b/examples/dom/CMakeLists.txt @@ -11,6 +11,7 @@ example(gauge) example(graph) example(gridbox) example(hflow) +example(vflow) example(html_like) example(package_manager) example(paragraph) diff --git a/examples/dom/border.cpp b/examples/dom/border.cpp index 586442a..ee91bfe 100644 --- a/examples/dom/border.cpp +++ b/examples/dom/border.cpp @@ -2,8 +2,8 @@ #include // for Full, Screen #include // for allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/border_style.cpp b/examples/dom/border_style.cpp index c837a11..2c9780b 100644 --- a/examples/dom/border_style.cpp +++ b/examples/dom/border_style.cpp @@ -1,10 +1,10 @@ -#include // for text, operator|, vbox, border, Element, Fit, hbox -#include // for Full, Screen -#include -#include // for allocator +#include // for operator|, text, Element, Fit, borderDouble, borderHeavy, borderLight, borderRounded, vbox +#include // for Screen +#include // for endl, cout, ostream +#include // for allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/color_info_palette256.cpp b/examples/dom/color_info_palette256.cpp index 944ff5c..e1395e6 100644 --- a/examples/dom/color_info_palette256.cpp +++ b/examples/dom/color_info_palette256.cpp @@ -4,9 +4,9 @@ #include // for move #include // for vector, allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui -#include "ftxui/screen/color.hpp" // for Color, Color::Palette256 +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render +#include "ftxui/screen/color.hpp" // for Color, Color::Palette256 using namespace ftxui; #include "./color_info_sorted_2d.ipp" // for ColorInfoSorted2D diff --git a/examples/dom/color_truecolor_HSV.cpp b/examples/dom/color_truecolor_HSV.cpp index fbf19a7..613f295 100644 --- a/examples/dom/color_truecolor_HSV.cpp +++ b/examples/dom/color_truecolor_HSV.cpp @@ -3,9 +3,9 @@ #include // for allocator #include // for move -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui -#include "ftxui/screen/color.hpp" // for Color +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render +#include "ftxui/screen/color.hpp" // for Color int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/color_truecolor_RGB.cpp b/examples/dom/color_truecolor_RGB.cpp index 26785dc..5117f5e 100644 --- a/examples/dom/color_truecolor_RGB.cpp +++ b/examples/dom/color_truecolor_RGB.cpp @@ -3,9 +3,9 @@ #include // for allocator #include // for move -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui -#include "ftxui/screen/color.hpp" // for Color +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render +#include "ftxui/screen/color.hpp" // for Color int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/dbox.cpp b/examples/dom/dbox.cpp index dd8e1a0..f74b757 100644 --- a/examples/dom/dbox.cpp +++ b/examples/dom/dbox.cpp @@ -2,8 +2,8 @@ #include // for Full, Screen #include // for allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/gauge.cpp b/examples/dom/gauge.cpp index 52fc117..37eb102 100644 --- a/examples/dom/gauge.cpp +++ b/examples/dom/gauge.cpp @@ -5,8 +5,8 @@ #include // for allocator, operator+, char_traits, operator<<, string, to_string, basic_string #include // for sleep_for -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/graph.cpp b/examples/dom/graph.cpp index 55d6257..d500843 100644 --- a/examples/dom/graph.cpp +++ b/examples/dom/graph.cpp @@ -1,16 +1,16 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include // for operator""s, chrono_literals +#include // for sin +#include // for operator|, graph, separator, color, Element, vbox, flex, inverted, Fit, hbox, size, border, GREATER_THAN, HEIGHT +#include // for Full, Screen +#include // for ref, reference_wrapper +#include // for cout, ostream +#include // for operator<<, string +#include // for sleep_for +#include // for vector -#include "ftxui/dom/node.hpp" -#include "ftxui/screen/box.hpp" -#include "ftxui/screen/color.hpp" +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render +#include "ftxui/screen/color.hpp" // for Color, Color::BlueLight, Color::RedLight, Color::YellowLight class Graph { public: diff --git a/examples/dom/gridbox.cpp b/examples/dom/gridbox.cpp index 2139626..355f3f7 100644 --- a/examples/dom/gridbox.cpp +++ b/examples/dom/gridbox.cpp @@ -1,10 +1,10 @@ -#include // for getchar -#include // for filler, text, hbox, vbox -#include // for Full, Screen -#include // for allocator +#include // for getchar +#include // for Elements, gridbox, Fit, operator|, text, border, Element +#include // for Screen +#include // for allocator, shared_ptr -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/hflow.cpp b/examples/dom/hflow.cpp index 2b61fe5..e98d2af 100644 --- a/examples/dom/hflow.cpp +++ b/examples/dom/hflow.cpp @@ -1,11 +1,12 @@ #include // for size_t +#include // for getchar #include // for operator|, size, Element, text, hcenter, Decorator, Fit, WIDTH, hflow, window, EQUAL, GREATER_THAN, HEIGHT, bold, border, dim, LESS_THAN #include // for Full, Screen #include // for allocator, shared_ptr #include // for operator+, to_string, char_traits, string -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; @@ -44,6 +45,7 @@ int main(int argc, const char* argv[]) { auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document)); Render(screen, document); screen.Print(); + getchar(); return 0; } diff --git a/examples/dom/html_like.cpp b/examples/dom/html_like.cpp index f89bdef..1425273 100644 --- a/examples/dom/html_like.cpp +++ b/examples/dom/html_like.cpp @@ -44,7 +44,7 @@ int main(int argc, const char* argv[]) { paragraph(" A spinner "), spinner(6, i / 10)) | border; - auto screen = Screen::Create(Dimension::Full()); + auto screen = Screen::Create(Dimension::Fit(document)); Render(screen, document); std::cout << reset_position; screen.Print(); diff --git a/examples/dom/package_manager.cpp b/examples/dom/package_manager.cpp index e4978db..ee6434a 100644 --- a/examples/dom/package_manager.cpp +++ b/examples/dom/package_manager.cpp @@ -9,8 +9,8 @@ #include // for move #include // for vector -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render #include "ftxui/screen/color.hpp" // for Color, Color::Green, Color::Red, Color::RedLight int main(int argc, const char* argv[]) { diff --git a/examples/dom/paragraph.cpp b/examples/dom/paragraph.cpp index 2101402..0223e1a 100644 --- a/examples/dom/paragraph.cpp +++ b/examples/dom/paragraph.cpp @@ -1,35 +1,50 @@ -#include // for getchar -#include // for operator|, hflow, paragraph, border, Element, hbox, flex, vbox +#include // for operator""s, chrono_literals #include // for Full, Screen -#include // for allocator, string +#include // for cout, ostream +#include // for allocator, shared_ptr +#include // for string, operator<< +#include // for sleep_for -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/elements.hpp" // for hflow, paragraph, separator, hbox, vbox, filler, operator|, border, Element +#include "ftxui/dom/node.hpp" // for Render +#include "ftxui/screen/box.hpp" // for ftxui +using namespace std::chrono_literals; int main(int argc, const char* argv[]) { using namespace ftxui; std::string p = R"(In probability theory and statistics, Bayes' theorem (alternatively Bayes' law or Bayes' rule) describes the probability of an event, based on prior knowledge of conditions that might be related to the event. For example, if cancer is related to age, then, using Bayes' theorem, a person's age can be used to more accurately assess the probability that they have cancer, compared to the assessment of the probability of cancer made without knowledge of the person's age. One of the many applications of Bayes' theorem is Bayesian inference, a particular approach to statistical inference. When applied, the probabilities involved in Bayes' theorem may have different probability interpretations. With the Bayesian probability interpretation the theorem expresses how a subjective degree of belief should rationally change to account for availability of related evidence. Bayesian inference is fundamental to Bayesian statistics.)"; - auto document = vbox({ - hbox({ - hflow(paragraph(p)) | border, - hflow(paragraph(p)) | border, - hflow(paragraph(p)) | border, - }) | flex, - hbox({ - hflow(paragraph(p)) | border, - hflow(paragraph(p)) | border, - }) | flex, - hbox({ - hflow(paragraph(p)) | border, - }) | flex, - }); + std::string reset_position; + for (int i = 0;; ++i) { + auto document = vbox({ + hflow(paragraph(p)), + separator(), + hflow(paragraph(p)), + separator(), + hbox({ + hflow(paragraph(p)), + separator(), + hflow(paragraph(p)), + }), + }) | + border; - auto screen = Screen::Create(Dimension::Full(), Dimension::Full()); - Render(screen, document); - screen.Print(); - getchar(); + document = vbox(filler(), document); + + // auto screen = Screen::Create(Dimension::Fit(document)); + // Render(screen, document); + // screen.Print(); + // getchar(); + + auto screen = Screen::Create(Dimension::Full()); + Render(screen, document); + std::cout << reset_position; + screen.Print(); + reset_position = screen.ResetPosition(); + + std::this_thread::sleep_for(0.01s); + } return 0; } diff --git a/examples/dom/separator.cpp b/examples/dom/separator.cpp index 3013a5f..382e1a3 100644 --- a/examples/dom/separator.cpp +++ b/examples/dom/separator.cpp @@ -2,8 +2,8 @@ #include // for Full, Screen #include // for allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/size.cpp b/examples/dom/size.cpp index 2192e32..7ea574f 100644 --- a/examples/dom/size.cpp +++ b/examples/dom/size.cpp @@ -4,8 +4,8 @@ #include // for string, to_string #include // for move -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/spinner.cpp b/examples/dom/spinner.cpp index dc59915..6c64d2c 100644 --- a/examples/dom/spinner.cpp +++ b/examples/dom/spinner.cpp @@ -7,8 +7,8 @@ #include // for move #include // for vector -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/style_blink.cpp b/examples/dom/style_blink.cpp index cb1a2fa..889c22c 100644 --- a/examples/dom/style_blink.cpp +++ b/examples/dom/style_blink.cpp @@ -2,8 +2,8 @@ #include // for Full, Screen #include // for allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/style_bold.cpp b/examples/dom/style_bold.cpp index a984394..936a9d3 100644 --- a/examples/dom/style_bold.cpp +++ b/examples/dom/style_bold.cpp @@ -2,8 +2,8 @@ #include // for Full, Screen #include // for allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/style_dim.cpp b/examples/dom/style_dim.cpp index 7d8b9d1..f4073c7 100644 --- a/examples/dom/style_dim.cpp +++ b/examples/dom/style_dim.cpp @@ -2,8 +2,8 @@ #include // for Full, Screen #include // for allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/style_gallery.cpp b/examples/dom/style_gallery.cpp index 141cee9..c370357 100644 --- a/examples/dom/style_gallery.cpp +++ b/examples/dom/style_gallery.cpp @@ -2,9 +2,9 @@ #include // for Full, Screen #include // for allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui -#include "ftxui/screen/color.hpp" // for Color, Color::Blue +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render +#include "ftxui/screen/color.hpp" // for Color, Color::Blue int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/style_inverted.cpp b/examples/dom/style_inverted.cpp index 8bf2f17..b6d1a7f 100644 --- a/examples/dom/style_inverted.cpp +++ b/examples/dom/style_inverted.cpp @@ -2,8 +2,8 @@ #include // for Full, Screen #include // for allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/style_underlined.cpp b/examples/dom/style_underlined.cpp index 00df1f8..684b246 100644 --- a/examples/dom/style_underlined.cpp +++ b/examples/dom/style_underlined.cpp @@ -2,8 +2,8 @@ #include // for Full, Screen #include // for allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/table.cpp b/examples/dom/table.cpp index cb78d51..afc072f 100644 --- a/examples/dom/table.cpp +++ b/examples/dom/table.cpp @@ -5,8 +5,8 @@ #include // for basic_string, allocator, string #include // for vector -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render #include "ftxui/screen/color.hpp" // for Color, Color::Blue, Color::Cyan, Color::White int main(int argc, const char* argv[]) { diff --git a/examples/dom/vbox_hbox.cpp b/examples/dom/vbox_hbox.cpp index ec33c7a..7bf1372 100644 --- a/examples/dom/vbox_hbox.cpp +++ b/examples/dom/vbox_hbox.cpp @@ -3,8 +3,8 @@ #include // for Full, Screen #include // for allocator -#include "ftxui/dom/node.hpp" // for Render -#include "ftxui/screen/box.hpp" // for ftxui +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render int main(int argc, const char* argv[]) { using namespace ftxui; diff --git a/examples/dom/vflow.cpp b/examples/dom/vflow.cpp new file mode 100644 index 0000000..4f642ca --- /dev/null +++ b/examples/dom/vflow.cpp @@ -0,0 +1,52 @@ +#include // for size_t +#include // for getchar +#include // for operator|, Element, size, text, hcenter, Fit, vflow, window, EQUAL, bold, border, dim, HEIGHT, WIDTH +#include // for Full, Screen +#include // for allocator, shared_ptr +#include // for operator+, to_string, char_traits, string + +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render + +int main(int argc, const char* argv[]) { + using namespace ftxui; + auto make_box = [](size_t dimx, size_t dimy) { + std::string title = std::to_string(dimx) + "x" + std::to_string(dimy); + return window(text(title) | hcenter | bold, + text("content") | hcenter | dim) | + size(WIDTH, EQUAL, dimx) | size(HEIGHT, EQUAL, dimy); + }; + + auto document = vflow({ + make_box(7, 7), + make_box(7, 5), + make_box(5, 7), + make_box(10, 4), + make_box(10, 4), + make_box(10, 4), + make_box(10, 4), + make_box(11, 4), + make_box(11, 4), + make_box(11, 4), + make_box(11, 4), + make_box(12, 4), + make_box(12, 5), + make_box(12, 4), + make_box(13, 4), + make_box(13, 3), + make_box(13, 3), + make_box(10, 3), + }) | + border; + + auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document)); + Render(screen, document); + screen.Print(); + getchar(); + + 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. diff --git a/examples/dom/window.cpp b/examples/dom/window.cpp index ea2e917..95d6004 100644 --- a/examples/dom/window.cpp +++ b/examples/dom/window.cpp @@ -1,10 +1,10 @@ -#include -#include -#include +#include // for operator|, color, Element, bgcolor, graph, border +#include // for Fixed, Screen +#include // for vector -#include "ftxui/dom/node.hpp" -#include "ftxui/screen/box.hpp" -#include "ftxui/screen/color.hpp" +#include "ftxui/dom/flexbox_config.hpp" // for ftxui +#include "ftxui/dom/node.hpp" // for Render +#include "ftxui/screen/color.hpp" // for Color, Color::DarkBlue, Color::Green, Color::Red int main(void) { using namespace ftxui; diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index 0a77850..a9e7967 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -4,6 +4,7 @@ #include #include +#include "ftxui/dom/flexbox_config.hpp" #include "ftxui/dom/node.hpp" #include "ftxui/screen/box.hpp" #include "ftxui/screen/color.hpp" @@ -49,7 +50,11 @@ 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 paragraph(std::string text); +Element paragraphAlignLeft(std::string text); +Element paragraphAlignRight(std::string text); +Element paragraphAlignCenter(std::string text); +Element paragraphAlignJustify(std::string text); Element graph(GraphFunction); Element emptyElement(); @@ -69,8 +74,11 @@ Element bgcolor(Color, Element); Element hbox(Elements); Element vbox(Elements); Element dbox(Elements); +Element flexbox(Elements, FlexboxConfig config = FlexboxConfig()); Element gridbox(std::vector lines); -Element hflow(Elements); + +Element hflow(Elements); // Helper: default flexbox with row direction. +Element vflow(Elements); // Helper: default flexbox with column direction. // -- Flexibility --- // Define how to share the remaining space when not all of it is used inside a diff --git a/include/ftxui/dom/flexbox_config.hpp b/include/ftxui/dom/flexbox_config.hpp new file mode 100644 index 0000000..49a4b6a --- /dev/null +++ b/include/ftxui/dom/flexbox_config.hpp @@ -0,0 +1,115 @@ +#ifndef FTXUI_DOM_FLEXBOX_CONFIG_HPP +#define FTXUI_DOM_FLEXBOX_CONFIG_HPP + +/* + This replicate the CSS flexbox model. + See guide for documentation: + https://css-tricks.com/snippets/css/a-guide-to-flexbox/ +*/ + +namespace ftxui { + +struct FlexboxConfig { + /// This establishes the main-axis, thus defining the direction flex items are + /// placed in the flex container. Flexbox is (aside wrapping) single-direction + /// layout concept. Think of flex items as primarily laying out either in + /// horizontal rows or vertical columns. + enum class Direction { + Row, ///< Flex items are laid out in a row. + RowInversed, ///< Flex items are laid out in a row, but in reverse order. + Column, ///< Flex items are laid out in a column. + ColumnInversed ///< Flex items are laid out in a column, but in reverse + ///< order. + }; + Direction direction = Direction::Row; + + /// By default, flex items will all try to fit onto one line. You can change + /// that and allow the items to wrap as needed with this property. + enum class Wrap { + NoWrap, ///< Flex items will all try to fit onto one line. + Wrap, ///< Flex items will wrap onto multiple lines. + WrapInversed, ///< Flex items will wrap onto multiple lines, but in reverse + ///< order. + }; + Wrap wrap = Wrap::Wrap; + + /// This defines the alignment along the main axis. It helps distribute extra + /// free space leftover when either all the flex items on a line are + /// inflexible, or are flexible but have reached their maximum size. It also + /// exerts some control over the alignment of items when they overflow the + /// line. + enum class JustifyContent { + /// Items are aligned to the start of flexbox's direction. + FlexStart, + /// Items are aligned to the end of flexbox's direction. + FlexEnd, + /// Items are centered along the line. + Center, + /// Items are stretched to fill the line. + Stretch, + /// Items are evenly distributed in the line; first item is on the start + // line, last item on the end line + SpaceBetween, + /// Items are evenly distributed in the line with equal space around them. + /// Note that visually the spaces aren’t equal, since all the items have + /// equal space on both sides. The first item will have one unit of space + /// against the container edge, but two units of space between the next item + /// because that next item has its own spacing that applies. + SpaceAround, + /// Items are distributed so that the spacing between any two items (and the + /// space to the edges) is equal. + SpaceEvenly, + }; + JustifyContent justify_content = JustifyContent::FlexStart; + + /// This defines the default behavior for how flex items are laid out along + /// the cross axis on the current line. Think of it as the justify-content + /// version for the cross-axis (perpendicular to the main-axis). + enum class AlignItems { + FlexStart, ///< items are placed at the start of the cross axis. + FlexEnd, ///< items are placed at the end of the cross axis. + Center, ///< items are centered along the cross axis. + Stretch, ///< items are stretched to fill the cross axis. + }; + AlignItems align_items = AlignItems::FlexStart; + + // This aligns a flex container’s lines within when there is extra space in + // the cross-axis, similar to how justify-content aligns individual items + // within the main-axis. + enum class AlignContent { + FlexStart, ///< items are placed at the start of the cross axis. + FlexEnd, ///< items are placed at the end of the cross axis. + Center, ///< items are centered along the cross axis. + Stretch, ///< items are stretched to fill the cross axis. + SpaceBetween, ///< items are evenly distributed in the cross axis. + SpaceAround, ///< tems evenly distributed with equal space around each + ///< line. + SpaceEvenly, ///< items are evenly distributed in the cross axis with equal + ///< space around them. + }; + AlignContent align_content = AlignContent::FlexStart; + + int gap_x = 0; + int gap_y = 0; + + // Constructor pattern. For chained use like: + // ``` + // FlexboxConfig() + // .Set(FlexboxConfig::Direction::Row) + // .Set(FlexboxConfig::Wrap::Wrap); + // ``` + FlexboxConfig& Set(FlexboxConfig::Direction); + FlexboxConfig& Set(FlexboxConfig::Wrap); + FlexboxConfig& Set(FlexboxConfig::JustifyContent); + FlexboxConfig& Set(FlexboxConfig::AlignItems); + FlexboxConfig& Set(FlexboxConfig::AlignContent); + FlexboxConfig& SetGap(int gap_x, int gap_y); +}; + +} // namespace ftxui + +#endif /* end of include guard: FTXUI_DOM_FLEXBOX_CONFIG_HPP */ + +// 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. diff --git a/include/ftxui/dom/node.hpp b/include/ftxui/dom/node.hpp index c99e607..5bace34 100644 --- a/include/ftxui/dom/node.hpp +++ b/include/ftxui/dom/node.hpp @@ -35,6 +35,15 @@ class Node { // Step 3: Draw this element. virtual void Render(Screen& screen); + // Layout may not resolve within a single iteration for some elements. This + // allows them to request additionnal iterations. This signal must be + // forwarded to children at least once. + struct Status { + int iteration = 0; + bool need_iteration = false; + }; + virtual void Check(Status* status); + protected: Elements children_; Requirement requirement_; diff --git a/include/ftxui/dom/requirement.hpp b/include/ftxui/dom/requirement.hpp index c255423..8f7e8b8 100644 --- a/include/ftxui/dom/requirement.hpp +++ b/include/ftxui/dom/requirement.hpp @@ -28,7 +28,7 @@ struct Requirement { } // namespace ftxui -#endif /* end of include guard: FTXUI_REQUIREMENT_HPP */ +#endif /* end of include guard: FTXUI_DOM_REQUIREMENT_HPP */ // Copyright 2020 Arthur Sonzogni. All rights reserved. // Use of this source code is governed by the MIT license that can be found in diff --git a/include/ftxui/screen/box.hpp b/include/ftxui/screen/box.hpp index e421d1d..75cff81 100644 --- a/include/ftxui/screen/box.hpp +++ b/include/ftxui/screen/box.hpp @@ -11,6 +11,8 @@ struct Box { static Box Intersection(Box a, Box b); bool Contain(int x, int y); + bool operator==(const Box& other) const; + bool operator!=(const Box& other) const; }; } // namespace ftxui diff --git a/iwyu.imp b/iwyu.imp index 4f6902b..ff946c1 100644 --- a/iwyu.imp +++ b/iwyu.imp @@ -1,12 +1,17 @@ [ - { symbol: [ "char_traits", private, "", public ] }, - { symbol: [ "ECHO", private, "", public ] }, - { symbol: [ "ICANON", private, "", public ] }, - { symbol: [ "TCSANOW", private, "", public ] }, - { symbol: [ "VMIN", private, "", public ] }, - { symbol: [ "VTIME", private, "", public ] }, - { symbol: [ "__shared_ptr_access", private, "", public ] }, - { symbol: [ "termios", private, "", public ] }, - { symbol: ["__alloc_traits<>:value_type", private, "", public ] }, - { include: ["", private, "", public] }, + { include: ["", "private", "", "public"]}, + { include: ["", "private", "", "public"]}, + { include: ["", "private", "", "public"]}, + { include: ["", "private", "", "public"]}, + { include: ["", "private", "", "public"] }, + { symbol: [ "ftxui", "private", "", "public" ] }, + { symbol: [ "char_traits", "private", "", "public" ] }, + { symbol: [ "ECHO", "private", "", "public" ] }, + { symbol: [ "ICANON", "private", "", "public" ] }, + { symbol: [ "TCSANOW", "private", "", "public" ] }, + { symbol: [ "VMIN", "private", "", "public" ] }, + { symbol: [ "VTIME", "private", "", "public" ] }, + { symbol: [ "__shared_ptr_access", "private", "", "public" ] }, + { symbol: [ "termios", "private", "", "public" ] }, + { symbol: ["__alloc_traits<>:value_type", "private", "", "public" ] }, ] diff --git a/src/ftxui/component/component.cpp b/src/ftxui/component/component.cpp index db8a22d..21c70ca 100644 --- a/src/ftxui/component/component.cpp +++ b/src/ftxui/component/component.cpp @@ -1,5 +1,5 @@ #include // for size_t -#include // for find_if, max +#include // for find_if #include // for assert #include // for begin, end #include // 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 diff --git a/src/ftxui/component/container.cpp b/src/ftxui/component/container.cpp index 40e04ec..400884a 100644 --- a/src/ftxui/component/container.cpp +++ b/src/ftxui/component/container.cpp @@ -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 -#include // for __shared_ptr_access -#include // for string -#include // for move +#include // for max, min +#include // for __shared_ptr_access +#include // for string +#include // for move #include "ftxui/component/component.hpp" // for Maybe, Checkbox, Make, Radiobox, Vertical, Dropdown #include "ftxui/component/component_base.hpp" // for Component, ComponentBase diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index 27bf866..4017cc1 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -6,7 +6,7 @@ #include // for cout, ostream, basic_ostream, operator<<, endl, flush #include // for stack #include // for thread -#include // for move +#include // for swap, move #include // 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 // for select, FD_ISSET, FD_SET, FD_ZERO, fd_set -#include // for tcsetattr, termios, tcgetattr, TCSANOW, cc_t, ECHO, ICANON, VMIN, VTIME -#include // for STDIN_FILENO, read +#include // for tcsetattr, tcgetattr, cc_t +#include // for STDIN_FILENO, read #endif // Quick exit is missing in standard CLang headers diff --git a/src/ftxui/dom/benchmark_test.cpp b/src/ftxui/dom/benchmark_test.cpp index 20536a9..9871336 100644 --- a/src/ftxui/dom/benchmark_test.cpp +++ b/src/ftxui/dom/benchmark_test.cpp @@ -1,9 +1,9 @@ #include #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; diff --git a/src/ftxui/dom/blink.cpp b/src/ftxui/dom/blink.cpp index 97d5f7b..d426f6e 100644 --- a/src/ftxui/dom/blink.cpp +++ b/src/ftxui/dom/blink.cpp @@ -23,7 +23,6 @@ class Blink : public NodeDecorator { } }; - /// @brief The text drawn alternates in between visible and hidden. /// @ingroup dom Element blink(Element child) { diff --git a/src/ftxui/dom/border.cpp b/src/ftxui/dom/border.cpp index 82ff6d7..0b7d7a9 100644 --- a/src/ftxui/dom/border.cpp +++ b/src/ftxui/dom/border.cpp @@ -1,12 +1,12 @@ #include // for max #include // for begin, end #include // for allocator, make_shared, __shared_ptr_access -#include // for basic_string, string +#include // for string, basic_string #include // for move #include // 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 diff --git a/src/ftxui/dom/box_helper.hpp b/src/ftxui/dom/box_helper.hpp index a0e9db6..4d95624 100644 --- a/src/ftxui/dom/box_helper.hpp +++ b/src/ftxui/dom/box_helper.hpp @@ -25,4 +25,4 @@ void Compute(std::vector* 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. diff --git a/src/ftxui/dom/flex.cpp b/src/ftxui/dom/flex.cpp index 23d66c2..7ff7263 100644 --- a/src/ftxui/dom/flex.cpp +++ b/src/ftxui/dom/flex.cpp @@ -1,9 +1,9 @@ #include // for make_shared, __shared_ptr_access #include // for move -#include // for vector, __alloc_traits<>::value_type +#include // 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 diff --git a/src/ftxui/dom/flexbox.cpp b/src/ftxui/dom/flexbox.cpp new file mode 100644 index 0000000..884ee14 --- /dev/null +++ b/src/ftxui/dom/flexbox.cpp @@ -0,0 +1,239 @@ +#include // for size_t +#include // for min, max +#include // for __shared_ptr_access, shared_ptr, allocator_traits<>::value_type, make_shared +#include // for move, swap +#include // 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(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. diff --git a/src/ftxui/dom/flexbox_config.cpp b/src/ftxui/dom/flexbox_config.cpp new file mode 100644 index 0000000..acec612 --- /dev/null +++ b/src/ftxui/dom/flexbox_config.cpp @@ -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. diff --git a/src/ftxui/dom/flexbox_helper.cpp b/src/ftxui/dom/flexbox_helper.cpp new file mode 100644 index 0000000..31f2fc6 --- /dev/null +++ b/src/ftxui/dom/flexbox_helper.cpp @@ -0,0 +1,325 @@ +#include "ftxui/dom/flexbox_helper.hpp" + +#include // for size_t +#include // for min, max +#include // for allocator_traits<>::value_type +#include // 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 blocks; +}; + +void SetX(Global& global, std::vector lines) { + for (auto& line : lines) { + std::vector 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 lines) { + std::vector 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 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 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 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. diff --git a/src/ftxui/dom/flexbox_helper.hpp b/src/ftxui/dom/flexbox_helper.hpp new file mode 100644 index 0000000..733e25c --- /dev/null +++ b/src/ftxui/dom/flexbox_helper.hpp @@ -0,0 +1,45 @@ +#ifndef FTXUI_DOM_FLEXBOX_HELPER_HPP +#define FTXUI_DOM_FLEXBOX_HELPER_HPP + +#include +#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 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. diff --git a/src/ftxui/dom/flexbox_helper_test.cpp b/src/ftxui/dom/flexbox_helper_test.cpp new file mode 100644 index 0000000..ab6af7b --- /dev/null +++ b/src/ftxui/dom/flexbox_helper_test.cpp @@ -0,0 +1,233 @@ +#include // for Message +#include // for TestPartResult, SuiteApiResolver, TestFactoryImpl +#include // 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. diff --git a/src/ftxui/dom/flexbox_test.cpp b/src/ftxui/dom/flexbox_test.cpp new file mode 100644 index 0000000..4c71e99 --- /dev/null +++ b/src/ftxui/dom/flexbox_test.cpp @@ -0,0 +1,437 @@ +#include // for Message +#include // for SuiteApiResolver, TestFactoryImpl, TestPartResult +#include // 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. diff --git a/src/ftxui/dom/frame.cpp b/src/ftxui/dom/frame.cpp index 86eabff..4215ba5 100644 --- a/src/ftxui/dom/frame.cpp +++ b/src/ftxui/dom/frame.cpp @@ -1,10 +1,10 @@ #include // for max, min -#include // for make_shared, shared_ptr, __shared_ptr_access +#include // for make_shared, __shared_ptr_access #include // for move -#include // for vector, __alloc_traits<>::value_type +#include // 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 diff --git a/src/ftxui/dom/gauge_test.cpp b/src/ftxui/dom/gauge_test.cpp index 0eb3275..9ebb877 100644 --- a/src/ftxui/dom/gauge_test.cpp +++ b/src/ftxui/dom/gauge_test.cpp @@ -1,12 +1,12 @@ -#include // for Message -#include // for TestPartResult +#include // for Message +#include // for SuiteApiResolver, TestFactoryImpl, TestPartResult #include // 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; diff --git a/src/ftxui/dom/gridbox_test.cpp b/src/ftxui/dom/gridbox_test.cpp index da9bc39..48cde9c 100644 --- a/src/ftxui/dom/gridbox_test.cpp +++ b/src/ftxui/dom/gridbox_test.cpp @@ -5,10 +5,10 @@ #include // 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")}, diff --git a/src/ftxui/dom/hbox_test.cpp b/src/ftxui/dom/hbox_test.cpp index 45b003d..71c0d3f 100644 --- a/src/ftxui/dom/hbox_test.cpp +++ b/src/ftxui/dom/hbox_test.cpp @@ -4,10 +4,10 @@ #include // 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; diff --git a/src/ftxui/dom/hflow.cpp b/src/ftxui/dom/hflow.cpp deleted file mode 100644 index 8360786..0000000 --- a/src/ftxui/dom/hflow.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include // for max -#include // for __shared_ptr_access, make_shared, shared_ptr -#include // for move -#include // 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(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. diff --git a/src/ftxui/dom/node.cpp b/src/ftxui/dom/node.cpp index 6bf0294..fe65823 100644 --- a/src/ftxui/dom/node.cpp +++ b/src/ftxui/dom/node.cpp @@ -1,7 +1,7 @@ -#include +#include // 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 diff --git a/src/ftxui/dom/node_decorator.cpp b/src/ftxui/dom/node_decorator.cpp index 2134348..6b80989 100644 --- a/src/ftxui/dom/node_decorator.cpp +++ b/src/ftxui/dom/node_decorator.cpp @@ -1,5 +1,5 @@ #include // for __shared_ptr_access -#include // for __alloc_traits<>::value_type, vector +#include // for __alloc_traits<>::value_type #include "ftxui/dom/node_decorator.hpp" #include "ftxui/dom/requirement.hpp" // for Requirement diff --git a/src/ftxui/dom/paragraph.cpp b/src/ftxui/dom/paragraph.cpp index 3e9299b..4648a6d 100644 --- a/src/ftxui/dom/paragraph.cpp +++ b/src/ftxui/dom/paragraph.cpp @@ -1,38 +1,71 @@ -#include // for basic_istream, wstringstream -#include // for allocator, char_traits, getline, operator+, wstring, basic_string +#include // for basic_istream, stringstream +#include // for string, allocator, getline +#include // 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 diff --git a/src/ftxui/dom/size.cpp b/src/ftxui/dom/size.cpp index 9d66246..3087dbb 100644 --- a/src/ftxui/dom/size.cpp +++ b/src/ftxui/dom/size.cpp @@ -2,10 +2,10 @@ #include // for min, max #include // for make_shared, __shared_ptr_access #include // for move -#include // for __alloc_traits<>::value_type, vector +#include // 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 diff --git a/src/ftxui/dom/spinner.cpp b/src/ftxui/dom/spinner.cpp index 5cc4e3d..1bd3033 100644 --- a/src/ftxui/dom/spinner.cpp +++ b/src/ftxui/dom/spinner.cpp @@ -1,6 +1,6 @@ #include // for size_t #include // for allocator, allocator_traits<>::value_type -#include // for string +#include // for basic_string, string #include // for move #include // for vector, __alloc_traits<>::value_type diff --git a/src/ftxui/dom/table_test.cpp b/src/ftxui/dom/table_test.cpp index 41777cf..4de0ef4 100644 --- a/src/ftxui/dom/table_test.cpp +++ b/src/ftxui/dom/table_test.cpp @@ -3,9 +3,9 @@ #include // 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 diff --git a/src/ftxui/dom/text_test.cpp b/src/ftxui/dom/text_test.cpp index 5733d0a..8e6fe27 100644 --- a/src/ftxui/dom/text_test.cpp +++ b/src/ftxui/dom/text_test.cpp @@ -2,11 +2,11 @@ #include // for SuiteApiResolver, TestFactoryImpl, TestPartResult #include // 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; diff --git a/src/ftxui/dom/util.cpp b/src/ftxui/dom/util.cpp index 111a51a..2b63c51 100644 --- a/src/ftxui/dom/util.cpp +++ b/src/ftxui/dom/util.cpp @@ -1,12 +1,13 @@ #include // for min #include // for function -#include // for __shared_ptr_access +#include // for __shared_ptr_access, make_unique #include // for move #include // 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. diff --git a/src/ftxui/dom/vbox_test.cpp b/src/ftxui/dom/vbox_test.cpp index a16543b..9eee852 100644 --- a/src/ftxui/dom/vbox_test.cpp +++ b/src/ftxui/dom/vbox_test.cpp @@ -5,10 +5,10 @@ #include // 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; diff --git a/src/ftxui/screen/box.cpp b/src/ftxui/screen/box.cpp index 07826f8..c8b84c5 100644 --- a/src/ftxui/screen/box.cpp +++ b/src/ftxui/screen/box.cpp @@ -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.