diff --git a/CMakeLists.txt b/CMakeLists.txt index e56a391..dfd71f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.0) + enable_testing() add_subdirectory(ftxui) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c733e2d..5f62cc8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,3 +1,5 @@ +cmake_minimum_required(VERSION 3.0) + function(example name) add_executable(${name} ${name}.cpp) target_link_libraries(${name} PUBLIC ftxui) diff --git a/examples/component/CMakeLists.txt b/examples/component/CMakeLists.txt index b6b9615..38c0e98 100644 --- a/examples/component/CMakeLists.txt +++ b/examples/component/CMakeLists.txt @@ -1,6 +1,5 @@ -example(color) -example(gauge) example(input) example(menu) example(menu2) +example(menu_style) example(toggle) diff --git a/examples/component/menu2.cpp b/examples/component/menu2.cpp index f2244af..ee45654 100644 --- a/examples/component/menu2.cpp +++ b/examples/component/menu2.cpp @@ -70,7 +70,7 @@ class MyComponent : ComponentHorizontal { int main(int argc, const char *argv[]) { - ftxui::ScreenInteractive screen(60,17); + ftxui::ScreenInteractive screen(60,18); 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 new file mode 100644 index 0000000..f49baf6 --- /dev/null +++ b/examples/component/menu_style.cpp @@ -0,0 +1,87 @@ +#include +#include + +#include "ftxui/component/component_horizontal.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 : ComponentHorizontal { + public: + MyComponent(ftxui::component::Delegate* delegate) + : ComponentHorizontal(delegate), + menu_1(delegate->NewChild()), + menu_2(delegate->NewChild()), + menu_3(delegate->NewChild()), + menu_4(delegate->NewChild()), + menu_5(delegate->NewChild()), + menu_6(delegate->NewChild()) { + + for(Menu* menu : {&menu_1, &menu_2, &menu_3, &menu_4, &menu_5, &menu_6}) { + menu->entries = { + L"Monkey", + L"Dog", + L"Cat", + L"Bird", + L"Elephant", + }; + menu->on_enter = [this]() { on_enter(); }; + } + + menu_2.selected_style = color(Color::Blue); + menu_2.active_style = compose(bold, color(Color::Blue)); + + menu_3.selected_style = color(Color::Blue); + menu_3.active_style = bgcolor(Color::Blue); + + menu_4.selected_style = bgcolor(Color::Blue); + menu_4.active_style = bgcolor(Color::BlueLight); + + menu_5.normal_style = bgcolor(Color::Blue); + 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)); + + Focus(&menu_1); + } + + std::function on_enter = [](){}; + private: + Menu menu_1; + Menu menu_2; + Menu menu_3; + Menu menu_4; + Menu menu_5; + Menu menu_6; + + 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())) + ) + ); + } +}; + +int main(int argc, const char *argv[]) +{ + ftxui::ScreenInteractive screen(90,14); + MyComponent component(screen.delegate()); + component.on_enter = screen.ExitLoopClosure(); + screen.Loop(); +} diff --git a/examples/component/toggle.cpp b/examples/component/toggle.cpp index e032392..127cb8f 100644 --- a/examples/component/toggle.cpp +++ b/examples/component/toggle.cpp @@ -18,15 +18,12 @@ class MyComponent : ComponentVertical { : ComponentVertical(delegate), toggle_1(delegate->NewChild()), toggle_2(delegate->NewChild()), - toggle_3(delegate->NewChild()) { - toggle_1.on = L"On"; - toggle_1.off = L"Off"; - - toggle_2.on = L"Enabled"; - toggle_2.off = L"Disabled"; - - toggle_3.on = L"10€"; - toggle_3.off = L"0€"; + toggle_3(delegate->NewChild()), + toggle_4(delegate->NewChild()) { + toggle_1.options = {L"On", L"Off"}; + toggle_2.options = {L"Enabled", L"Disabled"}; + toggle_3.options = {L"10€", L"0€"}; + toggle_4.options = {L"Nothing", L"One element", L"Several elements"}; Focus(&toggle_1); } @@ -37,6 +34,7 @@ class MyComponent : ComponentVertical { Toggle toggle_1; Toggle toggle_2; Toggle toggle_3; + Toggle toggle_4; Element Render() override { return @@ -45,7 +43,8 @@ class MyComponent : ComponentVertical { text(L""), hbox(text(L" * Poweroff on startup : "), toggle_1.Render()), hbox(text(L" * Out of process : "), toggle_2.Render()), - hbox(text(L" * Price of the information : "), toggle_3.Render()) + hbox(text(L" * Price of the information : "), toggle_3.Render()), + hbox(text(L" * Number of elements : "), toggle_4.Render()) ); } @@ -63,7 +62,7 @@ class MyComponent : ComponentVertical { }; int main(int argc, const char* argv[]) { - ftxui::ScreenInteractive screen(50,5); + ftxui::ScreenInteractive screen(70,7); MyComponent component(screen.delegate()); component.on_enter = screen.ExitLoopClosure(); screen.Loop(); diff --git a/examples/dom/CMakeLists.txt b/examples/dom/CMakeLists.txt index 6aeb8a5..6d1bbb4 100644 --- a/examples/dom/CMakeLists.txt +++ b/examples/dom/CMakeLists.txt @@ -1,3 +1,6 @@ +example(color) +example(dbox) example(frame) +example(gauge) example(separator) example(vbox_hbox) diff --git a/examples/component/color.cpp b/examples/dom/color.cpp similarity index 100% rename from examples/component/color.cpp rename to examples/dom/color.cpp diff --git a/examples/dom/dbox.cpp b/examples/dom/dbox.cpp new file mode 100644 index 0000000..0a2e31d --- /dev/null +++ b/examples/dom/dbox.cpp @@ -0,0 +1,31 @@ +#include "ftxui/screen.hpp" +#include "ftxui/dom/elements.hpp" +#include + +int main(int argc, const char *argv[]) +{ + using namespace ftxui::dom; + auto document = + dbox( + frame( + vbox( + text(L"line_1"), + text(L"line_2"), + text(L"line_3"), + text(L"line_4"), + text(L"line_5") + ) + ), + center( + frame( + text(L"overlay") + ) + ) + ); + auto screen = ftxui::Screen::TerminalOutput(document); + Render(screen, document.get()); + + std::cout << screen.ToString(); + + return 0; +} diff --git a/examples/component/gauge.cpp b/examples/dom/gauge.cpp similarity index 83% rename from examples/component/gauge.cpp rename to examples/dom/gauge.cpp index 64ec77b..d1bee62 100644 --- a/examples/component/gauge.cpp +++ b/examples/dom/gauge.cpp @@ -7,9 +7,9 @@ int main(int argc, const char *argv[]) { - for(float percentage = 0; percentage <= 1.0; percentage+=0.001) { + for(float percentage = 0; percentage <= 1.0; percentage+=0.002) { std::wstring data_downloaded = - std::to_wstring(int(percentage * 44100)) + L"/44100"; + std::to_wstring(int(percentage * 5000)) + L"/5000"; using namespace ftxui::dom; auto document = hbox( diff --git a/ftxui/CMakeLists.txt b/ftxui/CMakeLists.txt index b877ae9..e5a8628 100644 --- a/ftxui/CMakeLists.txt +++ b/ftxui/CMakeLists.txt @@ -1,5 +1,4 @@ cmake_minimum_required(VERSION 3.0) -project(ftxui) add_library(ftxui src/ftxui/component/component.cpp @@ -13,6 +12,7 @@ add_library(ftxui src/ftxui/dom/bold.cpp src/ftxui/dom/color.cpp src/ftxui/dom/composite_decorator.cpp + src/ftxui/dom/dbox.cpp src/ftxui/dom/dim.cpp src/ftxui/dom/flex.cpp src/ftxui/dom/frame.cpp diff --git a/ftxui/include/ftxui/component/menu.hpp b/ftxui/include/ftxui/component/menu.hpp index 91aa484..b426c6d 100644 --- a/ftxui/include/ftxui/component/menu.hpp +++ b/ftxui/include/ftxui/component/menu.hpp @@ -2,6 +2,7 @@ #define FTXUI_COMPONENT_MENU #include "ftxui/component/component.hpp" +#include "ftxui/dom/elements.hpp" #include namespace ftxui { @@ -16,6 +17,10 @@ class Menu : public Component { std::vector entries = {}; int selected = 0; + dom::Decorator active_style = dom::inverted; + dom::Decorator selected_style = dom::bold; + dom::Decorator normal_style = dom::nothing; + // State update callback. std::function on_change = [](){}; std::function on_enter = [](){}; diff --git a/ftxui/include/ftxui/component/toggle.hpp b/ftxui/include/ftxui/component/toggle.hpp index 64eded3..5bfe6df 100644 --- a/ftxui/include/ftxui/component/toggle.hpp +++ b/ftxui/include/ftxui/component/toggle.hpp @@ -3,6 +3,7 @@ #include "ftxui/component/component.hpp" #include +#include namespace ftxui { namespace component { @@ -13,9 +14,8 @@ class Toggle : public Component { Toggle(Delegate*); // State. - bool activated = true; - std::wstring on = L"On"; - std::wstring off = L"Off"; + size_t activated = 0; + std::vector options = {L"On", L"Off"}; // Callback. std::function on_change = [](){}; diff --git a/ftxui/include/ftxui/dom/elements.hpp b/ftxui/include/ftxui/dom/elements.hpp index c2a7c73..d646f24 100644 --- a/ftxui/include/ftxui/dom/elements.hpp +++ b/ftxui/include/ftxui/dom/elements.hpp @@ -1,6 +1,8 @@ #ifndef FTXUI_DOM_ELEMENTS_HPP #define FTXUI_DOM_ELEMENTS_HPP +#include + #include "ftxui/color.hpp" #include "ftxui/dom/node.hpp" @@ -8,12 +10,14 @@ namespace ftxui { namespace dom { using Element = std::unique_ptr; +using Decorator = std::function; using Child = std::unique_ptr; using Children = std::vector; // --- Layout ---- Element vbox(Children); Element hbox(Children); +Element dbox(Children); Element flex(); Element flex(Element); @@ -32,6 +36,8 @@ Element underlined(Element); Element blink(Element); Element color(Color, Element); Element bgcolor(Color, Element); +Decorator color(Color); +Decorator bgcolor(Color); // --- Util --- Element hcenter(Element); @@ -40,6 +46,7 @@ Element center(Element); // --- Util --- Element nothing(Element element); +Decorator compose(Decorator, Decorator); template Children unpack(Args... args) { @@ -58,6 +65,11 @@ Element hbox(Args... children) { return hbox(unpack(std::forward(children)...)); } +template +Element dbox(Args... children) { + return dbox(unpack(std::forward(children)...)); +} + }; // namespace dom }; // namespace ftxui diff --git a/ftxui/src/ftxui/component/input.cpp b/ftxui/src/ftxui/component/input.cpp index 3da91e4..77e53d6 100644 --- a/ftxui/src/ftxui/component/input.cpp +++ b/ftxui/src/ftxui/component/input.cpp @@ -24,9 +24,6 @@ dom::Element Input::Render() { if (!is_focused) return flex(text(content)); - std::wstring sub_content = content; - size_t sub_cursor_position = cursor_position; - std::wstring part_before_cursor = content.substr(0,cursor_position); std::wstring part_at_cursor = cursor_position < (int)content.size() ? content.substr(cursor_position, 1) diff --git a/ftxui/src/ftxui/component/menu.cpp b/ftxui/src/ftxui/component/menu.cpp index ebc5e46..0c9ff32 100644 --- a/ftxui/src/ftxui/component/menu.cpp +++ b/ftxui/src/ftxui/component/menu.cpp @@ -14,12 +14,12 @@ dom::Element Menu::Render() { for (size_t i = 0; i < entries.size(); ++i) { if (size_t(selected) == i) { if (focused) - elements.push_back(inverted(text(L"> " + entries[i]))); + elements.push_back(active_style(text(L"> " + entries[i]))); else - elements.push_back(bold(text(L"> " + entries[i]))); + elements.push_back(selected_style(text(L"> " + entries[i]))); } else { - elements.push_back(text(L" " + entries[i])); + elements.push_back(normal_style(text(L" " + entries[i]))); } } return vbox(std::move(elements)); diff --git a/ftxui/src/ftxui/component/toggle.cpp b/ftxui/src/ftxui/component/toggle.cpp index 2552aff..ed8784f 100644 --- a/ftxui/src/ftxui/component/toggle.cpp +++ b/ftxui/src/ftxui/component/toggle.cpp @@ -11,32 +11,33 @@ dom::Element Toggle::Render() { Children children; children.push_back(text(L"[")); - if (activated) { - children.push_back(highlight(text(on))); - children.push_back(text(L"|")); - children.push_back(dim(text(off))); - } else { - children.push_back(dim(text(on))); - children.push_back(text(L"|")); - children.push_back(highlight(text(off))); + + for(size_t i = 0; i 0 && + (event == Event::ArrowLeft || event == Event::Character('h'))) { + activated--; + on_change(); + return true; + } + + if (activated < options.size() - 1 && + (event == Event::ArrowRight || event == Event::Character('l'))) { + activated++; + on_change(); + return true; } return false; diff --git a/ftxui/src/ftxui/dom/color.cpp b/ftxui/src/ftxui/dom/color.cpp index e0c44c9..984c616 100644 --- a/ftxui/src/ftxui/dom/color.cpp +++ b/ftxui/src/ftxui/dom/color.cpp @@ -47,5 +47,17 @@ std::unique_ptr bgcolor(Color c, Child child) { return std::make_unique(unpack(std::move(child)), c); } +Decorator color(Color c) { + return [c](Child child) { + return color(c, std::move(child)); + }; +} + +Decorator bgcolor(Color c) { + return [c](Child child) { + return bgcolor(c, std::move(child)); + }; +} + }; // namespace dom }; // namespace ftxui diff --git a/ftxui/src/ftxui/dom/dbox.cpp b/ftxui/src/ftxui/dom/dbox.cpp new file mode 100644 index 0000000..3704387 --- /dev/null +++ b/ftxui/src/ftxui/dom/dbox.cpp @@ -0,0 +1,37 @@ +#include "ftxui/dom/node.hpp" +#include "ftxui/dom/elements.hpp" + +namespace ftxui { +namespace dom { + +class DBox : public Node { + public: + DBox(Children children) : Node(std::move(children)) {} + ~DBox() {} + + void ComputeRequirement() override { + requirement_.min.x = 0; + requirement_.min.y = 0; + requirement_.flex.x = 1; + requirement_.flex.y = 0; + for (auto& child : children) { + child->ComputeRequirement(); + requirement_.min.x = std::max(requirement_.min.x, child->requirement().min.x); + requirement_.min.y = std::max(requirement_.min.y, child->requirement().min.y); + } + } + + void SetBox(Box box) override { + Node::SetBox(box); + + for (auto& child : children) + child->SetBox(box); + } +}; + +std::unique_ptr dbox(Children children) { + return std::make_unique(std::move(children)); +} + +}; // namespace dom +}; // namespace ftxui diff --git a/ftxui/src/ftxui/dom/util.cpp b/ftxui/src/ftxui/dom/util.cpp index b4dc76f..0e9468a 100644 --- a/ftxui/src/ftxui/dom/util.cpp +++ b/ftxui/src/ftxui/dom/util.cpp @@ -7,5 +7,14 @@ Element nothing(Element element) { return std::move(element); } +Decorator compose(Decorator a, Decorator b) { + return [ + a = std::move(a), + b = std::move(b) + ](Element element) { + return a(b(std::move(element))); + }; +} + }; // namespace dom }; // namespace ftxui