27Element DefaultOptionTransform(
const EntryState& state) {
28 std::string label = (state.active ?
"> " :
" ") + state.label;
67class MenuBase :
public ComponentBase {
69 MenuBase(ConstStringListRef entries,
int* selected, Ref<MenuOption> option)
70 : entries_(entries), selected_(selected), option_(std::move(option)) {}
72 bool IsHorizontal() {
return ftxui::IsHorizontal(option_->direction); }
74 if (option_->on_change) {
80 if (option_->on_enter) {
86 if (*selected_ != selected_previous_) {
89 boxes_.resize(size());
90 *selected_ =
util::clamp(*selected_, 0, size() - 1);
91 selected_previous_ =
util::clamp(selected_previous_, 0, size() - 1);
92 selected_focus_ =
util::clamp(selected_focus_, 0, size() - 1);
93 focused_entry() =
util::clamp(focused_entry(), 0, size() - 1);
96 void OnAnimation(animation::Params& params)
override {
99 for (
auto& animator : animator_background_) {
100 animator.OnAnimation(params);
102 for (
auto& animator : animator_foreground_) {
103 animator.OnAnimation(params);
109 UpdateAnimationTarget();
112 const bool is_menu_focused =
Focused();
113 if (option_->elements_prefix) {
114 elements.push_back(option_->elements_prefix());
116 for (
int i = 0; i < size(); ++i) {
117 if (i != 0 && option_->elements_infix) {
118 elements.push_back(option_->elements_infix());
120 const bool is_focused = (focused_entry() == i) && is_menu_focused;
121 const bool is_selected = (*selected_ == i);
123 const EntryState state = {
130 auto focus_management =
131 is_menu_focused && (selected_focus_ == i) ?
focus :
nothing;
134 (option_->entries.transform ? option_->entries.transform
135 : DefaultOptionTransform)
137 elements.push_back(element | AnimatedColorStyle(i) |
reflect(boxes_[i]) |
140 if (option_->elements_postfix) {
141 elements.push_back(option_->elements_postfix());
144 if (IsInverted(option_->direction)) {
145 std::reverse(elements.begin(), elements.end());
149 IsHorizontal() ?
hbox(std::move(elements)) :
vbox(std::move(elements));
151 if (!option_->underline.enabled) {
152 return bar | reflect(box_);
155 if (IsHorizontal()) {
159 option_->underline.color_active,
160 option_->underline.color_inactive),
166 option_->underline.color_active,
167 option_->underline.color_inactive),
174 void SelectedTakeFocus() {
175 selected_previous_ = *selected_;
176 selected_focus_ = *selected_;
180 switch (option_->direction) {
194 switch (option_->direction) {
208 switch (option_->direction) {
222 switch (option_->direction) {
236 bool OnEvent(Event event)
override {
242 if (event.is_mouse()) {
243 return OnMouseEvent(event);
247 const int old_selected = *selected_;
270 (*selected_) = size() - 1;
273 *selected_ = (*selected_ + 1) % size();
276 *selected_ = (*selected_ + size() - 1) % size();
279 *selected_ =
util::clamp(*selected_, 0, size() - 1);
281 if (*selected_ != old_selected) {
282 focused_entry() = *selected_;
297 bool OnMouseEvent(Event event) {
300 return OnMouseWheel(event);
310 for (
int i = 0; i < size(); ++i) {
311 if (!boxes_[i].Contain(event.mouse().x, event.mouse().y)) {
319 if (*selected_ != i) {
321 selected_previous_ = *selected_;
330 bool OnMouseWheel(Event event) {
331 if (!box_.
Contain(event.mouse().x, event.mouse().y)) {
334 const int old_selected = *selected_;
343 *selected_ =
util::clamp(*selected_, 0, size() - 1);
345 if (*selected_ != old_selected) {
352 void UpdateAnimationTarget() {
354 UpdateUnderlineTarget();
357 void UpdateColorTarget() {
358 if (size() != (
int)animation_background_.size()) {
359 animation_background_.resize(size());
360 animation_foreground_.resize(size());
361 animator_background_.clear();
362 animator_foreground_.clear();
364 for (
int i = 0; i < size(); ++i) {
365 animation_background_[i] = 0.F;
366 animation_foreground_[i] = 0.F;
367 animator_background_.emplace_back(&animation_background_[i], 0.F,
368 std::chrono::milliseconds(0),
370 animator_foreground_.emplace_back(&animation_foreground_[i], 0.F,
371 std::chrono::milliseconds(0),
376 const bool is_menu_focused =
Focused();
377 for (
int i = 0; i < size(); ++i) {
378 const bool is_focused = (focused_entry() == i) && is_menu_focused;
379 const bool is_selected = (*selected_ == i);
380 float target = is_selected ? 1.F : is_focused ? 0.5F : 0.F;
381 if (animator_background_[i].to() != target) {
382 animator_background_[i] = animation::Animator(
383 &animation_background_[i], target,
384 option_->entries.animated_colors.background.duration,
385 option_->entries.animated_colors.background.function);
386 animator_foreground_[i] = animation::Animator(
387 &animation_foreground_[i], target,
388 option_->entries.animated_colors.foreground.duration,
389 option_->entries.animated_colors.foreground.function);
396 if (option_->entries.animated_colors.foreground.enabled) {
398 animation_foreground_[i],
399 option_->entries.animated_colors.foreground.inactive,
400 option_->entries.animated_colors.foreground.active));
403 if (option_->entries.animated_colors.background.enabled) {
405 animation_background_[i],
406 option_->entries.animated_colors.background.inactive,
407 option_->entries.animated_colors.background.active));
412 void UpdateUnderlineTarget() {
413 if (!option_->underline.enabled) {
417 if (FirstTarget() == animator_first_.
to() &&
418 SecondTarget() == animator_second_.
to()) {
422 if (FirstTarget() >= animator_first_.
to()) {
423 animator_first_ = animation::Animator(
424 &first_, FirstTarget(), option_->underline.follower_duration,
425 option_->underline.follower_function,
426 option_->underline.follower_delay);
428 animator_second_ = animation::Animator(
429 &second_, SecondTarget(), option_->underline.leader_duration,
430 option_->underline.leader_function, option_->underline.leader_delay);
432 animator_first_ = animation::Animator(
433 &first_, FirstTarget(), option_->underline.leader_duration,
434 option_->underline.leader_function, option_->underline.leader_delay);
436 animator_second_ = animation::Animator(
437 &second_, SecondTarget(), option_->underline.follower_duration,
438 option_->underline.follower_function,
439 option_->underline.follower_delay);
443 bool Focusable() const final {
return entries_.
size(); }
444 int& focused_entry() {
return option_->focused_entry(); }
445 int size()
const {
return int(entries_.
size()); }
446 float FirstTarget() {
447 if (boxes_.empty()) {
450 const int value = IsHorizontal() ? boxes_[*selected_].x_min - box_.
x_min
451 : boxes_[*selected_].y_min - box_.
y_min;
454 float SecondTarget() {
455 if (boxes_.empty()) {
458 const int value = IsHorizontal() ? boxes_[*selected_].x_max - box_.
x_min
459 : boxes_[*selected_].y_max - box_.
y_min;
464 ConstStringListRef entries_;
466 int selected_previous_ = *selected_;
467 int selected_focus_ = *selected_;
468 Ref<MenuOption> option_;
470 std::vector<Box> boxes_;
475 animation::Animator animator_first_ = animation::Animator(&first_, 0.F);
476 animation::Animator animator_second_ = animation::Animator(&second_, 0.F);
478 std::vector<animation::Animator> animator_background_;
479 std::vector<animation::Animator> animator_foreground_;
480 std::vector<float> animation_background_;
481 std::vector<float> animation_foreground_;
556 : label_(std::move(label)), option_(std::move(option)) {}
560 const bool focused = Focused();
561 UpdateAnimationTarget();
571 (option_->transform ? option_->transform : DefaultOptionTransform)
575 return element | AnimatedColorStyle() | focus_management |
reflect(box_);
578 void UpdateAnimationTarget() {
579 const bool focused = Focused();
580 float target = focused ? 1.F : hovered_ ? 0.5F : 0.F;
581 if (target == animator_background_.to()) {
584 animator_background_ =
586 option_->animated_colors.background.duration,
587 option_->animated_colors.background.function);
588 animator_foreground_ =
590 option_->animated_colors.foreground.duration,
591 option_->animated_colors.foreground.function);
596 if (option_->animated_colors.foreground.enabled) {
597 style = style |
color(Color::Interpolate(
598 animation_foreground_,
599 option_->animated_colors.foreground.inactive,
600 option_->animated_colors.foreground.active));
603 if (option_->animated_colors.background.enabled) {
604 style = style |
bgcolor(Color::Interpolate(
605 animation_background_,
606 option_->animated_colors.background.inactive,
607 option_->animated_colors.background.active));
612 bool Focusable()
const override {
return true; }
613 bool OnEvent(
Event event)
override {
618 hovered_ = box_.Contain(event.
mouse().
x, event.
mouse().
y);
634 animator_background_.OnAnimation(params);
635 animator_foreground_.OnAnimation(params);
641 bool hovered_ =
false;
643 float animation_background_ = 0.F;
644 float animation_foreground_ = 0.F;
651 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