mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-22 10:40:00 +08:00
Component decorators (#354)
Add decorator variants for decorator components Add the "pipe" operator for components, similar to what was done for Elements. We are able to put something like: ``` Button(...) | Maybe(&show_button) ``` Add decorators for: - `Maybe` - `CatchEvent` - `Renderer` Signed-off-by: Kefu Chai <tchaikov@gmail.com> Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
This commit is contained in:
parent
3e28fd6520
commit
95c766e9e4
@ -23,6 +23,7 @@ Element gaugeDirection(float ratio, GaugeDirection);
|
||||
with others nearby.
|
||||
- Fix the `Table` rendering function, to allow automerging characters.
|
||||
- Bugfix: The `vscroll_indicator` now computes its offset and size correctly.
|
||||
- Add the `operator|=(Element, Decorator)`
|
||||
|
||||
### Component
|
||||
- Support SIGTSTP. (ctrl+z).
|
||||
@ -33,6 +34,14 @@ Element gaugeDirection(float ratio, GaugeDirection);
|
||||
- **bugfix** Automatically convert '\r' keys into '\n' for Linux programs that
|
||||
do not send the correct code for the return key, like the 'bind'.
|
||||
https://github.com/ArthurSonzogni/FTXUI/issues/337
|
||||
- Add decorator for components:
|
||||
- `operator|(Component, ComponentDecorator)`
|
||||
- `operator|=(Component, ComponentDecorator)`
|
||||
- `operator|(Component, ElementDecorator)`
|
||||
- `operator|=(Component, ElementDecorator)`
|
||||
- Add the `Maybe` decorator.
|
||||
- Add the `CatchEvent` decorator.
|
||||
- Add the `Renderer` decorator.
|
||||
|
||||
2.0.0
|
||||
-----
|
||||
|
@ -112,6 +112,7 @@ add_library(component
|
||||
src/ftxui/component/terminal_input_parser.cpp
|
||||
src/ftxui/component/terminal_input_parser.hpp
|
||||
src/ftxui/component/toggle.cpp
|
||||
src/ftxui/component/util.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(dom
|
||||
|
@ -1,17 +1,15 @@
|
||||
#include <memory> // for shared_ptr, __shared_ptr_access
|
||||
#include <string> // for string, basic_string, allocator
|
||||
#include <memory> // for allocator, shared_ptr
|
||||
#include <string> // for string, basic_string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for ftxui
|
||||
#include "ftxui/component/component.hpp" // for Checkbox, Maybe, Radiobox, Renderer, Vertical
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase
|
||||
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
|
||||
#include "ftxui/dom/elements.hpp" // for Element, operator|, border
|
||||
#include "ftxui/component/component.hpp" // for operator|, Maybe, Checkbox, Radiobox, Renderer, Vertical
|
||||
#include "ftxui/component/component_base.hpp" // for Component
|
||||
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
|
||||
#include "ftxui/dom/elements.hpp" // for border, color, operator|, text, Element
|
||||
#include "ftxui/screen/color.hpp" // for Color, Color::Red
|
||||
|
||||
using namespace ftxui;
|
||||
Component Border(Component child) {
|
||||
return Renderer(child, [child] { return child->Render() | border; });
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
std::vector<std::string> entries = {
|
||||
@ -21,20 +19,19 @@ int main(int argc, const char* argv[]) {
|
||||
};
|
||||
int menu_1_selected = 0;
|
||||
int menu_2_selected = 0;
|
||||
auto menu_1 = Radiobox(&entries, &menu_1_selected);
|
||||
auto menu_2 = Radiobox(&entries, &menu_2_selected);
|
||||
|
||||
menu_1 = Border(menu_1);
|
||||
menu_2 = Border(menu_2);
|
||||
|
||||
bool menu_1_show = false;
|
||||
bool menu_2_show = false;
|
||||
|
||||
auto layout = Container::Vertical({
|
||||
Checkbox("Show menu_1", &menu_1_show),
|
||||
Maybe(menu_1, &menu_1_show),
|
||||
Radiobox(&entries, &menu_1_selected) | border | Maybe(&menu_1_show),
|
||||
Checkbox("Show menu_2", &menu_2_show),
|
||||
Maybe(menu_2, &menu_2_show),
|
||||
Radiobox(&entries, &menu_2_selected) | border | Maybe(&menu_2_show),
|
||||
|
||||
Renderer([] {
|
||||
return text("You found the secret combinaison!") | color(Color::Red);
|
||||
}) | Maybe([&] { return menu_1_selected == 1 && menu_2_selected == 2; }),
|
||||
});
|
||||
|
||||
auto screen = ScreenInteractive::TerminalOutput();
|
||||
|
@ -84,7 +84,7 @@ int main(int argc, const char* argv[]) {
|
||||
return window(text("keys"), vbox(std::move(children)));
|
||||
});
|
||||
|
||||
component = CatchEvent(component, [&](Event event) {
|
||||
component |= CatchEvent([&](Event event) {
|
||||
keys.push_back(event);
|
||||
return true;
|
||||
});
|
||||
|
@ -1,4 +1,3 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <stdio.h> // for getchar
|
||||
#include <ftxui/dom/elements.hpp> // for operator|, size, Element, text, hcenter, Decorator, Fit, WIDTH, hflow, window, EQUAL, GREATER_THAN, HEIGHT, bold, border, dim, LESS_THAN
|
||||
#include <ftxui/screen/screen.hpp> // for Full, Screen
|
||||
|
@ -1,4 +1,3 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <stdio.h> // for getchar
|
||||
#include <ftxui/dom/elements.hpp> // for operator|, Element, size, text, hcenter, Fit, vflow, window, EQUAL, bold, border, dim, HEIGHT, WIDTH
|
||||
#include <ftxui/screen/screen.hpp> // for Full, Screen
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <functional> // for function
|
||||
#include <memory> // for make_shared, shared_ptr
|
||||
#include <string> // for wstring
|
||||
#include <utility> // for forward
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/component/component_base.hpp" // for Component, Components
|
||||
@ -26,6 +27,14 @@ std::shared_ptr<T> Make(Args&&... args) {
|
||||
return std::make_shared<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Pipe operator to decorate components.
|
||||
using ComponentDecorator = std::function<Component(Component)>;
|
||||
using ElementDecorator = std::function<Element(Element)>;
|
||||
Component operator|(Component component, ComponentDecorator decorator);
|
||||
Component operator|(Component component, ElementDecorator decorator);
|
||||
Component& operator|=(Component& component, ComponentDecorator decorator);
|
||||
Component& operator|=(Component& component, ElementDecorator decorator);
|
||||
|
||||
namespace Container {
|
||||
Component Vertical(Components children);
|
||||
Component Vertical(Components children, int* selector);
|
||||
@ -38,38 +47,55 @@ Component Tab(Components children, int* selector);
|
||||
Component Button(ConstStringRef label,
|
||||
std::function<void()> on_click,
|
||||
Ref<ButtonOption> = {});
|
||||
|
||||
Component Checkbox(ConstStringRef label,
|
||||
bool* checked,
|
||||
Ref<CheckboxOption> option = {});
|
||||
|
||||
Component Input(StringRef content,
|
||||
ConstStringRef placeholder,
|
||||
Ref<InputOption> option = {});
|
||||
|
||||
Component Menu(ConstStringListRef entries,
|
||||
int* selected_,
|
||||
Ref<MenuOption> = {});
|
||||
|
||||
Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> = {});
|
||||
|
||||
Component Dropdown(ConstStringListRef entries, int* selected);
|
||||
|
||||
Component Radiobox(ConstStringListRef entries,
|
||||
int* selected_,
|
||||
Ref<RadioboxOption> option = {});
|
||||
|
||||
Component Toggle(ConstStringListRef entries,
|
||||
int* selected,
|
||||
Ref<ToggleOption> option = {});
|
||||
|
||||
template <class T> // T = {int, float, long}
|
||||
Component Slider(ConstStringRef label, T* value, T min, T max, T increment);
|
||||
|
||||
Component ResizableSplitLeft(Component main, Component back, int* main_size);
|
||||
Component ResizableSplitRight(Component main, Component back, int* main_size);
|
||||
Component ResizableSplitTop(Component main, Component back, int* main_size);
|
||||
Component ResizableSplitBottom(Component main, Component back, int* main_size);
|
||||
|
||||
Component Renderer(Component child, std::function<Element()>);
|
||||
Component Renderer(std::function<Element()>);
|
||||
Component Renderer(std::function<Element(bool /* focused */)>);
|
||||
ComponentDecorator Renderer(ElementDecorator);
|
||||
|
||||
Component CatchEvent(Component child, std::function<bool(Event)>);
|
||||
ComponentDecorator CatchEvent(std::function<bool(Event)> on_event);
|
||||
|
||||
Component Maybe(Component, const bool* show);
|
||||
Component Maybe(Component, std::function<bool()>);
|
||||
ComponentDecorator Maybe(const bool* show);
|
||||
ComponentDecorator Maybe(std::function<bool()>);
|
||||
|
||||
Component Collapsible(ConstStringRef label,
|
||||
Component child,
|
||||
Ref<bool> show = false);
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
// Include component using the old deprecated wstring.
|
||||
|
@ -5,9 +5,10 @@
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Component Input(WideStringRef content,
|
||||
ConstStringRef placeholder,
|
||||
Ref<InputOption> option = {});
|
||||
[[deprecated("use Input with normal std::string instead.")]] Component Input(
|
||||
WideStringRef content,
|
||||
ConstStringRef placeholder,
|
||||
Ref<InputOption> option = {});
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* FTXUI_COMPONENT_DEPRECATED_HPP */
|
||||
|
@ -28,6 +28,7 @@ enum class GaugeDirection { Left, Up, Right, Down };
|
||||
// -> text("ftxui") | bold | underlined
|
||||
// -> underlined(bold(text("FTXUI")))
|
||||
Element operator|(Element, Decorator);
|
||||
Element& operator|=(Element&, Decorator);
|
||||
Elements operator|(Elements, Decorator);
|
||||
Decorator operator|(Decorator, Decorator);
|
||||
|
||||
|
@ -55,6 +55,33 @@ Component CatchEvent(Component child,
|
||||
return out;
|
||||
}
|
||||
|
||||
/// @brief Decorate a component, using |on_event| to catch events. This function
|
||||
/// must returns true when the event has been handled, false otherwise.
|
||||
/// @param on_event The function drawing the interface.
|
||||
/// @ingroup component
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto screen = ScreenInteractive::TerminalOutput();
|
||||
/// auto renderer = Renderer([] { return text("Hello world"); });
|
||||
/// renderer |= CatchEvent([&](Event event) {
|
||||
/// if (event == Event::Character('q')) {
|
||||
/// screen.ExitLoopClosure()();
|
||||
/// return true;
|
||||
/// }
|
||||
/// return false;
|
||||
/// });
|
||||
/// screen.Loop(renderer);
|
||||
/// ```
|
||||
ComponentDecorator CatchEvent(std::function<bool(Event)> on_event) {
|
||||
return [on_event = std::move(on_event)](Component child) {
|
||||
return CatchEvent(child, [on_event = std::move(on_event)](Event event) {
|
||||
return on_event(event);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
// Copyright 2021 Arthur Sonzogni. All rights reserved.
|
||||
|
@ -1,38 +1,86 @@
|
||||
#include <functional> // for function
|
||||
#include <memory> // for make_unique, __shared_ptr_access, __shared_ptr_access<>::element_type, shared_ptr
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/component.hpp" // for Make, Maybe
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
|
||||
#include "ftxui/component/component.hpp" // for ComponentDecorator, Maybe, Make
|
||||
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
|
||||
#include "ftxui/component/event.hpp" // for Event
|
||||
#include "ftxui/dom/elements.hpp" // for Element
|
||||
#include "ftxui/dom/node.hpp" // for Node
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Component Maybe(Component child, const bool* show) {
|
||||
Component Maybe(Component child, std::function<bool()> show) {
|
||||
class Impl : public ComponentBase {
|
||||
public:
|
||||
Impl(const bool* show) : show_(show) {}
|
||||
Impl(std::function<bool()> show) : show_(std::move(show)) {}
|
||||
|
||||
private:
|
||||
Element Render() override {
|
||||
return *show_ ? ComponentBase::Render() : std::make_unique<Node>();
|
||||
return show_() ? ComponentBase::Render() : std::make_unique<Node>();
|
||||
}
|
||||
bool Focusable() const override {
|
||||
return *show_ && ComponentBase::Focusable();
|
||||
return show_() && ComponentBase::Focusable();
|
||||
}
|
||||
bool OnEvent(Event event) override {
|
||||
return *show_ && ComponentBase::OnEvent(event);
|
||||
return show_() && ComponentBase::OnEvent(event);
|
||||
}
|
||||
|
||||
const bool* show_;
|
||||
std::function<bool()> show_;
|
||||
};
|
||||
|
||||
auto maybe = Make<Impl>(show);
|
||||
auto maybe = Make<Impl>(std::move(show));
|
||||
maybe->Add(std::move(child));
|
||||
return maybe;
|
||||
}
|
||||
|
||||
/// @brief Decorate a component. It is shown only when the |show| function
|
||||
/// returns true.
|
||||
/// @params show a function returning whether the decoratorated component should
|
||||
/// be shown.
|
||||
/// @ingroup component
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto component = Renderer([]{ return "Hello World!"; });
|
||||
/// auto maybe_component = component | Maybe([&]{ return counter == 42; });
|
||||
/// ```
|
||||
ComponentDecorator Maybe(std::function<bool()> show) {
|
||||
return [show = std::move(show)](Component child) mutable {
|
||||
return Maybe(child, std::move(show));
|
||||
};
|
||||
}
|
||||
|
||||
/// @brief Decorate a component |child|. It is shown only when |show| is true.
|
||||
/// @params child the compoennt to decorate.
|
||||
/// @params show a boolean. |child| is shown when |show| is true.
|
||||
/// @ingroup component
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto component = Renderer([]{ return "Hello World!"; });
|
||||
/// auto maybe_component = Maybe(component, &show);
|
||||
/// ```
|
||||
Component Maybe(Component child, const bool* show) {
|
||||
return Maybe(child, [show] { return *show; });
|
||||
}
|
||||
|
||||
/// @brief Decorate a component. It is shown only when |show| is true.
|
||||
/// @params show a boolean. |child| is shown when |show| is true.
|
||||
/// @ingroup component
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto component = Renderer([]{ return "Hello World!"; });
|
||||
/// auto maybe_component = component | Maybe(&show);
|
||||
/// ```
|
||||
ComponentDecorator Maybe(const bool* show) {
|
||||
return [show](Component child) { return Maybe(child, show); };
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
// Copyright 2021 Arthur Sonzogni. All rights reserved.
|
||||
|
@ -104,6 +104,28 @@ Component Renderer(std::function<Element(bool)> render) {
|
||||
return Make<Impl>(std::move(render));
|
||||
}
|
||||
|
||||
/// @brief Decorate a component, by decorating what it renders.
|
||||
/// @param decorator the function modifying the element it renders.
|
||||
/// @ingroup component
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto screen = ScreenInteractive::TerminalOutput();
|
||||
/// auto renderer =
|
||||
// Renderer([] { return text("Hello");)
|
||||
/// | Renderer(bold)
|
||||
/// | Renderer(inverted);
|
||||
/// screen.Loop(renderer);
|
||||
/// ```
|
||||
ComponentDecorator Renderer(ElementDecorator decorator) {
|
||||
return [decorator](Component component) {
|
||||
return Renderer(component, [component, decorator] {
|
||||
return component->Render() | decorator;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
// Copyright 2021 Arthur Sonzogni. All rights reserved.
|
||||
|
32
src/ftxui/component/util.cpp
Normal file
32
src/ftxui/component/util.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include <functional> // for function
|
||||
#include <memory> // for __shared_ptr_access
|
||||
|
||||
#include "ftxui/component/component.hpp" // for ElementDecorator, Renderer, ComponentDecorator, operator|, operator|=
|
||||
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
|
||||
#include "ftxui/dom/elements.hpp" // for Element
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Component operator|(Component component, ComponentDecorator decorator) {
|
||||
return decorator(component);
|
||||
}
|
||||
|
||||
Component operator|(Component component, ElementDecorator decorator) {
|
||||
return component | Renderer(decorator);
|
||||
}
|
||||
|
||||
Component& operator|=(Component& component, ComponentDecorator decorator) {
|
||||
component = component | decorator;
|
||||
return component;
|
||||
}
|
||||
|
||||
Component& operator|=(Component& component, ElementDecorator decorator) {
|
||||
component = component | decorator;
|
||||
return component;
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
// Copyright 2022 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
@ -66,6 +66,22 @@ Element operator|(Element element, Decorator decorator) {
|
||||
return decorator(std::move(element));
|
||||
}
|
||||
|
||||
/// @brief Apply a decorator to an element.
|
||||
/// @return the decorated element.
|
||||
/// @ingroup dom
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// Both of these are equivalent:
|
||||
/// ```cpp
|
||||
/// auto element = text("Hello");
|
||||
/// element |= bold;
|
||||
/// ```
|
||||
Element& operator|=(Element& e, Decorator d) {
|
||||
e = e | d;
|
||||
return e;
|
||||
}
|
||||
|
||||
/// The minimal dimension that will fit the given element.
|
||||
/// @see Fixed
|
||||
/// @see Full
|
||||
|
Loading…
Reference in New Issue
Block a user