From fddcbdea653c33bcb760110a31b9158747a12343 Mon Sep 17 00:00:00 2001 From: Arthur Sonzogni Date: Sat, 19 Jan 2019 22:06:05 +0100 Subject: [PATCH] Add "frame" : scrollable area. --- README.md | 8 +- examples/component/CMakeLists.txt | 2 + examples/component/checkbox.cpp | 11 +- examples/component/checkbox_in_frame.cpp | 42 ++++++ examples/component/gallery.cpp | 2 +- examples/component/input.cpp | 2 +- examples/component/menu2.cpp | 2 +- examples/component/menu_style.cpp | 12 +- examples/component/radiobox_in_frame.cpp | 34 +++++ examples/component/tab_horizontal.cpp | 2 +- examples/dom/CMakeLists.txt | 2 +- examples/dom/{frame.cpp => border.cpp} | 12 +- examples/dom/dbox.cpp | 22 ++- examples/dom/spinner.cpp | 2 +- ftxui/CMakeLists.txt | 4 +- ftxui/include/ftxui/component/container.hpp | 6 +- ftxui/include/ftxui/component/menu.hpp | 4 +- ftxui/include/ftxui/component/toggle.hpp | 6 +- ftxui/include/ftxui/dom/box.hpp | 15 -- ftxui/include/ftxui/dom/elements.hpp | 40 +++--- ftxui/include/ftxui/dom/node.hpp | 2 +- ftxui/include/ftxui/dom/requirement.hpp | 10 ++ ftxui/include/ftxui/screen/box.hpp | 17 +++ ftxui/include/ftxui/screen/screen.hpp | 2 + ftxui/include/ftxui/util/autoreset.hpp | 22 +++ ftxui/src/ftxui/component/checkbox.cpp | 7 +- ftxui/src/ftxui/component/container.cpp | 6 +- ftxui/src/ftxui/component/input.cpp | 7 +- ftxui/src/ftxui/component/menu.cpp | 21 ++- ftxui/src/ftxui/component/radiobox.cpp | 8 +- ftxui/src/ftxui/component/toggle.cpp | 11 +- ftxui/src/ftxui/dom/blink.cpp | 4 +- ftxui/src/ftxui/dom/bold.cpp | 4 +- ftxui/src/ftxui/dom/border.cpp | 88 ++++++++++++ ftxui/src/ftxui/dom/color.cpp | 8 +- ftxui/src/ftxui/dom/dbox.cpp | 5 + ftxui/src/ftxui/dom/dim.cpp | 4 +- ftxui/src/ftxui/dom/frame.cpp | 149 ++++++++++++-------- ftxui/src/ftxui/dom/gauge.cpp | 8 +- ftxui/src/ftxui/dom/hbox.cpp | 22 +-- ftxui/src/ftxui/dom/inverted.cpp | 4 +- ftxui/src/ftxui/dom/node.cpp | 9 +- ftxui/src/ftxui/dom/separator.cpp | 8 +- ftxui/src/ftxui/dom/size.cpp | 1 + ftxui/src/ftxui/dom/text.cpp | 8 +- ftxui/src/ftxui/dom/underlined.cpp | 4 +- ftxui/src/ftxui/dom/vbox.cpp | 22 +-- ftxui/src/ftxui/screen/box.cpp | 14 ++ ftxui/src/ftxui/screen/screen.cpp | 19 ++- tutorial.md | 62 ++++---- 50 files changed, 543 insertions(+), 243 deletions(-) create mode 100644 examples/component/checkbox_in_frame.cpp create mode 100644 examples/component/radiobox_in_frame.cpp rename examples/dom/{frame.cpp => border.cpp} (86%) delete mode 100644 ftxui/include/ftxui/dom/box.hpp create mode 100644 ftxui/include/ftxui/screen/box.hpp create mode 100644 ftxui/include/ftxui/util/autoreset.hpp create mode 100644 ftxui/src/ftxui/dom/border.cpp create mode 100644 ftxui/src/ftxui/screen/box.cpp diff --git a/README.md b/README.md index d8b96ff..24ea2ec 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ A C++ library for making text based user interface. ~~~cpp vbox( hbox( - text(L"left") | frame, - text(L"middle") | frame | flex, - text(L"right") | frame + text(L"left") | border, + text(L"middle") | border | flex, + text(L"right") | border ), - gauge(0.5) | frame + gauge(0.5) | border ) ~~~ diff --git a/examples/component/CMakeLists.txt b/examples/component/CMakeLists.txt index ef7bf5f..5c05f26 100644 --- a/examples/component/CMakeLists.txt +++ b/examples/component/CMakeLists.txt @@ -5,12 +5,14 @@ function(example name) endfunction(example) example(checkbox) +example(checkbox_in_frame) example(gallery) example(input) example(menu) example(menu2) example(menu_style) example(radiobox) +example(radiobox_in_frame) example(tab_horizontal) example(tab_vertical) example(toggle) diff --git a/examples/component/checkbox.cpp b/examples/component/checkbox.cpp index 467b6c8..1f87bcf 100644 --- a/examples/component/checkbox.cpp +++ b/examples/component/checkbox.cpp @@ -22,19 +22,10 @@ class MyComponent : public Component { box_3_.label = L"Use WebAssembly"; box_3_.state = true; } - - Element Render() { - return - window(text(L" Checkbox "), - hbox( - container_.Render() - ) - ); - } }; int main(int argc, const char *argv[]) { - auto screen = ScreenInteractive::FixedSize(30,5); + auto screen = ScreenInteractive::TerminalOutput(); MyComponent component; screen.Loop(&component); return 0; diff --git a/examples/component/checkbox_in_frame.cpp b/examples/component/checkbox_in_frame.cpp new file mode 100644 index 0000000..90bd7e3 --- /dev/null +++ b/examples/component/checkbox_in_frame.cpp @@ -0,0 +1,42 @@ +#include "ftxui/component/checkbox.hpp" +#include "ftxui/component/container.hpp" +#include "ftxui/component/input.hpp" +#include "ftxui/component/menu.hpp" +#include "ftxui/component/checkbox.hpp" +#include "ftxui/component/screen_interactive.hpp" +#include "ftxui/component/toggle.hpp" +#include "ftxui/screen/string.hpp" + +using namespace ftxui; + +class MyComponent : public Component { + public: + MyComponent() { + Add(&container); + checkbox.resize(30); + for(int i = 0; i checkbox; + Container container = Container::Vertical(); +}; + +int main(int argc, const char* argv[]) { + auto screen = ScreenInteractive::FitComponent(); + MyComponent component; + screen.Loop(&component); + + return 0; +} diff --git a/examples/component/gallery.cpp b/examples/component/gallery.cpp index a9b497d..6bea97b 100644 --- a/examples/component/gallery.cpp +++ b/examples/component/gallery.cpp @@ -69,7 +69,7 @@ class MyComponent : public Component { Render(L"radiobox", radiobox), separator(), Render(L"input", input) - ) | frame; + ) | border | frame | size(10,10) | border; } }; diff --git a/examples/component/input.cpp b/examples/component/input.cpp index 2fb6c2b..c402242 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -30,7 +30,7 @@ class MyComponent : public Component { Element Render() override { return - frame( + border( vbox( hbox(text(L" input_1 : "), input_1.Render()), hbox(text(L" input_2 : "), input_2.Render()), diff --git a/examples/component/menu2.cpp b/examples/component/menu2.cpp index 0091dfb..f5665fc 100644 --- a/examples/component/menu2.cpp +++ b/examples/component/menu2.cpp @@ -34,7 +34,7 @@ class MyComponent : public Component { Element Render() override { int sum = left_menu.selected * 10 + right_menu.selected; return - frame( + border( vbox( // -------- Top panel -------------- hbox( diff --git a/examples/component/menu_style.cpp b/examples/component/menu_style.cpp index e6b347f..0f6b6fe 100644 --- a/examples/component/menu_style.cpp +++ b/examples/component/menu_style.cpp @@ -26,21 +26,21 @@ class MyComponent : public Component { } menu_2.selected_style = color(Color::Blue); - menu_2.active_style = bold | color(Color::Blue); + menu_2.focused_style = bold | color(Color::Blue); menu_3.selected_style = color(Color::Blue); - menu_3.active_style = bgcolor(Color::Blue); + menu_3.focused_style = bgcolor(Color::Blue); menu_4.selected_style = bgcolor(Color::Blue); - menu_4.active_style = bgcolor(Color::BlueLight); + menu_4.focused_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_5.focused_style = bgcolor(Color::Red); menu_6.normal_style = dim | color(Color::Blue); menu_6.selected_style = color(Color::Blue); - menu_6.active_style = bold | color(Color::Blue); + menu_6.focused_style = bold | color(Color::Blue); } std::function on_enter = [](){}; @@ -62,7 +62,7 @@ class MyComponent : public Component { menu_4.Render() | flex, separator(), menu_5.Render() | flex, separator(), menu_6.Render() | flex - ) | frame; + ) | border; } }; diff --git a/examples/component/radiobox_in_frame.cpp b/examples/component/radiobox_in_frame.cpp new file mode 100644 index 0000000..b72b4bd --- /dev/null +++ b/examples/component/radiobox_in_frame.cpp @@ -0,0 +1,34 @@ +#include "ftxui/component/checkbox.hpp" +#include "ftxui/component/container.hpp" +#include "ftxui/component/input.hpp" +#include "ftxui/component/menu.hpp" +#include "ftxui/component/radiobox.hpp" +#include "ftxui/component/screen_interactive.hpp" +#include "ftxui/component/toggle.hpp" +#include "ftxui/screen/string.hpp" + +using namespace ftxui; + +class MyComponent : public Component { + RadioBox radiobox; + + public: + MyComponent() { + for(int i = 0; i<30; ++i) { + radiobox.entries.push_back(L"RadioBox " + to_wstring(i)); + } + Add(&radiobox); + } + + Element Render() override { + return radiobox.Render() | frame | size(20,10) | border; + } +}; + +int main(int argc, const char* argv[]) { + auto screen = ScreenInteractive::FitComponent(); + MyComponent component; + screen.Loop(&component); + + return 0; +} diff --git a/examples/component/tab_horizontal.cpp b/examples/component/tab_horizontal.cpp index 097cc3b..47c8ec1 100644 --- a/examples/component/tab_horizontal.cpp +++ b/examples/component/tab_horizontal.cpp @@ -50,7 +50,7 @@ class MyComponent : public Component { toggle_.Render(), separator(), tab_container_.Render() - ) | frame; + ) | border; } private: diff --git a/examples/dom/CMakeLists.txt b/examples/dom/CMakeLists.txt index 258e655..81493f4 100644 --- a/examples/dom/CMakeLists.txt +++ b/examples/dom/CMakeLists.txt @@ -4,7 +4,7 @@ function(example name) endfunction(example) example(dbox) -example(frame) +example(border) example(gauge) example(package_manager) example(separator) diff --git a/examples/dom/frame.cpp b/examples/dom/border.cpp similarity index 86% rename from examples/dom/frame.cpp rename to examples/dom/border.cpp index dd2f49e..6a5a2f9 100644 --- a/examples/dom/frame.cpp +++ b/examples/dom/border.cpp @@ -15,13 +15,11 @@ int main(int argc, const char *argv[]) text(L"Line 1"), text(L"Line 2"), text(L"Line 3"), - frame( - vbox( - text(L"Line 4"), - text(L"Line 5"), - text(L"Line 6") - ) - ), + vbox( + text(L"Line 4"), + text(L"Line 5"), + text(L"Line 6") + ) | border, hbox( window(text(L"frame 2"), vbox( diff --git a/examples/dom/dbox.cpp b/examples/dom/dbox.cpp index cdfbed9..cb8b07e 100644 --- a/examples/dom/dbox.cpp +++ b/examples/dom/dbox.cpp @@ -7,20 +7,14 @@ int main(int argc, const char *argv[]) using namespace ftxui; 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") - ) - ) + vbox( + text(L"line_1"), + text(L"line_2"), + text(L"line_3"), + text(L"line_4"), + text(L"line_5") + ) | border, + text(L"overlay") | border | center ); auto screen = Screen::TerminalOutput(document); Render(screen, document.get()); diff --git a/examples/dom/spinner.cpp b/examples/dom/spinner.cpp index b748e7e..5e943c5 100644 --- a/examples/dom/spinner.cpp +++ b/examples/dom/spinner.cpp @@ -24,7 +24,7 @@ int main(int argc, const char *argv[]) ) ); } - auto document = hbox(vbox(std::move(entries)) | frame, filler()); + auto document = hbox(vbox(std::move(entries)) | border, filler()); auto screen = Screen::TerminalOutput(document); Render(screen, document.get()); std::cout << reset_position << screen.ToString() << std::flush; diff --git a/ftxui/CMakeLists.txt b/ftxui/CMakeLists.txt index fd8d885..6001d8b 100644 --- a/ftxui/CMakeLists.txt +++ b/ftxui/CMakeLists.txt @@ -1,14 +1,16 @@ cmake_minimum_required(VERSION 3.0) add_library(screen + src/ftxui/screen/box.cpp src/ftxui/screen/screen.cpp - src/ftxui/screen/terminal.cpp src/ftxui/screen/string.cpp + src/ftxui/screen/terminal.cpp ) add_library(dom src/ftxui/dom/blink.cpp src/ftxui/dom/bold.cpp + src/ftxui/dom/border.cpp src/ftxui/dom/color.cpp src/ftxui/dom/composite_decorator.cpp src/ftxui/dom/dbox.cpp diff --git a/ftxui/include/ftxui/component/container.hpp b/ftxui/include/ftxui/component/container.hpp index 69628b5..270009f 100644 --- a/ftxui/include/ftxui/component/container.hpp +++ b/ftxui/include/ftxui/component/container.hpp @@ -13,7 +13,7 @@ class Container : public Component { public: static Container Vertical(); static Container Horizontal(); - static Container Tab(size_t* selector); + static Container Tab(int* selector); ~Container() override = default; @@ -36,8 +36,8 @@ class Container : public Component { Element TabRender(); RenderHandler render_handler_; - size_t selected_ = 0; - size_t* selector_ = &selected_; + int selected_ = 0; + int* selector_ = &selected_; }; } // namespace ftxui diff --git a/ftxui/include/ftxui/component/menu.hpp b/ftxui/include/ftxui/component/menu.hpp index 8f4d102..166800f 100644 --- a/ftxui/include/ftxui/component/menu.hpp +++ b/ftxui/include/ftxui/component/menu.hpp @@ -15,9 +15,9 @@ class Menu : public Component { // State. std::vector entries = {}; - size_t selected = 0; + int selected = 0; - Decorator active_style = inverted; + Decorator focused_style = inverted; Decorator selected_style = bold; Decorator normal_style = nothing; diff --git a/ftxui/include/ftxui/component/toggle.hpp b/ftxui/include/ftxui/component/toggle.hpp index 659d2e2..2c60a17 100644 --- a/ftxui/include/ftxui/component/toggle.hpp +++ b/ftxui/include/ftxui/component/toggle.hpp @@ -13,9 +13,13 @@ class Toggle : public Component { ~Toggle() override = default; // State. - size_t selected = 0; + int selected = 0; std::vector entries = {L"On", L"Off"}; + Decorator focused_style = inverted; + Decorator selected_style = bold; + Decorator normal_style = dim; + // Callback. std::function on_change = [](){}; diff --git a/ftxui/include/ftxui/dom/box.hpp b/ftxui/include/ftxui/dom/box.hpp deleted file mode 100644 index ec1e5b4..0000000 --- a/ftxui/include/ftxui/dom/box.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef FTXUI_DOM_BOX_HPP -#define FTXUI_DOM_BOX_HPP - -namespace ftxui { - -struct Box { - int left; - int right; - int top; - int bottom; -}; - -}; // namespace ftxui - -#endif /* end of include guard: FTXUI_DOM_BOX_HPP */ diff --git a/ftxui/include/ftxui/dom/elements.hpp b/ftxui/include/ftxui/dom/elements.hpp index 6e6845f..8949f8c 100644 --- a/ftxui/include/ftxui/dom/elements.hpp +++ b/ftxui/include/ftxui/dom/elements.hpp @@ -12,21 +12,11 @@ using Element = std::unique_ptr; using Elements = std::vector; using Decorator = std::function; -// --- Layout ---- -Element vbox(Elements); -Element hbox(Elements); -Element dbox(Elements); - -// -- Flexibility -- -Element filler(); -Element flex(Element); -Decorator size(size_t width, size_t height); - -// --- Widget -- +// --- Widget --- Element text(std::wstring text); Element separator(); Element gauge(float ratio); -Element frame(Element); +Element border(Element); Element window(Element title, Element content); Element spinner(int charset_index, size_t image_index); @@ -36,19 +26,37 @@ Element dim(Element); Element inverted(Element); Element underlined(Element); Element blink(Element); - Decorator color(Color); Decorator bgcolor(Color); Element color(Color, Element); Element bgcolor(Color, Element); -// --- Util --- +// --- Layout --- +// Horizontal, Vertical or stacked set of elements. +Element vbox(Elements); +Element hbox(Elements); +Element dbox(Elements); + +// -- Flexibility --- +// Define how to share the remaining space when not all of it is used inside a +// container. +Element filler(); +Element flex(Element); +Decorator size(size_t width, size_t height); + +// --- Frame --- +// A frame is a scrollable area. The internal area is potentially larger than +// the external one. The internal area is scrolled in order to make visible the +// focused element. +Element frame(Element); +Element focus(Element); +Element select(Element); + +// --- Util -------------------------------------------------------------------- Element hcenter(Element); Element vcenter(Element); Element center(Element); Element align_right(Element); - -// --- Util --- Element nothing(Element element); // Pipe elements into decorator togethers. diff --git a/ftxui/include/ftxui/dom/node.hpp b/ftxui/include/ftxui/dom/node.hpp index 1283262..9b9471e 100644 --- a/ftxui/include/ftxui/dom/node.hpp +++ b/ftxui/include/ftxui/dom/node.hpp @@ -4,8 +4,8 @@ #include #include -#include "ftxui/dom/box.hpp" #include "ftxui/dom/requirement.hpp" +#include "ftxui/screen/box.hpp" #include "ftxui/screen/screen.hpp" namespace ftxui { diff --git a/ftxui/include/ftxui/dom/requirement.hpp b/ftxui/include/ftxui/dom/requirement.hpp index 1a32e74..b8cf19b 100644 --- a/ftxui/include/ftxui/dom/requirement.hpp +++ b/ftxui/include/ftxui/dom/requirement.hpp @@ -1,6 +1,8 @@ #ifndef FTXUI_DOM_REQUIREMENT_HPP #define FTXUI_DOM_REQUIREMENT_HPP +#include "ftxui/screen/box.hpp" + namespace ftxui { struct Requirement { @@ -9,6 +11,14 @@ struct Requirement { // How much flexibility is given to the component. struct { int x = 0; int y = 0; } flex; + + // Frame. + enum Selection { + NORMAL = 0, + SELECTED = 1, + FOCUSED = 2, + } selection = NORMAL; + Box selected_box; }; }; // namespace ftxui diff --git a/ftxui/include/ftxui/screen/box.hpp b/ftxui/include/ftxui/screen/box.hpp new file mode 100644 index 0000000..e1906be --- /dev/null +++ b/ftxui/include/ftxui/screen/box.hpp @@ -0,0 +1,17 @@ +#ifndef FTXUI_SCREEN_BOX_HPP +#define FTXUI_SCREEN_BOX_HPP + +namespace ftxui { + +struct Box { + int x_min; + int x_max; + int y_min; + int y_max; + + static Box Intersection(Box a, Box b); +}; + +}; // namespace ftxui + +#endif /* end of include guard: FTXUI_SCREEN_BOX_HPP */ diff --git a/ftxui/include/ftxui/screen/screen.hpp b/ftxui/include/ftxui/screen/screen.hpp index d491d9d..4a89fb5 100644 --- a/ftxui/include/ftxui/screen/screen.hpp +++ b/ftxui/include/ftxui/screen/screen.hpp @@ -6,6 +6,7 @@ #include #include "ftxui/screen/color.hpp" +#include "ftxui/screen/box.hpp" namespace ftxui { class Node; @@ -51,6 +52,7 @@ class Screen { void Clear(); void ApplyShader(); + Box stencil; protected: size_t dimx_; diff --git a/ftxui/include/ftxui/util/autoreset.hpp b/ftxui/include/ftxui/util/autoreset.hpp new file mode 100644 index 0000000..412b62e --- /dev/null +++ b/ftxui/include/ftxui/util/autoreset.hpp @@ -0,0 +1,22 @@ +#ifndef FTXUI_UTIL_AUTORESET_HPP +#define FTXUI_UTIL_AUTORESET_HPP + +namespace ftxui { + +template +class AutoReset { + public: + AutoReset(T* variable, T new_value) + : variable_(variable), + previous_value_(std::move(*variable)) { + *variable_ = std::move(new_value); + } + ~AutoReset() { *variable_ = std::move(previous_value_); } + private: + T* variable_; + T previous_value_; +}; + +} // namespace ftxui + +#endif /* end of include guard: FTXUI_UTIL_AUTORESET_HPP */ diff --git a/ftxui/src/ftxui/component/checkbox.cpp b/ftxui/src/ftxui/component/checkbox.cpp index b6b6a49..cf0de37 100644 --- a/ftxui/src/ftxui/component/checkbox.cpp +++ b/ftxui/src/ftxui/component/checkbox.cpp @@ -4,8 +4,11 @@ namespace ftxui { Element CheckBox::Render() { - auto style = Focused() ? focused_style : unfocused_style; - return hbox(text(state ? checked : unchecked), text(label) | style); + bool is_focused = Focused(); + auto style = is_focused ? focused_style : unfocused_style; + auto focus_management = is_focused ? focus : state ? select : nothing; + return hbox(text(state ? checked : unchecked), + text(label) | style | focus_management); } bool CheckBox::OnEvent(Event event) { diff --git a/ftxui/src/ftxui/component/container.cpp b/ftxui/src/ftxui/component/container.cpp index fad00d1..929d378 100644 --- a/ftxui/src/ftxui/component/container.cpp +++ b/ftxui/src/ftxui/component/container.cpp @@ -19,7 +19,7 @@ Container Container::Vertical() { } // static -Container Container::Tab(size_t* selector) { +Container Container::Tab(int* selector) { Container container; container.event_handler_ = &Container::TabEvent; container.render_handler_ = &Container::TabRender; @@ -53,7 +53,7 @@ bool Container::VerticalEvent(Event event) { // Left pressed ? if (event == Event::ArrowDown || event == Event::Character('j')) { - if (selected_ != children_.size() - 1) { + if (selected_ != int(children_.size()) - 1) { selected_++; return true; } @@ -74,7 +74,7 @@ bool Container::HorizontalEvent(Event event) { // Left pressed ? if (event == Event::ArrowRight || event == Event::Character('l')) { - if (selected_ != children_.size() - 1) { + if (selected_ != int(children_.size()) - 1) { selected_++; return true; } diff --git a/ftxui/src/ftxui/component/input.cpp b/ftxui/src/ftxui/component/input.cpp index 8e9bb6f..20f41a1 100644 --- a/ftxui/src/ftxui/component/input.cpp +++ b/ftxui/src/ftxui/component/input.cpp @@ -26,12 +26,15 @@ Element Input::Render() { std::wstring part_after_cursor = cursor_position < (int)content.size() - 1 ? content.substr(cursor_position + 1) : L""; + auto focused = + is_focused ? focus : select; + return hbox( text(part_before_cursor), - text(part_at_cursor) | underlined, + text(part_at_cursor) | underlined | focused, text(part_after_cursor) - ) | flex | inverted; + ) | flex | inverted | frame; } bool Input::OnEvent(Event event) { std::wstring c; diff --git a/ftxui/src/ftxui/component/menu.cpp b/ftxui/src/ftxui/component/menu.cpp index a703bc2..93bf988 100644 --- a/ftxui/src/ftxui/component/menu.cpp +++ b/ftxui/src/ftxui/component/menu.cpp @@ -6,17 +6,14 @@ namespace ftxui { Element Menu::Render() { std::vector elements; - bool focused = Focused(); + bool is_focused = Focused(); for (size_t i = 0; i < entries.size(); ++i) { - if (selected == i) { - if (focused) - elements.push_back(active_style(text(L"> " + entries[i]))); - else - elements.push_back(selected_style(text(L"> " + entries[i]))); - } - else { - elements.push_back(normal_style(text(L" " + entries[i]))); - } + auto style = (selected != int(i)) + ? normal_style + : is_focused ? focused_style : selected_style; + auto focused = (selected != int(i)) ? nothing : is_focused ? focus : select; + auto icon = (selected != int(i)) ? L" " : L"> "; + elements.push_back(text(icon + entries[i]) | style | focused); } return vbox(std::move(elements)); } @@ -25,12 +22,12 @@ bool Menu::OnEvent(Event event) { if (!Focused()) return false; - size_t new_selected = selected; + int new_selected = selected; if (event == Event::ArrowUp || event == Event::Character('k')) new_selected--; if (event == Event::ArrowDown || event == Event::Character('j')) new_selected++; - new_selected = std::max(size_t(0), std::min(entries.size()-size_t(1), new_selected)); + new_selected = std::max(0, std::min(int(entries.size()) - 1, new_selected)); if (selected != new_selected) { selected = new_selected; diff --git a/ftxui/src/ftxui/component/radiobox.cpp b/ftxui/src/ftxui/component/radiobox.cpp index 88b1531..226c98e 100644 --- a/ftxui/src/ftxui/component/radiobox.cpp +++ b/ftxui/src/ftxui/component/radiobox.cpp @@ -9,8 +9,12 @@ Element RadioBox::Render() { for (size_t i = 0; i < entries.size(); ++i) { auto style = (focused == int(i) && is_focused) ? focused_style : unfocused_style; + auto focus_management = + (focused != int(i)) ? nothing : is_focused ? focus : select; + const std::wstring& symbol = selected == int(i) ? checked : unchecked; - elements.push_back(hbox(text(symbol), text(entries[i]) | style)); + elements.push_back(hbox(text(symbol), text(entries[i]) | style) | + focus_management); } return vbox(std::move(elements)); } @@ -30,7 +34,7 @@ bool RadioBox::OnEvent(Event event) { return true; } - if (event == Event::Character(' ')) { + if (event == Event::Character(' ') || event==Event::Return) { selected = focused; on_change(); } diff --git a/ftxui/src/ftxui/component/toggle.cpp b/ftxui/src/ftxui/component/toggle.cpp index 1784ac8..88f5df3 100644 --- a/ftxui/src/ftxui/component/toggle.cpp +++ b/ftxui/src/ftxui/component/toggle.cpp @@ -3,7 +3,7 @@ namespace ftxui { Element Toggle::Render() { - auto highlight = Focused() ? inverted : bold; + bool is_focused = Focused(); Elements children; for(size_t i = 0; irequirement(); + requirement_.min.x += 2; + requirement_.min.y += 2; + if (children.size() == 2) { + requirement_.min.x = + std::max(requirement_.min.x, children[1]->requirement().min.x + 2); + } + requirement_.selected_box.x_min++; + requirement_.selected_box.x_max++; + requirement_.selected_box.y_min++; + requirement_.selected_box.y_max++; + } + + void SetBox(Box box) override { + Node::SetBox(box); + if (children.size() == 2) { + Box title_box; + title_box.x_min = box.x_min + 1; + title_box.x_max = box.x_max - 1; + title_box.y_min = box.y_min; + title_box.y_max = box.y_min; + children[1]->SetBox(title_box); + } + box.x_min++; + box.x_max--; + box.y_min++; + box.y_max--; + children[0]->SetBox(box); + } + + void Render(Screen& screen) override { + // Draw content. + children[0]->Render(screen); + + // Draw the border. + if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max) + return; + + screen.at(box_.x_min, box_.y_min) = charset[0]; + screen.at(box_.x_max, box_.y_min) = charset[1]; + screen.at(box_.x_min, box_.y_max) = charset[2]; + screen.at(box_.x_max, box_.y_max) = charset[3]; + for(float x = box_.x_min + 1; xRender(screen); + } +}; + +std::unique_ptr border(Element child) { + return std::make_unique(unpack(std::move(child))); +} + +std::unique_ptr window(Element title, Element content) { + return std::make_unique(unpack(std::move(content), std::move(title))); +} + +Decorator boxed() { + return [](Element child) { + return border(std::move(child)); + }; +} + +}; // namespace ftxui diff --git a/ftxui/src/ftxui/dom/color.cpp b/ftxui/src/ftxui/dom/color.cpp index e2e7d49..8d5bedf 100644 --- a/ftxui/src/ftxui/dom/color.cpp +++ b/ftxui/src/ftxui/dom/color.cpp @@ -9,8 +9,8 @@ class BgColor : public NodeDecorator { : NodeDecorator(std::move(children)), color_(color) {} void Render(Screen& screen) override { - for (int y = box_.top; y <= box_.bottom; ++y) { - for (int x = box_.left; x <= box_.right; ++x) { + for (int y = box_.y_min; y <= box_.y_max; ++y) { + for (int x = box_.x_min; x <= box_.x_max; ++x) { screen.PixelAt(x, y).background_color = color_; } } @@ -27,8 +27,8 @@ class FgColor : public NodeDecorator { ~FgColor() override {} void Render(Screen& screen) override { - for (int y = box_.top; y <= box_.bottom; ++y) { - for (int x = box_.left; x <= box_.right; ++x) { + for (int y = box_.y_min; y <= box_.y_max; ++y) { + for (int x = box_.x_min; x <= box_.x_max; ++x) { screen.PixelAt(x, y).foreground_color = color_; } } diff --git a/ftxui/src/ftxui/dom/dbox.cpp b/ftxui/src/ftxui/dom/dbox.cpp index f0b8afb..c72db4b 100644 --- a/ftxui/src/ftxui/dom/dbox.cpp +++ b/ftxui/src/ftxui/dom/dbox.cpp @@ -17,6 +17,11 @@ class DBox : public Node { 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); + + if (requirement_.selection < child->requirement().selection) { + requirement_.selection = child->requirement().selection; + requirement_.selected_box = child->requirement().selected_box; + } } } diff --git a/ftxui/src/ftxui/dom/dim.cpp b/ftxui/src/ftxui/dom/dim.cpp index a4fefdf..f5b622f 100644 --- a/ftxui/src/ftxui/dom/dim.cpp +++ b/ftxui/src/ftxui/dom/dim.cpp @@ -12,8 +12,8 @@ class Dim : public NodeDecorator { void Render(Screen& screen) override { Node::Render(screen); - for (int y = box_.top; y <= box_.bottom; ++y) { - for (int x = box_.left; x <= box_.right; ++x) { + for (int y = box_.y_min; y <= box_.y_max; ++y) { + for (int x = box_.x_min; x <= box_.x_max; ++x) { screen.PixelAt(x,y).dim = true; } } diff --git a/ftxui/src/ftxui/dom/frame.cpp b/ftxui/src/ftxui/dom/frame.cpp index c6282fb..92f64cd 100644 --- a/ftxui/src/ftxui/dom/frame.cpp +++ b/ftxui/src/ftxui/dom/frame.cpp @@ -1,69 +1,116 @@ -#include "ftxui/dom/node.hpp" #include "ftxui/dom/elements.hpp" +#include "ftxui/dom/node.hpp" +#include "ftxui/util/autoreset.hpp" namespace ftxui { using namespace ftxui; -static wchar_t charset[] = L"┌┐└┘─│┬┴┤├"; +// ----------------------------------------------------------------------------- -class Frame : public Node { +class Select : public Node { public: - Frame(Elements children) : Node(std::move(children)) {} - ~Frame() override {} + Select(std::vector> children) + : Node(std::move(children)) {} + + void ComputeRequirement() override { + Node::ComputeRequirement(); + requirement_ = children[0]->requirement(); + auto& selected_box = requirement_.selected_box; + selected_box.x_min = 0; + selected_box.y_min = 0; + selected_box.x_max = requirement_.min.x; + selected_box.y_max = requirement_.min.y; + requirement_.selection = Requirement::SELECTED; + }; + + void SetBox(Box box) override { + box_ = box; + children[0]->SetBox(box); + } +}; + +std::unique_ptr select(Element child) { + return std::make_unique