28Element DefaultOptionTransform(
const EntryState& state) {
29 std::string label = (state.active ?
"> " :
" ") + state.label;
68class MenuBase :
public ComponentBase {
70 MenuBase(ConstStringListRef entries,
int* selected, Ref<MenuOption> option)
71 : entries_(entries), selected_(selected), option_(std::move(option)) {}
73 bool IsHorizontal() {
return ftxui::IsHorizontal(option_->direction); }
75 if (option_->on_change) {
81 if (option_->on_enter) {
87 if (*selected_ != selected_previous_) {
90 boxes_.resize(size());
91 *selected_ =
util::clamp(*selected_, 0, size() - 1);
92 selected_previous_ =
util::clamp(selected_previous_, 0, size() - 1);
93 selected_focus_ =
util::clamp(selected_focus_, 0, size() - 1);
94 focused_entry() =
util::clamp(focused_entry(), 0, size() - 1);
97 void OnAnimation(animation::Params& params)
override {
100 for (
auto& animator : animator_background_) {
101 animator.OnAnimation(params);
103 for (
auto& animator : animator_foreground_) {
104 animator.OnAnimation(params);
110 UpdateAnimationTarget();
113 const bool is_menu_focused =
Focused();
114 if (option_->elements_prefix) {
115 elements.push_back(option_->elements_prefix());
117 for (
int i = 0; i < size(); ++i) {
118 if (i != 0 && option_->elements_infix) {
119 elements.push_back(option_->elements_infix());
121 const bool is_focused = (focused_entry() == i) && is_menu_focused;
122 const bool is_selected = (*selected_ == i);
124 const EntryState state = {
131 auto focus_management =
132 is_menu_focused && (selected_focus_ == i) ?
focus :
nothing;
135 (option_->entries.transform ? option_->entries.transform
136 : DefaultOptionTransform)
138 elements.push_back(element | AnimatedColorStyle(i) |
reflect(boxes_[i]) |
141 if (option_->elements_postfix) {
142 elements.push_back(option_->elements_postfix());
145 if (IsInverted(option_->direction)) {
146 std::reverse(elements.begin(), elements.end());
150 IsHorizontal() ?
hbox(std::move(elements)) :
vbox(std::move(elements));
152 if (!option_->underline.enabled) {
153 return bar | reflect(box_);
156 if (IsHorizontal()) {
160 option_->underline.color_active,
161 option_->underline.color_inactive),
167 option_->underline.color_active,
168 option_->underline.color_inactive),
175 void SelectedTakeFocus() {
176 selected_previous_ = *selected_;
177 selected_focus_ = *selected_;
181 switch (option_->direction) {
195 switch (option_->direction) {
209 switch (option_->direction) {
223 switch (option_->direction) {
237 bool OnEvent(Event event)
override {
243 if (event.is_mouse()) {
244 return OnMouseEvent(event);
248 const int old_selected = *selected_;
271 (*selected_) = size() - 1;
274 *selected_ = (*selected_ + 1) % size();
277 *selected_ = (*selected_ + size() - 1) % size();
280 *selected_ =
util::clamp(*selected_, 0, size() - 1);
282 if (*selected_ != old_selected) {
283 focused_entry() = *selected_;
298 bool OnMouseEvent(Event event) {
301 return OnMouseWheel(event);
311 for (
int i = 0; i < size(); ++i) {
312 if (!boxes_[i].Contain(event.mouse().x, event.mouse().y)) {
320 if (*selected_ != i) {
322 selected_previous_ = *selected_;
331 bool OnMouseWheel(Event event) {
332 if (!box_.
Contain(event.mouse().x, event.mouse().y)) {
335 const int old_selected = *selected_;
344 *selected_ =
util::clamp(*selected_, 0, size() - 1);
346 if (*selected_ != old_selected) {
353 void UpdateAnimationTarget() {
355 UpdateUnderlineTarget();
358 void UpdateColorTarget() {
359 if (size() != (
int)animation_background_.size()) {
360 animation_background_.resize(size());
361 animation_foreground_.resize(size());
362 animator_background_.clear();
363 animator_foreground_.clear();
365 for (
int i = 0; i < size(); ++i) {
366 animation_background_[i] = 0.F;
367 animation_foreground_[i] = 0.F;
368 animator_background_.emplace_back(&animation_background_[i], 0.F,
369 std::chrono::milliseconds(0),
371 animator_foreground_.emplace_back(&animation_foreground_[i], 0.F,
372 std::chrono::milliseconds(0),
377 const bool is_menu_focused =
Focused();
378 for (
int i = 0; i < size(); ++i) {
379 const bool is_focused = (focused_entry() == i) && is_menu_focused;
380 const bool is_selected = (*selected_ == i);
381 float target = is_selected ? 1.F : is_focused ? 0.5F : 0.F;
382 if (animator_background_[i].to() != target) {
383 animator_background_[i] = animation::Animator(
384 &animation_background_[i], target,
385 option_->entries.animated_colors.background.duration,
386 option_->entries.animated_colors.background.function);
387 animator_foreground_[i] = animation::Animator(
388 &animation_foreground_[i], target,
389 option_->entries.animated_colors.foreground.duration,
390 option_->entries.animated_colors.foreground.function);
397 if (option_->entries.animated_colors.foreground.enabled) {
399 animation_foreground_[i],
400 option_->entries.animated_colors.foreground.inactive,
401 option_->entries.animated_colors.foreground.active));
404 if (option_->entries.animated_colors.background.enabled) {
406 animation_background_[i],
407 option_->entries.animated_colors.background.inactive,
408 option_->entries.animated_colors.background.active));
413 void UpdateUnderlineTarget() {
414 if (!option_->underline.enabled) {
418 if (FirstTarget() == animator_first_.
to() &&
419 SecondTarget() == animator_second_.
to()) {
423 if (FirstTarget() >= animator_first_.
to()) {
424 animator_first_ = animation::Animator(
425 &first_, FirstTarget(), option_->underline.follower_duration,
426 option_->underline.follower_function,
427 option_->underline.follower_delay);
429 animator_second_ = animation::Animator(
430 &second_, SecondTarget(), option_->underline.leader_duration,
431 option_->underline.leader_function, option_->underline.leader_delay);
433 animator_first_ = animation::Animator(
434 &first_, FirstTarget(), option_->underline.leader_duration,
435 option_->underline.leader_function, option_->underline.leader_delay);
437 animator_second_ = animation::Animator(
438 &second_, SecondTarget(), option_->underline.follower_duration,
439 option_->underline.follower_function,
440 option_->underline.follower_delay);
444 bool Focusable() const final {
return entries_.
size(); }
445 int& focused_entry() {
return option_->focused_entry(); }
446 int size()
const {
return int(entries_.
size()); }
447 float FirstTarget() {
448 if (boxes_.empty()) {
451 const int value = IsHorizontal() ? boxes_[*selected_].x_min - box_.
x_min
452 : boxes_[*selected_].y_min - box_.
y_min;
455 float SecondTarget() {
456 if (boxes_.empty()) {
459 const int value = IsHorizontal() ? boxes_[*selected_].x_max - box_.
x_min
460 : boxes_[*selected_].y_max - box_.
y_min;
465 ConstStringListRef entries_;
467 int selected_previous_ = *selected_;
468 int selected_focus_ = *selected_;
469 Ref<MenuOption> option_;
471 std::vector<Box> boxes_;
476 animation::Animator animator_first_ = animation::Animator(&first_, 0.F);
477 animation::Animator animator_second_ = animation::Animator(&second_, 0.F);
479 std::vector<animation::Animator> animator_background_;
480 std::vector<animation::Animator> animator_foreground_;
481 std::vector<float> animation_background_;
482 std::vector<float> animation_foreground_;
557 : label_(std::move(label)), option_(std::move(option)) {}
561 const bool focused = Focused();
562 UpdateAnimationTarget();
572 (option_->transform ? option_->transform : DefaultOptionTransform)
576 return element | AnimatedColorStyle() | focus_management |
reflect(box_);
579 void UpdateAnimationTarget() {
580 const bool focused = Focused();
581 float target = focused ? 1.F : hovered_ ? 0.5F : 0.F;
582 if (target == animator_background_.to()) {
585 animator_background_ =
587 option_->animated_colors.background.duration,
588 option_->animated_colors.background.function);
589 animator_foreground_ =
591 option_->animated_colors.foreground.duration,
592 option_->animated_colors.foreground.function);
597 if (option_->animated_colors.foreground.enabled) {
598 style = style |
color(Color::Interpolate(
599 animation_foreground_,
600 option_->animated_colors.foreground.inactive,
601 option_->animated_colors.foreground.active));
604 if (option_->animated_colors.background.enabled) {
605 style = style |
bgcolor(Color::Interpolate(
606 animation_background_,
607 option_->animated_colors.background.inactive,
608 option_->animated_colors.background.active));
613 bool Focusable()
const override {
return true; }
614 bool OnEvent(
Event event)
override {
619 hovered_ = box_.Contain(event.
mouse().
x, event.
mouse().
y);
635 animator_background_.OnAnimation(params);
636 animator_foreground_.OnAnimation(params);
642 bool hovered_ =
false;
644 float animation_background_ = 0.F;
645 float animation_foreground_ = 0.F;
652 return Make<Impl>(std::move(label), std::move(option));
static Color Interpolate(float t, const Color &a, const Color &b)
It implement rendering itself as ftxui::Element. It implement keyboard navigation by responding to ft...
bool Focused() const
Returns if the elements if focused by the user. True when the ComponentBase is focused by the user....
CapturedMouse CaptureMouse(const Event &event)
Take the CapturedMouse if available. There is only one component of them. It represents a component t...
void TakeFocus()
Configure all the ancestors to give focus to this component.
An adapter. Reference a list of strings.
An adapter. Own or reference a constant string. For convenience, this class convert multiple immutabl...
An adapter. Own or reference an mutable object.
void OnAnimation(Params &)
constexpr const T & clamp(const T &v, const T &lo, const T &hi)
Decorator bgcolor(Color)
Decorate using a background color.
Element xflex(Element)
Expand/Minimize if possible/needed on the X axis.
std::function< Element(Element)> Decorator
Element separatorVSelector(float up, float down, Color unselected_color, Color selected_color)
Draw an vertical bar, with the area in between up/downcolored differently.
Element nothing(Element element)
A decoration doing absolutely nothing.
std::shared_ptr< T > Make(Args &&... args)
std::shared_ptr< Node > Element
Component Toggle(ConstStringListRef entries, int *selected)
An horizontal list of elements. The user can navigate through them.
Element bold(Element)
Use a bold font, for elements with more emphasis.
Element yflex(Element)
Expand/Minimize if possible/needed on the Y axis.
Component MenuEntry(ConstStringRef label, Ref< MenuEntryOption >={})
A specific menu entry. They can be put into a Container::Vertical to form a menu.
Element separatorHSelector(float left, float right, Color unselected_color, Color selected_color)
Draw an horizontal bar, with the area in between left/right colored differently.
Element hbox(Elements)
A container displaying elements horizontally one by one.
std::vector< Element > Elements
Element inverted(Element)
Add a filter that will invert the foreground and the background colors.
Element text(std::wstring text)
Display a piece of unicode text.
Component Menu(ConstStringListRef entries, int *selected_, Ref< MenuOption >=MenuOption::Vertical())
A list of text. The focused element is selected.
Decorator reflect(Box &box)
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
std::shared_ptr< ComponentBase > Component
Decorator color(Color)
Decorate using a foreground color.
Element vbox(Elements)
A container displaying elements vertically one by one.
arguments for |ButtonOption::transform|, |CheckboxOption::transform|, |Radiobox::transform|,...
bool Contain(int x, int y) const
Represent an event. It can be key press event, a terminal resize, or more ...
static const Event TabReverse
static const Event PageUp
static const Event ArrowUp
static const Event ArrowDown
static const Event PageDown
static const Event Return
static const Event ArrowLeft
static const Event ArrowRight