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