Animation (#355)

This commit is contained in:
Arthur Sonzogni
2022-03-13 18:51:46 +01:00
committed by GitHub
parent 95c766e9e4
commit 4da63b9260
43 changed files with 2439 additions and 654 deletions

View File

@@ -0,0 +1,119 @@
#ifndef FTXUI_ANIMATION_HPP
#define FTXUI_ANIMATION_HPP
#include <chrono> // for milliseconds, duration, steady_clock, time_point
#include <functional> // for function
#include "ftxui/component/event.hpp"
namespace ftxui {
namespace animation {
// Components who haven't completed their animation can call this function to
// request a new frame to be drawn later.
//
// When there is no new events and no animations to complete, no new frame is
// drawn.
void RequestAnimationFrame();
using Clock = std::chrono::steady_clock;
using TimePoint = std::chrono::time_point<Clock>;
using Duration = std::chrono::duration<double>;
// Parameter of Component::OnAnimation(param).
class Params {
public:
Params(Duration duration) : duration_(duration) {}
/// The duration this animation step represents.
Duration duration() const { return duration_; }
private:
Duration duration_;
};
namespace easing {
using Function = std::function<float(float)>;
// Linear interpolation (no easing)
float Linear(float p);
// Quadratic easing; p^2
float QuadraticIn(float p);
float QuadraticOut(float p);
float QuadraticInOut(float p);
// Cubic easing; p^3
float CubicIn(float p);
float CubicOut(float p);
float CubicInOut(float p);
// Quartic easing; p^4
float QuarticIn(float p);
float QuarticOut(float p);
float QuarticInOut(float p);
// Quintic easing; p^5
float QuinticIn(float p);
float QuinticOut(float p);
float QuinticInOut(float p);
// Sine wave easing; sin(p * PI/2)
float SineIn(float p);
float SineOut(float p);
float SineInOut(float p);
// Circular easing; sqrt(1 - p^2)
float CircularIn(float p);
float CircularOut(float p);
float CircularInOut(float p);
// Exponential easing, base 2
float ExponentialIn(float p);
float ExponentialOut(float p);
float ExponentialInOut(float p);
// Exponentially-damped sine wave easing
float ElasticIn(float p);
float ElasticOut(float p);
float ElasticInOut(float p);
// Overshooting cubic easing;
float BackIn(float p);
float BackOut(float p);
float BackInOut(float p);
// Exponentially-decaying bounce easing
float BounceIn(float p);
float BounceOut(float p);
float BounceInOut(float p);
} // namespace easing
class Animator {
public:
Animator(float* from,
float to = 0.f,
Duration duration = std::chrono::milliseconds(250),
easing::Function easing_function = easing::Linear,
Duration delay = std::chrono::milliseconds(0));
void OnAnimation(Params&);
float to() const { return to_; }
private:
float* value_;
float from_;
float to_;
Duration duration_;
easing::Function easing_function_;
Duration current_;
};
} // namespace animation
} // namespace ftxui
#endif /* end of include guard: FTXUI_ANIMATION_HPP */
// 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.

View File

@@ -8,7 +8,7 @@
#include <vector> // for vector
#include "ftxui/component/component_base.hpp" // for Component, Components
#include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption, InputOption, MenuOption, RadioboxOption, ToggleOption
#include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption (ptr only), InputOption (ptr only), MenuEntryOption (ptr only), MenuOption, RadioboxOption (ptr only)
#include "ftxui/dom/elements.hpp" // for Element
#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef, ConstStringListRef, StringRef
@@ -19,7 +19,6 @@ struct Event;
struct InputOption;
struct MenuOption;
struct RadioboxOption;
struct ToggleOption;
struct MenuEntryOption;
template <class T, class... Args>
@@ -46,11 +45,11 @@ Component Tab(Components children, int* selector);
Component Button(ConstStringRef label,
std::function<void()> on_click,
Ref<ButtonOption> = {});
Ref<ButtonOption> = ButtonOption::Simple());
Component Checkbox(ConstStringRef label,
bool* checked,
Ref<CheckboxOption> option = {});
Ref<CheckboxOption> option = CheckboxOption::Simple());
Component Input(StringRef content,
ConstStringRef placeholder,
@@ -58,8 +57,7 @@ Component Input(StringRef content,
Component Menu(ConstStringListRef entries,
int* selected_,
Ref<MenuOption> = {});
Ref<MenuOption> = MenuOption::Vertical());
Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> = {});
Component Dropdown(ConstStringListRef entries, int* selected);
@@ -67,10 +65,7 @@ Component Dropdown(ConstStringListRef entries, int* selected);
Component Radiobox(ConstStringListRef entries,
int* selected_,
Ref<RadioboxOption> option = {});
Component Toggle(ConstStringListRef entries,
int* selected,
Ref<ToggleOption> option = {});
Component Toggle(ConstStringListRef entries, int* selected);
template <class T> // T = {int, float, long}
Component Slider(ConstStringRef label, T* value, T min, T max, T increment);

View File

@@ -13,6 +13,10 @@ class Delegate;
class Focus;
struct Event;
namespace animation {
class Params;
} // namespace animation
class ComponentBase;
using Component = std::shared_ptr<ComponentBase>;
using Components = std::vector<Component>;
@@ -42,6 +46,9 @@ class ComponentBase {
// Returns whether the event was handled or not.
virtual bool OnEvent(Event);
// Handle an animation step.
virtual void OnAnimation(animation::Params& params);
// Focus management ----------------------------------------------------------
//
// If this component contains children, this indicates which one is active,

View File

@@ -1,58 +1,136 @@
#ifndef FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP
#define FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP
#include <ftxui/dom/elements.hpp>
#include <ftxui/util/ref.hpp>
#include <chrono> // for milliseconds
#include <ftxui/component/animation.hpp> // for Duration, QuadraticInOut, Function
#include <ftxui/dom/elements.hpp> // for Decorator, bold, inverted, operator|, Element, nothing
#include <ftxui/util/ref.hpp> // for Ref
#include <functional> // for function
#include <optional> // for optional
#include <string> // for string, allocator
#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White
namespace ftxui {
/// @brief Option for the Menu component.
/// @brief arguments for |ButtonOption::transform|, |CheckboxOption::transform|,
/// |Radiobox::transform|, |MenuEntryOption::transform|,
/// |MenuOption::transform|.
struct EntryState {
std::string label; /// < The label to display.
bool state; /// < The state of the button/checkbox/radiobox
bool active; /// < Whether the entry is the active one.
bool focused; /// < Whether the entry is one focused by the user.
};
struct UnderlineOption {
bool enabled = false;
Color color_active = Color::White;
Color color_inactive = Color::GrayDark;
animation::easing::Function leader_function =
animation::easing::QuadraticInOut;
animation::easing::Function follower_function =
animation::easing::QuadraticInOut;
animation::Duration leader_duration = std::chrono::milliseconds(250);
animation::Duration leader_delay = std::chrono::milliseconds(0);
animation::Duration follower_duration = std::chrono::milliseconds(250);
animation::Duration follower_delay = std::chrono::milliseconds(0);
void SetAnimation(animation::Duration d, animation::easing::Function f);
void SetAnimationDuration(animation::Duration d);
void SetAnimationFunction(animation::easing::Function f);
void SetAnimationFunction(animation::easing::Function f_leader,
animation::easing::Function f_follower);
};
/// @brief Option about a potentially animated color.
/// @ingroup component
struct MenuOption {
Decorator style_normal = nothing; ///< style.
Decorator style_focused = inverted; ///< Style when focused.
Decorator style_selected = bold; ///< Style when selected.
Decorator style_selected_focused =
Decorator(inverted) | bold; ///< Style when selected and focused.
struct AnimatedColorOption {
void Set(
Color inactive,
Color active,
animation::Duration duration = std::chrono::milliseconds(250),
animation::easing::Function function = animation::easing::QuadraticInOut);
/// Called when the selected entry changes.
std::function<void()> on_change = [] {};
/// Called when the user presses enter.
std::function<void()> on_enter = [] {};
bool enabled = false;
Color inactive;
Color active;
animation::Duration duration = std::chrono::milliseconds(250);
animation::easing::Function function = animation::easing::QuadraticInOut;
};
Ref<int> focused_entry = 0;
struct AnimatedColorsOption {
AnimatedColorOption background;
AnimatedColorOption foreground;
};
/// @brief Option for the MenuEntry component.
/// @ingroup component
struct MenuEntryOption {
Decorator style_normal = nothing; ///< style.
Decorator style_focused = inverted; ///< Style when focused.
Decorator style_selected = bold; ///< Style when selected.
Decorator style_selected_focused =
Decorator(inverted) | bold; ///< Style when selected and focused.
std::function<Element(EntryState state)> transform;
AnimatedColorsOption animated_colors;
};
/// @brief Option for the Button component.
/// @brief Option for the Menu component.
/// @ingroup component
struct MenuOption {
// Standard constructors:
static MenuOption Horizontal();
static MenuOption HorizontalAnimated();
static MenuOption Vertical();
static MenuOption VerticalAnimated();
static MenuOption Toggle();
// Style:
UnderlineOption underline;
MenuEntryOption entries;
enum Direction { Up, Down, Left, Right };
Direction direction = Down;
std::function<Element()> elements_prefix;
std::function<Element()> elements_infix;
std::function<Element()> elements_postfix;
// Observers:
std::function<void()> on_change; ///> Called when the seelcted entry changes.
std::function<void()> on_enter; ///> Called when the user presses enter.
Ref<int> focused_entry = 0;
};
/// @brief Option for the AnimatedButton component.
/// @ingroup component
struct ButtonOption {
/// Whether to show a border around the button.
bool border = true;
// Standard constructors:
static ButtonOption Ascii();
static ButtonOption Simple();
static ButtonOption Border();
static ButtonOption Animated();
static ButtonOption Animated(Color color);
static ButtonOption Animated(Color background, Color foreground);
static ButtonOption Animated(Color background,
Color foreground,
Color background_active,
Color foreground_active);
// Style:
std::function<Element(EntryState)> transform;
AnimatedColorsOption animated_colors;
};
/// @brief Option for the Checkbox component.
/// @ingroup component
struct CheckboxOption {
std::string style_checked = ""; ///< Prefix for a "checked" state.
std::string style_unchecked = ""; ///< Prefix for a "unchecked" state.
Decorator style_normal = nothing; ///< style.
Decorator style_focused = inverted; ///< Style when focused.
Decorator style_selected = bold; ///< Style when selected.
Decorator style_selected_focused =
Decorator(inverted) | bold; ///< Style when selected and focused.
// Standard constructors:
static CheckboxOption Simple();
// Style:
std::function<Element(EntryState)> transform;
// Observer:
/// Called when the user change the state.
std::function<void()> on_change = []() {};
std::function<void()> on_change = [] {};
};
/// @brief Option for the Input component.
@@ -74,34 +152,15 @@ struct InputOption {
/// @brief Option for the Radiobox component.
/// @ingroup component
struct RadioboxOption {
std::string style_checked = ""; ///< Prefix for a "checked" state.
std::string style_unchecked = ""; ///< Prefix for a "unchecked" state.
Decorator style_normal = nothing; ///< style.
Decorator style_focused = inverted; ///< Style when focused.
Decorator style_selected = bold; ///< Style when selected.
Decorator style_selected_focused =
Decorator(inverted) | bold; ///< Style when selected and focused.
// Standard constructors:
static RadioboxOption Simple();
/// Called when the selected entry changes.
std::function<void()> on_change = []() {};
Ref<int> focused_entry = 0;
};
/// @brief Option for the Toggle component.
/// @ingroup component
struct ToggleOption {
Decorator style_normal = nothing; ///< style.
Decorator style_focused = inverted; ///< Style when focused.
Decorator style_selected = bold; ///< Style when selected.
Decorator style_selected_focused =
Decorator(inverted) | bold; ///< Style when selected and focused.
// Style:
std::function<Element(EntryState)> transform;
// Observers:
/// Called when the selected entry changes.
std::function<void()> on_change = [] {};
/// Called when the user presses enter.
std::function<void()> on_enter = [] {};
Ref<int> focused_entry = 0;
};

View File

@@ -9,6 +9,7 @@
#include <thread> // for thread
#include <variant> // for variant
#include "ftxui/component/animation.hpp" // for TimePoint
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/task.hpp" // for Closure, Task
@@ -23,16 +24,21 @@ class ScreenInteractivePrivate;
class ScreenInteractive : public Screen {
public:
// Constructors:
static ScreenInteractive FixedSize(int dimx, int dimy);
static ScreenInteractive Fullscreen();
static ScreenInteractive FitComponent();
static ScreenInteractive TerminalOutput();
// Return the currently active screen, nullptr if none.
static ScreenInteractive* Active();
void Loop(Component);
Closure ExitLoopClosure();
void Post(Task task);
void PostEvent(Event event);
void RequestAnimationFrame();
CapturedMouse CaptureMouse();
@@ -72,6 +78,9 @@ class ScreenInteractive : public Screen {
std::atomic<bool> quit_ = false;
std::thread event_listener_;
std::thread animation_listener_;
bool animation_requested_ = true;
animation::TimePoint previous_animation_time;
int cursor_x_ = 1;
int cursor_y_ = 1;

View File

@@ -1,12 +1,18 @@
#ifndef FTXUI_COMPONENT_ANIMATION_HPP
#define FTXUI_COMPONENT_ANIMATION_HPP
#include <functional>
#include <variant>
#include "ftxui/component/event.hpp"
namespace ftxui {
class AnimationTask {};
using Closure = std::function<void()>;
using Task = std::variant<Event, Closure>;
using Task = std::variant<Event, Closure, AnimationTask>;
} // namespace ftxui
#endif // FTXUI_COMPONENT_ANIMATION_HPP
// 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.

View File

@@ -43,6 +43,14 @@ Element separatorEmpty();
Element separatorStyled(BorderStyle);
Element separator(Pixel);
Element separatorCharacter(std::string);
Element separatorHSelector(float left,
float right,
Color background,
Color foreground);
Element separatorVSelector(float up,
float down,
Color background,
Color foreground);
Element gauge(float ratio);
Element gaugeLeft(float ratio);
Element gaugeRight(float ratio);

View File

@@ -27,6 +27,7 @@ class Color {
Color(uint8_t red, uint8_t green, uint8_t blue);
static Color RGB(uint8_t red, uint8_t green, uint8_t blue);
static Color HSV(uint8_t hue, uint8_t saturation, uint8_t value);
static Color Interpolate(float t, const Color& a, const Color& b);
//---------------------------
// List of colors: