diff --git a/examples/component/CMakeLists.txt b/examples/component/CMakeLists.txt index 38c0e989..d1bab8c9 100644 --- a/examples/component/CMakeLists.txt +++ b/examples/component/CMakeLists.txt @@ -3,3 +3,4 @@ example(menu) example(menu2) example(menu_style) example(toggle) +example(tab) diff --git a/examples/component/input.cpp b/examples/component/input.cpp index 72ea012d..1a521bd4 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -45,7 +45,7 @@ class MyComponent : ComponentVertical { }; int main(int argc, const char* argv[]) { - ftxui::ScreenInteractive screen(60, 5); + auto screen = ftxui::ScreenInteractive::TerminalOutput(); MyComponent component(screen.delegate()); component.on_enter = screen.ExitLoopClosure(); screen.Loop(); diff --git a/examples/component/menu.cpp b/examples/component/menu.cpp index 2dd62340..177bd74a 100644 --- a/examples/component/menu.cpp +++ b/examples/component/menu.cpp @@ -7,7 +7,7 @@ int main(int argc, const char *argv[]) { - ftxui::ScreenInteractive screen(30,3); + auto screen = ftxui::ScreenInteractive::FixedSize(30, 3); ftxui::component::Menu menu(screen.delegate()); menu.entries = { L"entry 1", diff --git a/examples/component/menu2.cpp b/examples/component/menu2.cpp index ee45654b..50a7ba04 100644 --- a/examples/component/menu2.cpp +++ b/examples/component/menu2.cpp @@ -40,29 +40,25 @@ class MyComponent : ComponentHorizontal { // -------- Top panel -------------- hbox( // -------- Left Menu -------------- - flex( - vbox( - hcenter(bold(text(L"Percentage by 10%"))), - separator(), - left_menu.Render() - ) - ), + vbox( + hcenter(bold(text(L"Percentage by 10%"))), + separator(), + left_menu.Render() + ) | flex, // -------- Right Menu -------------- - flex( - vbox( - hcenter(bold(text(L"Percentage by 1%"))), - separator(), - right_menu.Render() - ) - ), - flex() + vbox( + hcenter(bold(text(L"Percentage by 1%"))), + separator(), + right_menu.Render() + ) | flex, + filler() ), separator(), // -------- Bottom panel -------------- - flex(vbox( + vbox( hbox(text(L" gauge : "), gauge(sum/100.0)), hbox(text(L" text : "), text(to_wstring(std::to_string(sum) + " %"))) - )) + ) | flex ) ); } @@ -70,7 +66,7 @@ class MyComponent : ComponentHorizontal { int main(int argc, const char *argv[]) { - ftxui::ScreenInteractive screen(60,18); + auto screen = ftxui::ScreenInteractive::TerminalOutput(); MyComponent component(screen.delegate()); component.on_enter = screen.ExitLoopClosure(); screen.Loop(); diff --git a/examples/component/menu_style.cpp b/examples/component/menu_style.cpp index f49baf62..342e2e9f 100644 --- a/examples/component/menu_style.cpp +++ b/examples/component/menu_style.cpp @@ -33,7 +33,7 @@ class MyComponent : ComponentHorizontal { } menu_2.selected_style = color(Color::Blue); - menu_2.active_style = compose(bold, color(Color::Blue)); + menu_2.active_style = bold | color(Color::Blue); menu_3.selected_style = color(Color::Blue); menu_3.active_style = bgcolor(Color::Blue); @@ -45,9 +45,9 @@ class MyComponent : ComponentHorizontal { menu_5.selected_style = bgcolor(Color::Yellow); menu_5.active_style = bgcolor(Color::Red); - menu_6.normal_style = compose(dim, color(Color::Blue)); - menu_6.selected_style = compose(nothing, color(Color::Blue)); - menu_6.active_style = compose(bold, color(Color::Blue)); + menu_6.normal_style = dim | color(Color::Blue); + menu_6.selected_style = color(Color::Blue); + menu_6.active_style = bold | color(Color::Blue); Focus(&menu_1); } @@ -63,24 +63,21 @@ class MyComponent : ComponentHorizontal { Element Render() override { return - vbox( hbox( - flex(frame(center(text(L" menu_1 ")), menu_1.Render())), - flex(frame(center(text(L" menu_2 ")), menu_2.Render())), - flex(frame(center(text(L" menu_3 ")), menu_3.Render())) - ), - hbox( - flex(frame(center(text(L" menu_4 ")), menu_4.Render())), - flex(frame(center(text(L" menu_5 ")), menu_5.Render())), - flex(frame(center(text(L" menu_6 ")), menu_6.Render())) - ) - ); + menu_1.Render() | flex, separator(), + menu_2.Render() | flex, separator(), + menu_3.Render() | flex, separator(), + menu_4.Render() | flex, separator(), + menu_5.Render() | flex, separator(), + menu_6.Render() | flex + ) | frame; } }; int main(int argc, const char *argv[]) { - ftxui::ScreenInteractive screen(90,14); + //auto screen = ftxui::ScreenInteractive::TerminalOutput(); + auto screen = ftxui::ScreenInteractive::Fullscreen(); MyComponent component(screen.delegate()); component.on_enter = screen.ExitLoopClosure(); screen.Loop(); diff --git a/examples/component/tab.cpp b/examples/component/tab.cpp new file mode 100644 index 00000000..228aaa40 --- /dev/null +++ b/examples/component/tab.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include "ftxui/component/component_vertical.hpp" +#include "ftxui/component/toggle.hpp" +#include "ftxui/component/menu.hpp" +#include "ftxui/screen_interactive.hpp" +#include "ftxui/util/string.hpp" + +using namespace ftxui; +using namespace ftxui::component; +using namespace ftxui::dom; + +class MyComponent : ComponentVertical { + public: + MyComponent(ftxui::component::Delegate* delegate) + : ComponentVertical(delegate), + toggle(delegate->NewChild()), + menu(delegate->NewChild()) { + + toggle.options = {L" left ", L" middle ", L" end "}; + menu.entries = {L" top ", L" middle ", L" bottom "}; + Focus(&toggle); + } + + std::function on_enter = [](){}; + private: + Toggle toggle; + Menu menu; + + Element Render() override { + return + vbox( + hbox(frame(toggle.Render()), filler()), + frame(menu.Render())); + } +}; + + +int main(int argc, const char *argv[]) +{ + auto screen = ftxui::ScreenInteractive::TerminalOutput(); + MyComponent component(screen.delegate()); + component.on_enter = screen.ExitLoopClosure(); + screen.Loop(); +} diff --git a/examples/component/toggle.cpp b/examples/component/toggle.cpp index 127cb8f7..8a6d0fd8 100644 --- a/examples/component/toggle.cpp +++ b/examples/component/toggle.cpp @@ -62,7 +62,7 @@ class MyComponent : ComponentVertical { }; int main(int argc, const char* argv[]) { - ftxui::ScreenInteractive screen(70,7); + auto screen = ftxui::ScreenInteractive::TerminalOutput(); MyComponent component(screen.delegate()); component.on_enter = screen.ExitLoopClosure(); screen.Loop(); diff --git a/examples/dom/CMakeLists.txt b/examples/dom/CMakeLists.txt index 6d1bbb4b..7a4ff10f 100644 --- a/examples/dom/CMakeLists.txt +++ b/examples/dom/CMakeLists.txt @@ -2,5 +2,6 @@ example(color) example(dbox) example(frame) example(gauge) +example(package_manager) example(separator) example(vbox_hbox) diff --git a/examples/dom/color.cpp b/examples/dom/color.cpp index 5eb429ca..d8f80651 100644 --- a/examples/dom/color.cpp +++ b/examples/dom/color.cpp @@ -46,7 +46,7 @@ int main(int argc, const char *argv[]) bgcolor(Color::Yellow, text(L"Yellow")), bgcolor(Color::YellowLight, text(L"YellowLight")) ), - flex() + filler() ); auto screen = ftxui::Screen::TerminalOutput(document); diff --git a/examples/dom/frame.cpp b/examples/dom/frame.cpp index 329d0041..52ec6df8 100644 --- a/examples/dom/frame.cpp +++ b/examples/dom/frame.cpp @@ -10,7 +10,7 @@ int main(int argc, const char *argv[]) using namespace ftxui::dom; auto document = hbox( - frame(hcenter(text(L" main frame ")), + window(hcenter(text(L" main frame ")), vbox( text(L"Line 1"), text(L"Line 2"), @@ -23,14 +23,14 @@ int main(int argc, const char *argv[]) ) ), hbox( - frame(text(L"frame 2"), + window(text(L"frame 2"), vbox( text(L"Line 4"), text(L"Line 5"), text(L"Line 6") ) ), - frame(text(L"frame 3"), + window(text(L"frame 3"), vbox( text(L"Line 7"), text(L"Line 8"), @@ -41,7 +41,7 @@ int main(int argc, const char *argv[]) text(L"footer footer footer footer footer") ) ), - flex() + filler() ); auto screen = ftxui::Screen::TerminalOutput(document); Render(screen, document.get()); diff --git a/examples/dom/gauge.cpp b/examples/dom/gauge.cpp index d1bee62b..54ea3ae4 100644 --- a/examples/dom/gauge.cpp +++ b/examples/dom/gauge.cpp @@ -14,7 +14,7 @@ int main(int argc, const char *argv[]) auto document = hbox( text(L"downloading:"), - flex(gauge(percentage)), + gauge(percentage) | flex, text(L" " + data_downloaded) ); auto screen = ftxui::Screen(100, 1); diff --git a/examples/dom/package_manager.cpp b/examples/dom/package_manager.cpp new file mode 100644 index 00000000..2ba50093 --- /dev/null +++ b/examples/dom/package_manager.cpp @@ -0,0 +1,123 @@ +#include +#include +#include + +#include "ftxui/screen.hpp" +#include "ftxui/dom/elements.hpp" +#include "ftxui/util/string.hpp" +#include +#include + +using namespace ftxui; +using namespace ftxui::dom; + +int main(int argc, const char *argv[]) +{ + struct Task { + std::wstring name; + int number_of_threads; + int downloaded; + int size; + }; + + std::list remaining_tasks = { + {L"contact server " , 10 , 0 , 6*25} , + {L"download index.html " , 10 , 0 , 9*25} , + {L"download script.js " , 1 , 0 , 3*25} , + {L"download style.js " , 1 , 0 , 4*25} , + {L"download image.png " , 1 , 0 , 5*25} , + {L"download big_1.png " , 1 , 0 , 30*25} , + {L"download icon_1.png " , 1 , 0 , 7*25} , + {L"download icon_2.png " , 1 , 0 , 8*25} , + {L"download big_2.png " , 1 , 0 , 30*25} , + {L"download small_1.png " , 1 , 0 , 10*25} , + {L"download small_2.png " , 1 , 0 , 11*25} , + {L"download small_3.png " , 1 , 0 , 12*25} , + }; + + std::list displayed_task; + + int remaining_threads = 12; + std::string reset_position; + + int nb_queued = remaining_tasks.size(); + int nb_active = 0; + int nb_done = 0; + + auto to_text = [](int number) { + std::wstring t = to_wstring(std::to_string(number)); + while(t.size() < 3) + t = L" " + t; + return text(t); + }; + + for(;;) { + std::vector entries; + for(auto& task : displayed_task) { + auto style = (task.downloaded == task.size) ? dim : bold; + entries.push_back( + hbox( + text(task.name) | style, + separator(), + to_text(task.downloaded), + text(L"/"), + to_text(task.size), + separator(), + gauge(task.downloaded / float(task.size)) + ) + ); + } + + auto document = + vbox( + window(text(L" Task "), + vbox(std::move(entries)) + ), + hbox( + window(text(L" Summary "), + vbox( + hbox(text(L"- done: "), to_text(nb_done) | bold) | color(Color::Green), + hbox(text(L"- active: "), to_text(nb_active) | bold ) | color(Color::RedLight), + hbox(text(L"- queue: "), to_text(nb_queued) | bold) | color(Color::Red) + ) + ) + ) + ); + + // Draw. + //if (step != 0) screen.Clear(); + auto screen = ftxui::Screen::TerminalOutput(document); + Render(screen, document.get()); + std::cout << reset_position << screen.ToString() << std::flush; + reset_position = screen.ResetPosition(); + + // Simulate time. + using namespace std::chrono_literals; + std::this_thread::sleep_for(0.01s); + + if (nb_active + nb_queued == 0) + break; + + // Update the model. + for(auto& task : displayed_task) { + if (task.downloaded != task.size) { + task.downloaded++; + } else if (task.number_of_threads) { + remaining_threads += task.number_of_threads; + task.number_of_threads = 0; + nb_active--; + nb_done++; + } + } + + if (remaining_tasks.size() && + remaining_tasks.front().number_of_threads <= remaining_threads) { + remaining_threads -= remaining_tasks.front().number_of_threads; + displayed_task.push_back(remaining_tasks.front()); + remaining_tasks.pop_front(); + nb_queued--; + nb_active++; + } + } + std::cout << std::endl; +} diff --git a/examples/dom/separator.cpp b/examples/dom/separator.cpp index 79f1f9af..d25a14e8 100644 --- a/examples/dom/separator.cpp +++ b/examples/dom/separator.cpp @@ -10,7 +10,7 @@ int main(int argc, const char *argv[]) text(L"left-column"), separator(), flex(vbox( - flex(center(text(L"right-column"))), + center(text(L"right-column")) | flex, separator(), center(text(L"bottom-column")) )) diff --git a/examples/dom/vbox_hbox.cpp b/examples/dom/vbox_hbox.cpp index 77bdc358..281f791a 100644 --- a/examples/dom/vbox_hbox.cpp +++ b/examples/dom/vbox_hbox.cpp @@ -9,21 +9,21 @@ int main(int argc, const char *argv[]) vbox( hbox( text(L"north-west"), - flex(), + filler(), text(L"north-east") ), - flex(), + filler(), hbox( hbox( - flex(), + filler(), text(L"center"), - flex() + filler() ) ), - flex(), + filler(), hbox( text(L"south-west"), - flex(), + filler(), text(L"south-east") ) ); diff --git a/examples/print_key_press.cpp b/examples/print_key_press.cpp index eb5e927c..6bdf7f7e 100644 --- a/examples/print_key_press.cpp +++ b/examples/print_key_press.cpp @@ -42,7 +42,7 @@ class DrawKey : public ftxui::component::Component { }; int main(int argc, const char* argv[]) { - ftxui::ScreenInteractive screen(80,10); + auto screen = ftxui::ScreenInteractive::FixedSize(80,10); DrawKey draw_key(screen.delegate()); screen.Loop(); } diff --git a/ftxui/include/ftxui/README.md b/ftxui/include/ftxui/README.md index 0ad8ad1b..d1d2855c 100644 --- a/ftxui/include/ftxui/README.md +++ b/ftxui/include/ftxui/README.md @@ -22,7 +22,7 @@ ## Level 3: ftxui::component::Component A hierarchical set of component. A component render itself by producing ftxui::dom::Node in Component::Render(). - + Some component can handle events: * keyboard * mouse diff --git a/ftxui/include/ftxui/dom/elements.hpp b/ftxui/include/ftxui/dom/elements.hpp index d646f248..6609fd7c 100644 --- a/ftxui/include/ftxui/dom/elements.hpp +++ b/ftxui/include/ftxui/dom/elements.hpp @@ -18,17 +18,17 @@ using Children = std::vector; Element vbox(Children); Element hbox(Children); Element dbox(Children); -Element flex(); +Element filler(); Element flex(Element); // --- Widget -- Element text(std::wstring text); Element separator(); Element gauge(float ratio); -Element frame(Child); -Element frame(Child title, Child content); +Element frame(Element); +Element window(Child title, Child content); -// -- Style --- +// -- Decorator --- Element bold(Element); Element dim(Element); Element inverted(Element); @@ -46,8 +46,13 @@ Element center(Element); // --- Util --- Element nothing(Element element); -Decorator compose(Decorator, Decorator); +// Pipe elements into decorator togethers. +// Examples: text("ftxui") | bold | underlined; +Element operator|(Element, Decorator); +Decorator operator|(Decorator, Decorator); + +// Make container able to take several children. template Children unpack(Args... args) { Children vec; @@ -55,20 +60,15 @@ Children unpack(Args... args) { return vec; } -template -Element vbox(Args... children) { - return vbox(unpack(std::forward(children)...)); -} +#define TAKE_ANY_ARGS(container) \ + template \ + Element container(Args... children) { \ + return container(unpack(std::forward(children)...)); \ + } \ -template -Element hbox(Args... children) { - return hbox(unpack(std::forward(children)...)); -} - -template -Element dbox(Args... children) { - return dbox(unpack(std::forward(children)...)); -} +TAKE_ANY_ARGS(vbox) +TAKE_ANY_ARGS(hbox) +TAKE_ANY_ARGS(dbox) }; // namespace dom }; // namespace ftxui diff --git a/ftxui/include/ftxui/screen.hpp b/ftxui/include/ftxui/screen.hpp index f01e410e..6a858b52 100644 --- a/ftxui/include/ftxui/screen.hpp +++ b/ftxui/include/ftxui/screen.hpp @@ -49,7 +49,7 @@ class Screen { // Fill with space. void Clear(); - private: + protected: size_t dimx_; size_t dimy_; std::vector> pixels_; diff --git a/ftxui/include/ftxui/screen_interactive.hpp b/ftxui/include/ftxui/screen_interactive.hpp index 1acf9e1a..6ae80be9 100644 --- a/ftxui/include/ftxui/screen_interactive.hpp +++ b/ftxui/include/ftxui/screen_interactive.hpp @@ -14,7 +14,10 @@ namespace component { class ScreenInteractive : public Screen { public: - ScreenInteractive(size_t dimx, size_t dimy); + static ScreenInteractive FixedSize(size_t dimx, size_t dimy); + static ScreenInteractive Fullscreen(); + static ScreenInteractive TerminalOutput(); + ~ScreenInteractive(); component::Delegate* delegate(); void Loop(); @@ -27,6 +30,15 @@ class ScreenInteractive : public Screen { void Clear(); void Draw(); bool quit_ = false; + + enum class Dimension { + Fixed, + TerminalOutput, + Fullscreen, + }; + Dimension dimension_ = Dimension::Fixed; + + ScreenInteractive(size_t dimx, size_t dimy, Dimension dimension); }; } // namespace ftxui diff --git a/ftxui/src/ftxui/component/input.cpp b/ftxui/src/ftxui/component/input.cpp index 77e53d66..adfd82aa 100644 --- a/ftxui/src/ftxui/component/input.cpp +++ b/ftxui/src/ftxui/component/input.cpp @@ -15,14 +15,14 @@ dom::Element Input::Render() { // Placeholder. if (content.size() == 0) { if (is_focused) - return flex(inverted(dim(text(placeholder)))); + return text(placeholder) | dim | inverted | flex; else - return flex(dim(text(placeholder))); + return text(placeholder) | dim | flex; } // Not focused. if (!is_focused) - return flex(text(content)); + return text(content) | flex; std::wstring part_before_cursor = content.substr(0,cursor_position); std::wstring part_at_cursor = cursor_position < (int)content.size() @@ -31,11 +31,12 @@ dom::Element Input::Render() { std::wstring part_after_cursor = cursor_position < (int)content.size() - 1 ? content.substr(cursor_position + 1) : L""; - return flex(inverted(hbox( // - text(part_before_cursor), // - underlined(text(part_at_cursor)), // - text(part_after_cursor) // - ))); // + return + hbox( + text(part_before_cursor), + text(part_at_cursor) | underlined, + text(part_after_cursor) + ) | flex | inverted; } bool Input::OnEvent(Event event) { std::wstring c; diff --git a/ftxui/src/ftxui/component/toggle.cpp b/ftxui/src/ftxui/component/toggle.cpp index ed8784f6..39f12e70 100644 --- a/ftxui/src/ftxui/component/toggle.cpp +++ b/ftxui/src/ftxui/component/toggle.cpp @@ -10,18 +10,16 @@ dom::Element Toggle::Render() { auto highlight = Focused() ? inverted : bold; Children children; - children.push_back(text(L"[")); for(size_t i = 0; i hcenter(Element child) { - return hbox(flex(), std::move(child), flex()); + return hbox(filler(), std::move(child), filler()); } std::unique_ptr vcenter(Element child) { - return vbox(flex(), std::move(child), flex()); + return vbox(filler(), std::move(child), filler()); } std::unique_ptr center(Element child) { diff --git a/ftxui/src/ftxui/dom/flex.cpp b/ftxui/src/ftxui/dom/flex.cpp index 9684dff7..7741a779 100644 --- a/ftxui/src/ftxui/dom/flex.cpp +++ b/ftxui/src/ftxui/dom/flex.cpp @@ -27,7 +27,7 @@ class Flex : public Node { } }; -std::unique_ptr flex() { +std::unique_ptr filler() { return std::make_unique(); } diff --git a/ftxui/src/ftxui/dom/frame.cpp b/ftxui/src/ftxui/dom/frame.cpp index 6112b701..1bbe056f 100644 --- a/ftxui/src/ftxui/dom/frame.cpp +++ b/ftxui/src/ftxui/dom/frame.cpp @@ -84,9 +84,15 @@ std::unique_ptr frame(Child child) { return std::make_unique(unpack(std::move(child))); } -std::unique_ptr frame(Child title, Child content) { +std::unique_ptr window(Child title, Child content) { return std::make_unique(unpack(std::move(content), std::move(title))); } +Decorator boxed() { + return [](Child child) { + return frame(std::move(child)); + }; +} + }; // namespace dom }; // namespace ftxui diff --git a/ftxui/src/ftxui/dom/hbox_test.cpp b/ftxui/src/ftxui/dom/hbox_test.cpp index 83f775bd..859852ef 100644 --- a/ftxui/src/ftxui/dom/hbox_test.cpp +++ b/ftxui/src/ftxui/dom/hbox_test.cpp @@ -62,7 +62,7 @@ TEST(HBoxTest, ScreenBigger2) { TEST(HBoxTest, ScreenSmaller1Flex) { auto root = hbox( text(L"text_1"), - flex(), + filler(), text(L"text_2") ); Screen screen(11,1); @@ -74,7 +74,7 @@ TEST(HBoxTest, ScreenSmaller1Flex) { TEST(HBoxTest, ScreenSmaller2Flex) { auto root = hbox( text(L"text_1"), - flex(), + filler(), text(L"text_2") ); Screen screen(10,1); @@ -86,7 +86,7 @@ TEST(HBoxTest, ScreenSmaller2Flex) { TEST(HBoxTest, ScreenFitFlex) { auto root = hbox( text(L"text_1"), - flex(), + filler(), text(L"text_2") ); Screen screen(12,1); @@ -98,7 +98,7 @@ TEST(HBoxTest, ScreenFitFlex) { TEST(HBoxTest, ScreenBigger1Flex) { auto root = hbox( text(L"text_1"), - flex(), + filler(), text(L"text_2") ); Screen screen(13,1); @@ -110,7 +110,7 @@ TEST(HBoxTest, ScreenBigger1Flex) { TEST(HBoxTest, ScreenBigger2Flex) { auto root = hbox( text(L"text_1"), - flex(), + filler(), text(L"text_2") ); Screen screen(14,1); diff --git a/ftxui/src/ftxui/dom/separator.cpp b/ftxui/src/ftxui/dom/separator.cpp index db22aeb1..b298e06c 100644 --- a/ftxui/src/ftxui/dom/separator.cpp +++ b/ftxui/src/ftxui/dom/separator.cpp @@ -19,7 +19,7 @@ class Separator : public Node { wchar_t c = U'+'; if (is_line && !is_column) c = U'─'; - else if (!is_line && is_column) + else c = U'│'; for (int y = box_.top; y <= box_.bottom; ++y) { diff --git a/ftxui/src/ftxui/dom/util.cpp b/ftxui/src/ftxui/dom/util.cpp index 0e9468a5..afdb3dec 100644 --- a/ftxui/src/ftxui/dom/util.cpp +++ b/ftxui/src/ftxui/dom/util.cpp @@ -12,9 +12,17 @@ Decorator compose(Decorator a, Decorator b) { a = std::move(a), b = std::move(b) ](Element element) { - return a(b(std::move(element))); + return b(a(std::move(element))); }; } -}; // namespace dom -}; // namespace ftxui +Decorator operator|(Decorator a, Decorator b) { + return compose(a, b); +} + +Element operator|(Element e, Decorator d) { + return d(std::move(e)); +} + +} // namespace dom +} // namespace ftxui diff --git a/ftxui/src/ftxui/dom/vbox_test.cpp b/ftxui/src/ftxui/dom/vbox_test.cpp index 912c3491..3e1ef453 100644 --- a/ftxui/src/ftxui/dom/vbox_test.cpp +++ b/ftxui/src/ftxui/dom/vbox_test.cpp @@ -37,7 +37,7 @@ TEST(VBoxTest, ScreenBigger2) { } TEST(VBoxTest, ScreenSmaller1Flex) { - auto root = vbox(text(L"text_1"), flex(), text(L"text_2")); + auto root = vbox(text(L"text_1"), filler(), text(L"text_2")); Screen screen(6, 1); Render(screen, root.get()); @@ -45,7 +45,7 @@ TEST(VBoxTest, ScreenSmaller1Flex) { } TEST(VBoxTest, ScreenFitFlex) { - auto root = vbox(text(L"text_1"), flex(), text(L"text_2")); + auto root = vbox(text(L"text_1"), filler(), text(L"text_2")); Screen screen(6, 2); Render(screen, root.get()); @@ -53,14 +53,14 @@ TEST(VBoxTest, ScreenFitFlex) { } TEST(VBoxTest, ScreenBigger1Flex) { - auto root = vbox(text(L"text_1"), flex(), text(L"text_2")); + auto root = vbox(text(L"text_1"), filler(), text(L"text_2")); Screen screen(6, 3); Render(screen, root.get()); EXPECT_EQ("text_1\n \ntext_2", screen.ToString()); } TEST(VBoxTest, ScreenBigger2Flex) { - auto root = vbox(text(L"text_1"), flex(), text(L"text_2")); + auto root = vbox(text(L"text_1"), filler(), text(L"text_2")); Screen screen(6, 4); Render(screen, root.get()); diff --git a/ftxui/src/ftxui/screen.cpp b/ftxui/src/ftxui/screen.cpp index c2e05166..27cc923f 100644 --- a/ftxui/src/ftxui/screen.cpp +++ b/ftxui/src/ftxui/screen.cpp @@ -12,28 +12,19 @@ Screen::Screen(size_t dimx, size_t dimy) void UpdatePixelStyle(std::wstringstream& ss, Pixel& previous, Pixel& next) { if (next.bold != previous.bold) - ss << (next.bold ? L"\e[1m" : L"\e[0m"); - - if (next.inverted != previous.inverted) - ss << (next.inverted ? L"\e[7m" : L"\e[27m"); - - if (next.underlined != previous.underlined) - ss << (next.underlined ? L"\e[4m" : L"\e[24m"); - + ss << (next.bold ? L"\e[1m" : L"\e[22m"); // Can't use 21 here. if (next.dim != previous.dim) ss << (next.dim ? L"\e[2m" : L"\e[22m"); - + if (next.underlined != previous.underlined) + ss << (next.underlined ? L"\e[4m" : L"\e[24m"); if (next.blink != previous.blink) ss << (next.blink ? L"\e[5m" : L"\e[25m"); - - if (next.foreground_color != previous.foreground_color) { - ss << L"\e[" + to_wstring(std::to_string((uint8_t)next.foreground_color)) + - L"m"; - } - if (next.background_color != previous.background_color) { - ss << L"\e[" + - to_wstring(std::to_string(10 + (uint8_t)next.background_color)) + - L"m"; + if (next.inverted != previous.inverted) + ss << (next.inverted ? L"\e[7m" : L"\e[27m"); + if (next.foreground_color != previous.foreground_color || + next.background_color != previous.background_color) { + ss << L"\e[" + to_wstring(std::to_string((uint8_t)next.foreground_color)) + L"m"; + ss << L"\e[" + to_wstring(std::to_string(10 + (uint8_t)next.background_color)) + L"m"; } previous = next; @@ -49,8 +40,11 @@ std::string Screen::ToString() { UpdatePixelStyle(ss, previous_pixel, pixels_[y][x]); ss << pixels_[y][x].character; } + if (y + 1 < dimy_) ss << '\n'; + Pixel final_pixel; + UpdatePixelStyle(ss, previous_pixel, final_pixel); } return to_string(ss.str()); diff --git a/ftxui/src/ftxui/screen_interactive.cpp b/ftxui/src/ftxui/screen_interactive.cpp index 0ffd1fd5..8ba98fb1 100644 --- a/ftxui/src/ftxui/screen_interactive.cpp +++ b/ftxui/src/ftxui/screen_interactive.cpp @@ -1,6 +1,8 @@ #include "ftxui/screen_interactive.hpp" + #include "ftxui/component/component.hpp" #include "ftxui/component/delegate.hpp" +#include "ftxui/terminal.hpp" #include #include #include @@ -86,10 +88,27 @@ class ScreenInteractive::Delegate : public component::Delegate { component::Component* component() override { return component_; } }; -ScreenInteractive::ScreenInteractive(size_t dimx, size_t dimy) - : Screen(dimx, dimy), delegate_(new Delegate) {} +ScreenInteractive::ScreenInteractive(size_t dimx, + size_t dimy, + Dimension dimension) + : Screen(dimx, dimy), delegate_(new Delegate), dimension_(dimension) {} ScreenInteractive::~ScreenInteractive() {} +// static +ScreenInteractive ScreenInteractive::FixedSize(size_t dimx, size_t dimy) { + return ScreenInteractive(dimx, dimy, Dimension::Fixed); +} + +// static +ScreenInteractive ScreenInteractive::Fullscreen() { + return ScreenInteractive(0, 0, Dimension::Fullscreen); +} + +// static +ScreenInteractive ScreenInteractive::TerminalOutput() { + return ScreenInteractive(0, 0, Dimension::TerminalOutput); +} + void ScreenInteractive::Loop() { std::cout << "\033[?9h"; /* Send Mouse Row & Column on Button Press */ std::cout << "\033[?1000h"; /* Send Mouse X & Y on button press and release */ @@ -110,14 +129,12 @@ void ScreenInteractive::Loop() { tcsetattr(STDIN_FILENO, TCSANOW, &terminal_configuration_new); Draw(); - while (!quit_) { + while(!quit_) { delegate_->OnEvent(GetEvent()); - Clear(); Draw(); - } - std::cout << std::endl; - //Clear(); + } while(!quit_); + //std::cout << std::endl; // Restore the old terminal configuration. tcsetattr(STDIN_FILENO, TCSANOW, &terminal_configuration_old); @@ -125,6 +142,29 @@ void ScreenInteractive::Loop() { void ScreenInteractive::Draw() { auto document = delegate_->component()->Render(); + size_t dimx; + size_t dimy; + switch(dimension_) { + case Dimension::Fixed: + break; + case Dimension::TerminalOutput: + document->ComputeRequirement(); + dimx = Terminal::Size().dimx; + dimy = document->requirement().min.y; + break; + case Dimension::Fullscreen: + document->ComputeRequirement(); + dimx = Terminal::Size().dimx; + dimy = Terminal::Size().dimy; + break; + } + + if (dimx != dimx_ || dimy != dimy_) { + dimx_ = dimx; + dimy_ = dimy; + pixels_ = std::vector>(dimy, std::vector(dimx)); + } + Render(*this, document.get()); std::cout << ToString() << std::flush; }