Initial commit

This commit is contained in:
2025-03-21 18:47:49 +01:00
commit a8e1193e12
46 changed files with 2787 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
// 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.
#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<float>;
// 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 */

View File

@@ -0,0 +1,17 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_CAPTURED_MOUSE_HPP
#define FTXUI_CAPTURED_MOUSE_HPP
#include <memory>
namespace ftxui {
class CapturedMouseInterface {
public:
virtual ~CapturedMouseInterface() = default;
};
using CapturedMouse = std::unique_ptr<CapturedMouseInterface>;
} // namespace ftxui
#endif /* end of include guard: FTXUI_CAPTURED_MOUSE_HPP */

View File

@@ -0,0 +1,142 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_HPP
#define FTXUI_COMPONENT_HPP
#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
#include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption, MenuOption
#include "ftxui/dom/elements.hpp" // for Element
#include "ftxui/util/ref.hpp" // for ConstRef, Ref, ConstStringRef, ConstStringListRef, StringRef
namespace ftxui {
struct ButtonOption;
struct CheckboxOption;
struct Event;
struct InputOption;
struct MenuOption;
struct RadioboxOption;
struct MenuEntryOption;
template <class T, class... Args>
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);
Component Horizontal(Components children);
Component Horizontal(Components children, int* selector);
Component Tab(Components children, int* selector);
Component Stacked(Components children);
} // namespace Container
Component Button(ButtonOption options);
Component Button(ConstStringRef label,
std::function<void()> on_click,
ButtonOption options = ButtonOption::Simple());
Component Checkbox(CheckboxOption options);
Component Checkbox(ConstStringRef label,
bool* checked,
CheckboxOption options = CheckboxOption::Simple());
Component Input(InputOption options = {});
Component Input(StringRef content, InputOption options = {});
Component Input(StringRef content,
StringRef placeholder,
InputOption options = {});
Component Menu(MenuOption options);
Component Menu(ConstStringListRef entries,
int* selected_,
MenuOption options = MenuOption::Vertical());
Component MenuEntry(MenuEntryOption options);
Component MenuEntry(ConstStringRef label, MenuEntryOption options = {});
Component Radiobox(RadioboxOption options);
Component Radiobox(ConstStringListRef entries,
int* selected_,
RadioboxOption options = {});
Component Dropdown(ConstStringListRef entries, int* selected);
Component Toggle(ConstStringListRef entries, int* selected);
// General slider constructor:
template <typename T>
Component Slider(SliderOption<T> options);
// Shorthand without the `SliderOption` constructor:
Component Slider(ConstStringRef label,
Ref<int> value,
ConstRef<int> min = 0,
ConstRef<int> max = 100,
ConstRef<int> increment = 5);
Component Slider(ConstStringRef label,
Ref<float> value,
ConstRef<float> min = 0.f,
ConstRef<float> max = 100.f,
ConstRef<float> increment = 5.f);
Component Slider(ConstStringRef label,
Ref<long> value,
ConstRef<long> min = 0l,
ConstRef<long> max = 100l,
ConstRef<long> increment = 5l);
Component ResizableSplit(ResizableSplitOption options);
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 Modal(Component main, Component modal, const bool* show_modal);
ComponentDecorator Modal(Component modal, const bool* show_modal);
Component Collapsible(ConstStringRef label,
Component child,
Ref<bool> show = false);
Component Hoverable(Component component, bool* hover);
Component Hoverable(Component component,
std::function<void()> on_enter,
std::function<void()> on_leave);
Component Hoverable(Component component, //
std::function<void(bool)> on_change);
ComponentDecorator Hoverable(bool* hover);
ComponentDecorator Hoverable(std::function<void()> on_enter,
std::function<void()> on_leave);
ComponentDecorator Hoverable(std::function<void(bool)> on_change);
Component Window(WindowOptions option);
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_HPP */

View File

@@ -0,0 +1,98 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_BASE_HPP
#define FTXUI_COMPONENT_BASE_HPP
#include <memory> // for unique_ptr
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CaptureMouse
#include "ftxui/dom/elements.hpp" // for Element
namespace ftxui {
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>;
/// @brief It implement rendering itself as ftxui::Element. It implement
/// keyboard navigation by responding to ftxui::Event.
/// @ingroup component
class ComponentBase {
public:
// virtual Destructor.
virtual ~ComponentBase();
ComponentBase() = default;
// A component is not copiable.
ComponentBase(const ComponentBase&) = delete;
void operator=(const ComponentBase&) = delete;
// Component hierarchy:
ComponentBase* Parent() const;
Component& ChildAt(size_t i);
size_t ChildCount() const;
void Add(Component children);
void Detach();
void DetachAllChildren();
// Renders the component.
virtual Element Render();
// Handles an event.
// By default, reduce on children with a lazy OR.
//
// 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,
// nullptr if none is active.
//
// We say an element has the focus if the chain of ActiveChild() from the
// root component contains this object.
virtual Component ActiveChild();
// Return true when the component contains focusable elements.
// The non focusable Component will be skipped when navigating using the
// keyboard.
virtual bool Focusable() const;
// Whether this is the active child of its parent.
bool Active() const;
// Whether all the ancestors are active.
bool Focused() const;
// Make the |child| to be the "active" one.
virtual void SetActiveChild(ComponentBase* child);
void SetActiveChild(Component child);
// Configure all the ancestors to give focus to this component.
void TakeFocus();
protected:
CapturedMouse CaptureMouse(const Event& event);
Components children_;
private:
ComponentBase* parent_ = nullptr;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_BASE_HPP */

View File

@@ -0,0 +1,267 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP
#define FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP
#include <chrono> // for milliseconds
#include <ftxui/component/animation.hpp> // for Duration, QuadraticInOut, Function
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Left, Direction::Right, Direction::Down
#include <ftxui/dom/elements.hpp> // for Element, separator
#include <ftxui/util/ref.hpp> // for Ref, ConstRef, StringRef
#include <functional> // for function
#include <optional> // for optional
#include <string> // for string
#include "ftxui/component/component_base.hpp" // for Component
#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White
namespace ftxui {
/// @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 AnimatedColorOption {
void Set(
Color inactive,
Color active,
animation::Duration duration = std::chrono::milliseconds(250),
animation::easing::Function function = animation::easing::QuadraticInOut);
bool enabled = false;
Color inactive;
Color active;
animation::Duration duration = std::chrono::milliseconds(250);
animation::easing::Function function = animation::easing::QuadraticInOut;
};
struct AnimatedColorsOption {
AnimatedColorOption background;
AnimatedColorOption foreground;
};
/// @brief Option for the MenuEntry component.
/// @ingroup component
struct MenuEntryOption {
ConstStringRef label = "MenuEntry";
std::function<Element(const EntryState& state)> transform;
AnimatedColorsOption animated_colors;
};
/// @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();
ConstStringListRef entries; ///> The list of entries.
Ref<int> selected = 0; ///> The index of the selected entry.
// Style:
UnderlineOption underline;
MenuEntryOption entries_option;
Direction 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 selected 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 {
// 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);
ConstStringRef label = "Button";
std::function<void()> on_click = [] {};
// Style:
std::function<Element(const EntryState&)> transform;
AnimatedColorsOption animated_colors;
};
/// @brief Option for the Checkbox component.
/// @ingroup component
struct CheckboxOption {
// Standard constructors:
static CheckboxOption Simple();
ConstStringRef label = "Checkbox";
Ref<bool> checked = false;
// Style:
std::function<Element(const EntryState&)> transform;
// Observer:
/// Called when the user change the state.
std::function<void()> on_change = [] {};
};
/// @brief Used to define style for the Input component.
struct InputState {
Element element;
bool hovered; /// < Whether the input is hovered by the mouse.
bool focused; /// < Whether the input is focused by the user.
bool is_placeholder; /// < Whether the input is empty and displaying the
/// < placeholder.
};
/// @brief Option for the Input component.
/// @ingroup component
struct InputOption {
// A set of predefined styles:
/// @brief Create the default input style:
static InputOption Default();
/// @brief A white on black style with high margins:
static InputOption Spacious();
/// The content of the input.
StringRef content = "";
/// The content of the input when it's empty.
StringRef placeholder = "";
// Style:
std::function<Element(InputState)> transform;
Ref<bool> password = false; /// < Obscure the input content using '*'.
Ref<bool> multiline = true; /// < Whether the input can be multiline.
/// Called when the content changes.
std::function<void()> on_change = [] {};
/// Called when the user presses enter.
std::function<void()> on_enter = [] {};
// The char position of the cursor:
Ref<int> cursor_position = 0;
};
/// @brief Option for the Radiobox component.
/// @ingroup component
struct RadioboxOption {
// Standard constructors:
static RadioboxOption Simple();
// Content:
ConstStringListRef entries;
Ref<int> selected = 0;
// Style:
std::function<Element(const EntryState&)> transform;
// Observers:
/// Called when the selected entry changes.
std::function<void()> on_change = [] {};
Ref<int> focused_entry = 0;
};
struct ResizableSplitOption {
Component main;
Component back;
Ref<Direction> direction = Direction::Left;
Ref<int> main_size =
(direction() == Direction::Left || direction() == Direction::Right) ? 20
: 10;
std::function<Element()> separator_func = [] { return ::ftxui::separator(); };
};
// @brief Option for the `Slider` component.
// @ingroup component
template <typename T>
struct SliderOption {
Ref<T> value;
ConstRef<T> min = T(0);
ConstRef<T> max = T(100);
ConstRef<T> increment = (max() - min()) / 20;
Direction direction = Direction::Right;
Color color_active = Color::White;
Color color_inactive = Color::GrayDark;
};
// Parameter pack used by `WindowOptions::render`.
struct WindowRenderState {
Element inner; /// < The element wrapped inside this window.
const std::string& title; /// < The title of the window.
bool active = false; /// < Whether the window is the active one.
bool drag = false; /// < Whether the window is being dragged.
bool resize = false; /// < Whether the window is being resized.
bool hover_left = false; /// < Whether the resizeable left side is hovered.
bool hover_right = false; /// < Whether the resizeable right side is hovered.
bool hover_top = false; /// < Whether the resizeable top side is hovered.
bool hover_down = false; /// < Whether the resizeable down side is hovered.
};
// @brief Option for the `Window` component.
// @ingroup component
struct WindowOptions {
Component inner; /// < The component wrapped by this window.
ConstStringRef title = ""; /// < The title displayed by this window.
Ref<int> left = 0; /// < The left side position of the window.
Ref<int> top = 0; /// < The top side position of the window.
Ref<int> width = 20; /// < The width of the window.
Ref<int> height = 10; /// < The height of the window.
Ref<bool> resize_left = true; /// < Can the left side be resized?
Ref<bool> resize_right = true; /// < Can the right side be resized?
Ref<bool> resize_top = true; /// < Can the top side be resized?
Ref<bool> resize_down = true; /// < Can the down side be resized?
/// An optional function to customize how the window looks like:
std::function<Element(const WindowRenderState&)> render;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP */

View File

@@ -0,0 +1,112 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_EVENT_HPP
#define FTXUI_COMPONENT_EVENT_HPP
#include <ftxui/component/mouse.hpp> // for Mouse
#include <functional>
#include <string> // for string, operator==
#include <vector>
namespace ftxui {
class ScreenInteractive;
class ComponentBase;
/// @brief Represent an event. It can be key press event, a terminal resize, or
/// more ...
///
/// For example:
/// - Printable character can be created using Event::Character('a').
/// - Some special are predefined, like Event::ArrowLeft.
/// - One can find arbitrary code for special Events using:
/// ./example/util/print_key_press
/// For instance, CTLR+A maps to Event::Special({1});
///
/// Useful documentation about xterm specification:
/// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
struct Event {
// --- Constructor section ---------------------------------------------------
static Event Character(std::string);
static Event Character(char);
static Event Character(wchar_t);
static Event Special(std::string);
static Event Mouse(std::string, Mouse mouse);
static Event CursorReporting(std::string, int x, int y);
// --- Arrow ---
static const Event ArrowLeft;
static const Event ArrowRight;
static const Event ArrowUp;
static const Event ArrowDown;
static const Event ArrowLeftCtrl;
static const Event ArrowRightCtrl;
static const Event ArrowUpCtrl;
static const Event ArrowDownCtrl;
// --- Other ---
static const Event Backspace;
static const Event Delete;
static const Event Return;
static const Event Escape;
static const Event Tab;
static const Event TabReverse;
static const Event F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12;
static const Event Home;
static const Event End;
static const Event PageUp;
static const Event PageDown;
// --- Custom ---
static const Event Custom;
//--- Method section ---------------------------------------------------------
bool is_character() const { return type_ == Type::Character; }
std::string character() const { return input_; }
bool is_mouse() const { return type_ == Type::Mouse; }
struct Mouse& mouse() { return data_.mouse; }
bool is_cursor_reporting() const { return type_ == Type::CursorReporting; }
int cursor_x() const { return data_.cursor.x; }
int cursor_y() const { return data_.cursor.y; }
const std::string& input() const { return input_; }
bool operator==(const Event& other) const { return input_ == other.input_; }
bool operator!=(const Event& other) const { return !operator==(other); }
//--- State section ----------------------------------------------------------
ScreenInteractive* screen_ = nullptr;
private:
friend ComponentBase;
friend ScreenInteractive;
enum class Type {
Unknown,
Character,
Mouse,
CursorReporting,
};
Type type_ = Type::Unknown;
struct Cursor {
int x = 0;
int y = 0;
};
union {
struct Mouse mouse;
struct Cursor cursor;
} data_ = {};
std::string input_;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_EVENT_HPP */

View File

@@ -0,0 +1,38 @@
// 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.
#ifndef FTXUI_COMPONENT_LOOP_HPP
#define FTXUI_COMPONENT_LOOP_HPP
#include <memory> // for shared_ptr
#include "ftxui/component/component_base.hpp" // for ComponentBase
namespace ftxui {
class ComponentBase;
using Component = std::shared_ptr<ComponentBase>;
class ScreenInteractive;
class Loop {
public:
Loop(ScreenInteractive* screen, Component component);
~Loop();
bool HasQuitted();
void RunOnce();
void RunOnceBlocking();
void Run();
private:
// This class is non copyable.
Loop(const ScreenInteractive&) = delete;
Loop& operator=(const Loop&) = delete;
ScreenInteractive* screen_;
Component component_;
};
} // namespace ftxui
#endif // FTXUI_COMPONENT_LOOP_HPP

View File

@@ -0,0 +1,44 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_MOUSE_HPP
#define FTXUI_COMPONENT_MOUSE_HPP
namespace ftxui {
/// @brief A mouse event. It contains the coordinate of the mouse, the button
/// pressed and the modifier (shift, ctrl, meta).
/// @ingroup component
struct Mouse {
enum Button {
Left = 0,
Middle = 1,
Right = 2,
None = 3,
WheelUp = 4,
WheelDown = 5,
};
enum Motion {
Released = 0,
Pressed = 1,
};
// Button
Button button = Button::None;
// Motion
Motion motion = Motion::Pressed;
// Modifiers:
bool shift = false;
bool meta = false;
bool control = false;
// Coordinates:
int x = 0;
int y = 0;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_MOUSE_HPP */

View File

@@ -0,0 +1,140 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_RECEIVER_HPP_
#define FTXUI_COMPONENT_RECEIVER_HPP_
#include <algorithm> // for copy, max
#include <atomic> // for atomic, __atomic_base
#include <condition_variable> // for condition_variable
#include <functional>
#include <iostream>
#include <memory> // for unique_ptr, make_unique
#include <mutex> // for mutex, unique_lock
#include <queue> // for queue
#include <utility> // for move
namespace ftxui {
// Usage:
//
// Initialization:
// ---------------
//
// auto receiver = MakeReceiver<std:string>();
// auto sender_1= receiver->MakeSender();
// auto sender_2 = receiver->MakeSender();
//
// Then move the senders elsewhere, potentially in a different thread.
//
// On the producer side:
// ----------------------
// [thread 1] sender_1->Send("hello");
// [thread 2] sender_2->Send("world");
//
// On the consumer side:
// ---------------------
// char c;
// while(receiver->Receive(&c)) // Return true as long as there is a producer.
// print(c)
//
// Receiver::Receive() returns true when there are no more senders.
// clang-format off
template<class T> class SenderImpl;
template<class T> class ReceiverImpl;
template<class T> using Sender = std::unique_ptr<SenderImpl<T>>;
template<class T> using Receiver = std::unique_ptr<ReceiverImpl<T>>;
template<class T> Receiver<T> MakeReceiver();
// clang-format on
// ---- Implementation part ----
template <class T>
class SenderImpl {
public:
void Send(T t) { receiver_->Receive(std::move(t)); }
~SenderImpl() { receiver_->ReleaseSender(); }
Sender<T> Clone() { return receiver_->MakeSender(); }
private:
friend class ReceiverImpl<T>;
SenderImpl(ReceiverImpl<T>* consumer) : receiver_(consumer) {}
ReceiverImpl<T>* receiver_;
};
template <class T>
class ReceiverImpl {
public:
Sender<T> MakeSender() {
std::unique_lock<std::mutex> lock(mutex_);
senders_++;
return std::unique_ptr<SenderImpl<T>>(new SenderImpl<T>(this));
}
ReceiverImpl() { senders_ = 0; }
bool Receive(T* t) {
while (senders_ || !queue_.empty()) {
std::unique_lock<std::mutex> lock(mutex_);
if (queue_.empty())
notifier_.wait(lock);
if (queue_.empty())
continue;
*t = std::move(queue_.front());
queue_.pop();
return true;
}
return false;
}
bool ReceiveNonBlocking(T* t) {
std::unique_lock<std::mutex> lock(mutex_);
if (queue_.empty())
return false;
*t = queue_.front();
queue_.pop();
return true;
}
bool HasPending() {
std::unique_lock<std::mutex> lock(mutex_);
return !queue_.empty();
}
bool HasQuitted() {
std::unique_lock<std::mutex> lock(mutex_);
return queue_.empty() && !senders_;
}
private:
friend class SenderImpl<T>;
void Receive(T t) {
{
std::unique_lock<std::mutex> lock(mutex_);
queue_.push(std::move(t));
}
notifier_.notify_one();
}
void ReleaseSender() {
senders_--;
notifier_.notify_one();
}
std::mutex mutex_;
std::queue<T> queue_;
std::condition_variable notifier_;
std::atomic<int> senders_;
};
template <class T>
Receiver<T> MakeReceiver() {
return std::make_unique<ReceiverImpl<T>>();
}
} // namespace ftxui
#endif // FTXUI_COMPONENT_RECEIVER_HPP_

View File

@@ -0,0 +1,127 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP
#define FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP
#include <atomic> // for atomic
#include <ftxui/component/receiver.hpp> // for Receiver, Sender
#include <functional> // for function
#include <memory> // for shared_ptr
#include <string> // for string
#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 Task, Closure
#include "ftxui/screen/screen.hpp" // for Screen
namespace ftxui {
class ComponentBase;
class Loop;
struct Event;
using Component = std::shared_ptr<ComponentBase>;
class ScreenInteractivePrivate;
class ScreenInteractive : public Screen {
public:
// Constructors:
static ScreenInteractive FixedSize(int dimx, int dimy);
static ScreenInteractive Fullscreen();
static ScreenInteractive FitComponent();
static ScreenInteractive TerminalOutput();
// Options. Must be called before Loop().
void TrackMouse(bool enable = true);
// Return the currently active screen, nullptr if none.
static ScreenInteractive* Active();
// Start/Stop the main loop.
void Loop(Component);
void Exit();
Closure ExitLoopClosure();
// Post tasks to be executed by the loop.
void Post(Task task);
void PostEvent(Event event);
void RequestAnimationFrame();
CapturedMouse CaptureMouse();
// Decorate a function. The outputted one will execute similarly to the
// inputted one, but with the currently active screen terminal hooks
// temporarily uninstalled.
Closure WithRestoredIO(Closure);
private:
void ExitNow();
void Install();
void Uninstall();
void PreMain();
void PostMain();
bool HasQuitted();
void RunOnce(Component component);
void RunOnceBlocking(Component component);
void HandleTask(Component component, Task& task);
void Draw(Component component);
void ResetCursorPosition();
void Signal(int signal);
ScreenInteractive* suspended_screen_ = nullptr;
enum class Dimension {
FitComponent,
Fixed,
Fullscreen,
TerminalOutput,
};
Dimension dimension_ = Dimension::Fixed;
bool use_alternative_screen_ = false;
ScreenInteractive(int dimx,
int dimy,
Dimension dimension,
bool use_alternative_screen);
bool track_mouse_ = true;
Sender<Task> task_sender_;
Receiver<Task> task_receiver_;
std::string set_cursor_position;
std::string reset_cursor_position;
std::atomic<bool> quit_ = false;
std::thread event_listener_;
std::thread animation_listener_;
bool animation_requested_ = false;
animation::TimePoint previous_animation_time_;
int cursor_x_ = 1;
int cursor_y_ = 1;
bool mouse_captured = false;
bool previous_frame_resized_ = false;
bool frame_valid_ = false;
friend class Loop;
public:
class Private {
public:
static void Signal(ScreenInteractive& s, int signal) { s.Signal(signal); }
};
friend Private;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP */

View File

@@ -0,0 +1,17 @@
// 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.
#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, AnimationTask>;
} // namespace ftxui
#endif // FTXUI_COMPONENT_ANIMATION_HPP