Remove Ref<XxxOption> and add new interfaces. (#686)

1. Stop taking Ref<XxxOption> in Component constructors. Instead, use
   the XxxOption directly. Passing by copy avoid problems developers had
   where one was shared in between multiple component, causing issues.

2. Add variants of most component constructors taking a struct only.

This replaces:
https://github.com/ArthurSonzogni/FTXUI/pull/670

This fixes:
https://github.com/ArthurSonzogni/FTXUI/issues/426
This commit is contained in:
Arthur Sonzogni
2023-06-25 17:22:05 +02:00
committed by GitHub
parent e73e7f0d68
commit 455998d759
26 changed files with 918 additions and 693 deletions

View File

@@ -30,7 +30,148 @@ Element DefaultTransform(EntryState params) { // NOLINT
return element;
}
class ButtonBase : public ComponentBase, public ButtonOption {
public:
explicit ButtonBase(ButtonOption option) : ButtonOption(std::move(option)) {}
// Component implementation:
Element Render() override {
const bool active = Active();
const bool focused = Focused();
const bool focused_or_hover = focused || mouse_hover_;
float target = focused_or_hover ? 1.f : 0.f; // NOLINT
if (target != animator_background_.to()) {
SetAnimationTarget(target);
}
auto focus_management = focused ? focus : active ? select : nothing;
const EntryState state = {
*label,
false,
active,
focused_or_hover,
};
auto element = (transform ? transform : DefaultTransform) //
(state);
return element | AnimatedColorStyle() | focus_management | reflect(box_);
}
Decorator AnimatedColorStyle() {
Decorator style = nothing;
if (animated_colors.background.enabled) {
style = style |
bgcolor(Color::Interpolate(animation_foreground_, //
animated_colors.background.inactive,
animated_colors.background.active));
}
if (animated_colors.foreground.enabled) {
style =
style | color(Color::Interpolate(animation_foreground_, //
animated_colors.foreground.inactive,
animated_colors.foreground.active));
}
return style;
}
void SetAnimationTarget(float target) {
if (animated_colors.foreground.enabled) {
animator_foreground_ = animation::Animator(
&animation_foreground_, target, animated_colors.foreground.duration,
animated_colors.foreground.function);
}
if (animated_colors.background.enabled) {
animator_background_ = animation::Animator(
&animation_background_, target, animated_colors.background.duration,
animated_colors.background.function);
}
}
void OnAnimation(animation::Params& p) override {
animator_background_.OnAnimation(p);
animator_foreground_.OnAnimation(p);
}
void OnClick() {
on_click();
animation_background_ = 0.5F; // NOLINT
animation_foreground_ = 0.5F; // NOLINT
SetAnimationTarget(1.F); // NOLINT
}
bool OnEvent(Event event) override {
if (event.is_mouse()) {
return OnMouseEvent(event);
}
if (event == Event::Return) {
OnClick();
return true;
}
return false;
}
bool OnMouseEvent(Event event) {
mouse_hover_ =
box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
if (!mouse_hover_) {
return false;
}
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) {
TakeFocus();
OnClick();
return true;
}
return false;
}
bool Focusable() const final { return true; }
private:
bool mouse_hover_ = false;
Box box_;
ButtonOption option_;
float animation_background_ = 0;
float animation_foreground_ = 0;
animation::Animator animator_background_ =
animation::Animator(&animation_background_);
animation::Animator animator_foreground_ =
animation::Animator(&animation_foreground_);
};
} // namespace
//
/// @brief Draw a button. Execute a function when clicked.
/// @param option Additional optional parameters.
/// @ingroup component
/// @see ButtonBase
///
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::FitComponent();
/// Component button = Button({
/// .label = "Click to quit",
/// .on_click = screen.ExitLoopClosure(),
/// });
/// screen.Loop(button)
/// ```
///
/// ### Output
///
/// ```bash
/// ┌─────────────┐
/// │Click to quit│
/// └─────────────┘
/// ```
Component Button(ButtonOption option) {
return Make<ButtonBase>(std::move(option));
}
/// @brief Draw a button. Execute a function when clicked.
/// @param label The label of the button.
@@ -55,135 +196,13 @@ Element DefaultTransform(EntryState params) { // NOLINT
/// │Click to quit│
/// └─────────────┘
/// ```
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
// NOLINTNEXTLINE
Component Button(ConstStringRef label,
std::function<void()> on_click,
Ref<ButtonOption> option) {
class Impl : public ComponentBase {
public:
Impl(ConstStringRef label,
std::function<void()> on_click,
Ref<ButtonOption> option)
: label_(std::move(label)),
on_click_(std::move(on_click)),
option_(std::move(option)) {}
// Component implementation:
Element Render() override {
const bool active = Active();
const bool focused = Focused();
const bool focused_or_hover = focused || mouse_hover_;
float target = focused_or_hover ? 1.f : 0.f; // NOLINT
if (target != animator_background_.to()) {
SetAnimationTarget(target);
}
auto focus_management = focused ? focus : active ? select : nothing;
const EntryState state = {
*label_,
false,
active,
focused_or_hover,
};
auto element =
(option_->transform ? option_->transform : DefaultTransform) //
(state);
return element | AnimatedColorStyle() | focus_management | reflect(box_);
}
Decorator AnimatedColorStyle() {
Decorator style = nothing;
if (option_->animated_colors.background.enabled) {
style = style | bgcolor(Color::Interpolate(
animation_foreground_, //
option_->animated_colors.background.inactive,
option_->animated_colors.background.active));
}
if (option_->animated_colors.foreground.enabled) {
style = style | color(Color::Interpolate(
animation_foreground_, //
option_->animated_colors.foreground.inactive,
option_->animated_colors.foreground.active));
}
return style;
}
void SetAnimationTarget(float target) {
if (option_->animated_colors.foreground.enabled) {
animator_foreground_ =
animation::Animator(&animation_foreground_, target,
option_->animated_colors.foreground.duration,
option_->animated_colors.foreground.function);
}
if (option_->animated_colors.background.enabled) {
animator_background_ =
animation::Animator(&animation_background_, target,
option_->animated_colors.background.duration,
option_->animated_colors.background.function);
}
}
void OnAnimation(animation::Params& p) override {
animator_background_.OnAnimation(p);
animator_foreground_.OnAnimation(p);
}
void OnClick() {
on_click_();
animation_background_ = 0.5F; // NOLINT
animation_foreground_ = 0.5F; // NOLINT
SetAnimationTarget(1.F); // NOLINT
}
bool OnEvent(Event event) override {
if (event.is_mouse()) {
return OnMouseEvent(event);
}
if (event == Event::Return) {
OnClick();
return true;
}
return false;
}
bool OnMouseEvent(Event event) {
mouse_hover_ =
box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
if (!mouse_hover_) {
return false;
}
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) {
TakeFocus();
OnClick();
return true;
}
return false;
}
bool Focusable() const final { return true; }
private:
ConstStringRef label_;
std::function<void()> on_click_;
bool mouse_hover_ = false;
Box box_;
Ref<ButtonOption> option_;
float animation_background_ = 0;
float animation_foreground_ = 0;
animation::Animator animator_background_ =
animation::Animator(&animation_background_);
animation::Animator animator_foreground_ =
animation::Animator(&animation_foreground_);
};
return Make<Impl>(std::move(label), std::move(on_click), std::move(option));
ButtonOption option) {
option.label = label;
option.on_click = std::move(on_click);
return Make<ButtonBase>(std::move(option));
}
} // namespace ftxui