FTXUI  2.0.0
C++ functional terminal UI.
Loading...
Searching...
No Matches
input.cpp
Go to the documentation of this file.
1#include <stddef.h> // for size_t
2#include <algorithm> // for clamp, max, min
3#include <functional> // for function
4#include <memory> // for shared_ptr, allocator
5#include <string> // for string, wstring
6#include <utility> // for move
7#include <vector> // for vector
8
9#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
10#include "ftxui/component/component.hpp" // for Make, Input
11#include "ftxui/component/component_base.hpp" // for ComponentBase
12#include "ftxui/component/component_options.hpp" // for InputOption
13#include "ftxui/component/deprecated.hpp" // for Input
14#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Backspace, Event::Custom, Event::Delete, Event::End, Event::Home, Event::Return
15#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
16#include "ftxui/component/screen_interactive.hpp" // for Component
17#include "ftxui/dom/elements.hpp" // for operator|, text, Element, reflect, inverted, Decorator, flex, focus, hbox, size, bold, dim, frame, select, EQUAL, HEIGHT
18#include "ftxui/screen/box.hpp" // for Box
19#include "ftxui/screen/string.hpp" // for GlyphPosition, GlyphCount, to_string, CellToGlyphIndex, to_wstring
20#include "ftxui/util/ref.hpp" // for StringRef, Ref, WideStringRef, ConstStringRef
21
22namespace ftxui {
23
24namespace {
25
26std::string PasswordField(int size) {
27 std::string out;
28 out.reserve(2 * size);
29 while (size--)
30 out += "•";
31 return out;
32}
33
34// An input box. The user can type text into it.
35class InputBase : public ComponentBase {
36 public:
37 InputBase(StringRef content,
38 ConstStringRef placeholder,
39 Ref<InputOption> option)
40 : content_(content), placeholder_(placeholder), option_(option) {}
41
42 int cursor_position_internal_ = 0;
43 int& cursor_position() {
44 int& opt = option_->cursor_position();
45 if (opt != -1)
46 return opt;
47 return cursor_position_internal_;
48 }
49
50 // Component implementation:
51 Element Render() override {
52 std::string password_content;
53 if (option_->password())
54 password_content = PasswordField(content_->size());
55 std::string& content = option_->password() ? password_content : *content_;
56
57 int size = GlyphCount(content);
58
59 cursor_position() = std::max(0, std::min<int>(size, cursor_position()));
60 auto main_decorator = flex | ftxui::size(HEIGHT, EQUAL, 1);
61 bool is_focused = Focused();
62
63 // placeholder.
64 if (size == 0) {
65 bool hovered = hovered_;
66 Decorator decorator = dim | main_decorator;
67 if (is_focused)
68 decorator = decorator | focus | inverted;
69 if (hovered || is_focused)
70 decorator = decorator | inverted;
71 return text(*placeholder_) | decorator | reflect(box_);
72 }
73
74 // Not focused.
75 if (!is_focused) {
76 if (hovered_)
77 return text(content) | main_decorator | inverted | reflect(box_);
78 else
79 return text(content) | main_decorator | reflect(box_);
80 }
81
82 int index_before_cursor = GlyphPosition(content, cursor_position());
83 int index_after_cursor = GlyphPosition(content, 1, index_before_cursor);
84 std::string part_before_cursor = content.substr(0, index_before_cursor);
85 std::string part_at_cursor = " ";
86 if (cursor_position() < size) {
87 part_at_cursor = content.substr(index_before_cursor,
88 index_after_cursor - index_before_cursor);
89 }
90 std::string part_after_cursor = content.substr(index_after_cursor);
91 auto focused = (is_focused || hovered_) ? focus : select;
92 return hbox({
93 text(part_before_cursor),
94 text(part_at_cursor) | focused | inverted | reflect(cursor_box_),
95 text(part_after_cursor),
96 }) |
97 flex | frame | bold | main_decorator | reflect(box_);
98 }
99
100 bool OnEvent(Event event) override {
101 cursor_position() =
102 std::max(0, std::min<int>(content_->size(), cursor_position()));
103
104 if (event.is_mouse())
105 return OnMouseEvent(event);
106
107 std::string c;
108
109 // Backspace.
110 if (event == Event::Backspace) {
111 if (cursor_position() == 0)
112 return false;
113 size_t start = GlyphPosition(*content_, cursor_position() - 1);
114 size_t end = GlyphPosition(*content_, cursor_position());
115 content_->erase(start, end - start);
116 cursor_position()--;
117 option_->on_change();
118 return true;
119 }
120
121 // Delete
122 if (event == Event::Delete) {
123 if (cursor_position() == int(content_->size()))
124 return false;
125 size_t start = GlyphPosition(*content_, cursor_position());
126 size_t end = GlyphPosition(*content_, cursor_position() + 1);
127 content_->erase(start, end - start);
128 option_->on_change();
129 return true;
130 }
131
132 // Enter.
133 if (event == Event::Return) {
134 option_->on_enter();
135 return true;
136 }
137
138 if (event == Event::Custom) {
139 return false;
140 }
141
142 if (event == Event::ArrowLeft && cursor_position() > 0) {
143 cursor_position()--;
144 return true;
145 }
146
147 if (event == Event::ArrowRight &&
148 cursor_position() < (int)content_->size()) {
149 cursor_position()++;
150 return true;
151 }
152
153 if (event == Event::Home) {
154 cursor_position() = 0;
155 return true;
156 }
157
158 if (event == Event::End) {
159 cursor_position() = GlyphCount(*content_);
160 return true;
161 }
162
163 // Content
164 if (event.is_character()) {
165 size_t start = GlyphPosition(*content_, cursor_position());
166 content_->insert(start, event.character());
167 cursor_position()++;
168 option_->on_change();
169 return true;
170 }
171 return false;
172 }
173
174 private:
175 bool OnMouseEvent(Event event) {
176 hovered_ =
177 box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
178 if (!hovered_)
179 return false;
180
181 if (event.mouse().button != Mouse::Left ||
182 event.mouse().motion != Mouse::Pressed) {
183 return false;
184 }
185
186 TakeFocus();
187 if (content_->size() == 0)
188 return true;
189
190 auto mapping = CellToGlyphIndex(*content_);
191 int original_glyph = cursor_position();
192 original_glyph = std::clamp(original_glyph, 0, int(mapping.size()));
193 int original_cell = 0;
194 for (size_t i = 0; i < mapping.size(); i++) {
195 if (mapping[i] == original_glyph) {
196 original_cell = i;
197 break;
198 }
199 }
200 if (mapping[original_cell] != original_glyph)
201 original_cell = mapping.size();
202 int target_cell = original_cell + event.mouse().x - cursor_box_.x_min;
203 int target_glyph = target_cell < (int)mapping.size() ? mapping[target_cell]
204 : (int)mapping.size();
205 target_glyph = std::clamp(target_glyph, 0, GlyphCount(*content_));
206 if (cursor_position() != target_glyph) {
207 cursor_position() = target_glyph;
208 option_->on_change();
209 }
210 return true;
211 }
212
213 bool Focusable() const final { return true; }
214
215 bool hovered_ = false;
216 StringRef content_;
217 ConstStringRef placeholder_;
218
219 Box box_;
220 Box cursor_box_;
221 Ref<InputOption> option_;
222};
223
224// An input box. The user can type text into it.
225// For convenience, the std::wstring version of Input simply wrap a
226// InputBase.
227class WideInputBase : public InputBase {
228 public:
229 WideInputBase(WideStringRef content,
230 ConstStringRef placeholder,
231 Ref<InputOption> option)
232 : InputBase(&wrapped_content_, std::move(placeholder), std::move(option)),
233 content_(std::move(content)),
234 wrapped_content_(to_string(*content_)) {}
235
236 Element Render() override {
237 wrapped_content_ = to_string(*content_);
238 return InputBase::Render();
239 }
240
241 bool OnEvent(Event event) override {
242 wrapped_content_ = to_string(*content_);
243 if (InputBase::OnEvent(event)) {
244 *content_ = to_wstring(wrapped_content_);
245 return true;
246 }
247 return false;
248 }
249
250 WideStringRef content_;
251 std::string wrapped_content_;
252};
253
254} // namespace
255
256/// @brief An input box for editing text.
257/// @param content The editable content.
258/// @param placeholder The text displayed when content is still empty.
259/// @param option Additional optional parameters.
260/// @ingroup component
261/// @see InputBase
262///
263/// ### Example
264///
265/// ```cpp
266/// auto screen = ScreenInteractive::FitComponent();
267/// std::string content= "";
268/// std::string placeholder = "placeholder";
269/// Component input = Input(&content, &placeholder);
270/// screen.Loop(input);
271/// ```
272///
273/// ### Output
274///
275/// ```bash
276/// placeholder
277/// ```
279 ConstStringRef placeholder,
280 Ref<InputOption> option) {
281 return Make<InputBase>(content, placeholder, std::move(option));
282}
283
284/// @brief . An input box for editing text.
285/// @param content The editable content.
286/// @param placeholder The text displayed when content is still empty.
287/// @param option Additional optional parameters.
288/// @ingroup component
289/// @see InputBase
290///
291/// ### Example
292///
293/// ```cpp
294/// auto screen = ScreenInteractive::FitComponent();
295/// std::string content= "";
296/// std::string placeholder = "placeholder";
297/// Component input = Input(&content, &placeholder);
298/// screen.Loop(input);
299/// ```
300///
301/// ### Output
302///
303/// ```bash
304/// placeholder
305/// ```
307 ConstStringRef placeholder,
308 Ref<InputOption> option) {
309 return Make<WideInputBase>(content, placeholder, std::move(option));
310}
311
312} // namespace ftxui
313
314// Copyright 2020 Arthur Sonzogni. All rights reserved.
315// Use of this source code is governed by the MIT license that can be found in
316// the LICENSE file.
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
An adapter. Own or reference a constant string. For convenience, this class convert multiple mutable ...
Definition ref.hpp:43
An adapter. Own or reference a constant string. For convenience, this class convert multiple mutable ...
Definition ref.hpp:59
std::function< Element(Element)> Decorator
Definition elements.hpp:20
Element flex(Element)
Make a child element to expand proportionnally to the space left in a container.
Definition flex.cpp:119
std::shared_ptr< T > Make(Args &&... args)
Definition component.hpp:25
std::shared_ptr< Node > Element
Definition elements.hpp:18
Component Input(StringRef content, ConstStringRef placeholder, Ref< InputOption > option={})
An input box for editing text.
Definition input.cpp:278
Element bold(Element)
Use a bold font, for elements with more emphasis.
Definition bold.cpp:28
Element focus(Element)
Definition frame.cpp:79
Element hbox(Elements)
A container displaying elements horizontally one by one.
Definition hbox.cpp:76
std::wstring to_wstring(const std::string &s)
Convert a std::wstring into a UTF8 std::string.
Definition string.cpp:397
Element inverted(Element)
Add a filter that will invert the foreground and the background colors.
Definition inverted.cpp:29
std::string to_string(const std::wstring &s)
Convert a UTF8 std::string into a std::wstring.
Definition string.cpp:391
Element text(std::wstring text)
Display a piece of unicode text.
Definition text.cpp:106
int GlyphPosition(const std::string &input, size_t glyph_index, size_t start=0)
Definition string.cpp:291
std::vector< int > CellToGlyphIndex(const std::string &input)
Definition string.cpp:319
int GlyphCount(const std::string &input)
Definition string.cpp:359
Decorator reflect(Box &box)
Definition reflect.cpp:39
Element dim(Element)
Use a light font, for elements with less emphasis.
Definition dim.cpp:28
Element frame(Element)
Allow an element to be displayed inside a 'virtual' area. It size can be larger than its container....
Definition frame.cpp:138
void Render(Screen &screen, const Element &node)
Display an element on a ftxui::Screen.
Definition node.cpp:40
Decorator size(Direction, Constraint, int value)
Apply a constraint on the size of an element.
Definition size.cpp:86
Element select(Element)
Definition frame.cpp:38
std::shared_ptr< ComponentBase > Component
static Event Custom
Definition event.hpp:56
static const Event Backspace
Definition event.hpp:41
static const Event End
Definition event.hpp:50
static const Event Home
Definition event.hpp:49
static const Event Return
Definition event.hpp:43
static const Event ArrowLeft
Definition event.hpp:35
static const Event Delete
Definition event.hpp:42
static const Event ArrowRight
Definition event.hpp:36