FTXUI  2.0.0
C++ functional terminal UI.
Loading...
Searching...
No Matches
menu.cpp
Go to the documentation of this file.
1#include <algorithm> // for clamp, max
2#include <functional> // for function
3#include <memory> // for shared_ptr, allocator_traits<>::value_type
4#include <string> // for operator+, string
5#include <utility> // for move
6#include <vector> // for vector
7
8#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
9#include "ftxui/component/component.hpp" // for Make, Menu, MenuEntry
10#include "ftxui/component/component_base.hpp" // for ComponentBase
11#include "ftxui/component/component_options.hpp" // for MenuOption, MenuEntryOption
12#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::End, Event::Home, Event::PageDown, Event::PageUp, Event::Return, Event::Tab, Event::TabReverse
13#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Released, Mouse::WheelDown, Mouse::WheelUp, Mouse::None
14#include "ftxui/component/screen_interactive.hpp" // for Component
15#include "ftxui/dom/elements.hpp" // for operator|, Element, reflect, text, nothing, select, vbox, Elements, focus
16#include "ftxui/screen/box.hpp" // for Box
17#include "ftxui/screen/util.hpp"
18#include "ftxui/util/ref.hpp" // for Ref, ConstStringListRef, ConstStringRef
19
20namespace ftxui {
21
22/// @brief A list of items. The user can navigate through them.
23/// @ingroup component
24class MenuBase : public ComponentBase {
25 public:
26 MenuBase(ConstStringListRef entries, int* selected, Ref<MenuOption> option)
27 : entries_(entries), selected_(selected), option_(option) {}
28
29 Element Render() override {
30 Clamp();
31 Elements elements;
32 bool is_menu_focused = Focused();
33 for (int i = 0; i < size(); ++i) {
34 bool is_focused = (focused_entry() == i) && is_menu_focused;
35 bool is_selected = (*selected_ == i);
36
37 auto style = is_selected ? (is_focused ? option_->style_selected_focused
38 : option_->style_selected)
39 : (is_focused ? option_->style_focused
40 : option_->style_normal);
41 auto focus_management = !is_selected ? nothing
42 : is_menu_focused ? focus
43 : select;
44 auto icon = is_selected ? "> " : " ";
45 elements.push_back(text(icon + entries_[i]) | style | focus_management |
46 reflect(boxes_[i]));
47 }
48 return vbox(std::move(elements)) | reflect(box_);
49 }
50
51 bool OnEvent(Event event) override {
52 Clamp();
53 if (!CaptureMouse(event))
54 return false;
55
56 if (event.is_mouse())
57 return OnMouseEvent(event);
58
59 if (Focused()) {
60 int old_selected = *selected_;
61 if (event == Event::ArrowUp || event == Event::Character('k'))
62 (*selected_)--;
63 if (event == Event::ArrowDown || event == Event::Character('j'))
64 (*selected_)++;
65 if (event == Event::PageUp)
66 (*selected_) -= box_.y_max - box_.y_min;
67 if (event == Event::PageDown)
68 (*selected_) += box_.y_max - box_.y_min;
69 if (event == Event::Home)
70 (*selected_) = 0;
71 if (event == Event::End)
72 (*selected_) = size() - 1;
73 if (event == Event::Tab && size())
74 *selected_ = (*selected_ + 1) % size();
75 if (event == Event::TabReverse && size())
76 *selected_ = (*selected_ + size() - 1) % size();
77
78 *selected_ = util::clamp(*selected_, 0, size() - 1);
79
80 if (*selected_ != old_selected) {
81 focused_entry() = *selected_;
82 option_->on_change();
83 return true;
84 }
85 }
86
87 if (event == Event::Return) {
88 option_->on_enter();
89 return true;
90 }
91
92 return false;
93 }
94
95 bool OnMouseEvent(Event event) {
96 if (event.mouse().button == Mouse::WheelDown ||
97 event.mouse().button == Mouse::WheelUp) {
98 return OnMouseWheel(event);
99 }
100
101 if (event.mouse().button != Mouse::None &&
102 event.mouse().button != Mouse::Left) {
103 return false;
104 }
105 if (!CaptureMouse(event))
106 return false;
107 for (int i = 0; i < size(); ++i) {
108 if (!boxes_[i].Contain(event.mouse().x, event.mouse().y))
109 continue;
110
111 TakeFocus();
112 focused_entry() = i;
113 if (event.mouse().button == Mouse::Left &&
114 event.mouse().motion == Mouse::Released) {
115 if (*selected_ != i) {
116 *selected_ = i;
117 option_->on_change();
118 }
119 return true;
120 }
121 }
122 return false;
123 }
124
125 bool OnMouseWheel(Event event) {
126 if (!box_.Contain(event.mouse().x, event.mouse().y))
127 return false;
128 int old_selected = *selected_;
129
130 if (event.mouse().button == Mouse::WheelUp)
131 (*selected_)--;
132 if (event.mouse().button == Mouse::WheelDown)
133 (*selected_)++;
134
135 *selected_ = util::clamp(*selected_, 0, size() - 1);
136
137 if (*selected_ != old_selected)
138 option_->on_change();
139 return true;
140 }
141
142 void Clamp() {
143 boxes_.resize(size());
144 *selected_ = util::clamp(*selected_, 0, size() - 1);
145 focused_entry() = util::clamp(focused_entry(), 0, size() - 1);
146 }
147
148 bool Focusable() const final { return entries_.size(); }
149 int& focused_entry() { return option_->focused_entry(); }
150 int size() const { return entries_.size(); }
151
152 protected:
153 ConstStringListRef entries_;
154 int* selected_ = 0;
155 Ref<MenuOption> option_;
156
157 std::vector<Box> boxes_;
158 Box box_;
159};
160
161/// @brief A list of text. The focused element is selected.
162/// @param entries The list of entries in the menu.
163/// @param selected The index of the currently selected element.
164/// @param option Additional optional parameters.
165/// @ingroup component
166/// @see MenuBase
167///
168/// ### Example
169///
170/// ```cpp
171/// auto screen = ScreenInteractive::TerminalOutput();
172/// std::vector<std::string> entries = {
173/// "entry 1",
174/// "entry 2",
175/// "entry 3",
176/// };
177/// int selected = 0;
178/// auto menu = Menu(&entries, &selected);
179/// screen.Loop(menu);
180/// ```
181///
182/// ### Output
183///
184/// ```bash
185/// > entry 1
186/// entry 2
187/// entry 3
188/// ```
190 int* selected,
191 Ref<MenuOption> option) {
192 return Make<MenuBase>(entries, selected, std::move(option));
193}
194
196 class Impl : public ComponentBase {
197 public:
198 Impl(ConstStringRef label, Ref<MenuEntryOption> option)
199 : label_(std::move(label)), option_(std::move(option)) {}
200
201 private:
202 Element Render() override {
203 bool focused = Focused();
204 auto style =
205 hovered_ ? (focused ? option_->style_selected_focused
206 : option_->style_selected)
207 : (focused ? option_->style_focused : option_->style_normal);
208 auto focus_management = focused ? select : nothing;
209 auto label = focused ? "> " + (*label_) //
210 : " " + (*label_);
211 return text(label) | style | focus_management | reflect(box_);
212 }
213 bool Focusable() const override { return true; }
214 bool OnEvent(Event event) override {
215 if (!event.is_mouse())
216 return false;
217
218 hovered_ = box_.Contain(event.mouse().x, event.mouse().y);
219
220 if (!hovered_)
221 return false;
222
223 if (event.mouse().button == Mouse::Left &&
224 event.mouse().motion == Mouse::Released) {
225 TakeFocus();
226 return true;
227 }
228
229 return false;
230 }
231 ConstStringRef label_;
232 Ref<MenuEntryOption> option_;
233 Box box_;
234 bool hovered_ = false;
235 };
236
237 return Make<Impl>(std::move(label), std::move(option));
238}
239
240} // namespace ftxui
241
242// Copyright 2020 Arthur Sonzogni. All rights reserved.
243// Use of this source code is governed by the MIT license that can be found in
244// the LICENSE file.
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.
Definition ref.hpp:96
size_t size() const
Definition ref.hpp:101
An adapter. Own or reference a constant string. For convenience, this class convert multiple immutabl...
Definition ref.hpp:76
An adapter. Own or reference an mutable object.
Definition ref.hpp:27
constexpr const T & clamp(const T &v, const T &lo, const T &hi)
Definition util.hpp:6
Element nothing(Element element)
A decoration doing absolutely nothing.
Definition util.cpp:26
std::shared_ptr< T > Make(Args &&... args)
Definition component.hpp:25
std::shared_ptr< Node > Element
Definition elements.hpp:18
Element focus(Element)
Definition frame.cpp:79
std::vector< Element > Elements
Definition elements.hpp:19
Element text(std::wstring text)
Display a piece of unicode text.
Definition text.cpp:106
Component Menu(ConstStringListRef entries, int *selected_, Ref< MenuOption >={})
A list of text. The focused element is selected.
Definition menu.cpp:189
Decorator reflect(Box &box)
Definition reflect.cpp:39
Component MenuEntry(ConstStringRef label, Ref< MenuEntryOption >={})
Definition menu.cpp:195
void Render(Screen &screen, const Element &node)
Display an element on a ftxui::Screen.
Definition node.cpp:40
Element select(Element)
Definition frame.cpp:38
std::shared_ptr< ComponentBase > Component
Element vbox(Elements)
A container displaying elements vertically one by one.
Definition vbox.cpp:77
int y_min
Definition box.hpp:9
bool Contain(int x, int y)
Definition box.cpp:20
int y_max
Definition box.hpp:10
Represent an event. It can be key press event, a terminal resize, or more ...
Definition event.hpp:25
static const Event TabReverse
Definition event.hpp:46
static const Event PageUp
Definition event.hpp:52
bool is_mouse() const
Definition event.hpp:62
struct Mouse & mouse()
Definition event.hpp:63
static const Event ArrowUp
Definition event.hpp:37
static const Event Tab
Definition event.hpp:45
static const Event ArrowDown
Definition event.hpp:38
static const Event End
Definition event.hpp:50
static const Event Home
Definition event.hpp:49
static const Event PageDown
Definition event.hpp:53
static const Event Return
Definition event.hpp:43
Button button
Definition mouse.hpp:24
Motion motion
Definition mouse.hpp:27