30std::vector<std::string> SplitLines(std::string_view input) {
31 std::vector<std::string> output;
33 size_t end = input.find(
'\n');
34 while (end != std::string_view::npos) {
35 output.push_back(std::string(input.substr(start, end - start)));
37 end = input.find(
'\n', start);
39 output.push_back(std::string(input.substr(start)));
43size_t GlyphWidth(std::string_view input,
size_t iter) {
54bool IsWordCodePoint(uint32_t codepoint) {
82bool IsWordCharacter(std::string_view input,
size_t iter) {
88 return IsWordCodePoint(ucs);
92class InputBase :
public ComponentBase,
public InputOption {
95 InputBase(InputOption option) : InputOption(std::move(option)) {}
100 const bool is_focused = Focused();
101 const auto focused = (!is_focused && !hovered_) ? focus
102 : insert() ? focusCursorBarBlinking
103 : focusCursorBlockBlinking;
105 auto transform_func =
106 transform ? transform : InputOption::Default().transform;
109 if (content->empty()) {
112 return transform_func({
113 std::move(
element), hovered_, is_focused,
120 const std::vector<std::string> lines = SplitLines(*content);
122 cursor_position() =
util::clamp(cursor_position(), 0, (
int)content->size());
126 int cursor_char_index = cursor_position();
127 for (
const auto& line : lines) {
128 if (cursor_char_index <= (
int)line.size()) {
132 cursor_char_index -=
static_cast<int>(line.size() + 1);
137 elements.push_back(
text(
"") | focused);
140 elements.reserve(lines.size());
141 for (
size_t i = 0; i < lines.size(); ++i) {
142 const std::string& line = lines[i];
145 if (
int(i) != cursor_line) {
146 elements.push_back(
Text(line));
151 const std::string cursor_cell = is_focused ?
" " :
"";
152 if (cursor_char_index >= (
int)line.size()) {
156 text(cursor_cell) | focused |
reflect(cursor_box_),
163 const int glyph_start = cursor_char_index;
164 const int glyph_end =
static_cast<int>(
GlyphNext(line, glyph_start));
165 const std::string part_before_cursor = line.substr(0, glyph_start);
166 const std::string part_at_cursor =
167 line.substr(glyph_start, glyph_end - glyph_start);
168 const std::string part_after_cursor = line.substr(glyph_end);
170 Text(part_before_cursor),
171 Text(part_at_cursor) | focused |
reflect(cursor_box_),
172 Text(part_after_cursor),
179 return transform_func({
180 std::move(
element), hovered_, is_focused,
193 out.reserve(glyph_count * 3);
194 for (
size_t i = 0; i < glyph_count; ++i) {
200 bool HandleBackspace() {
201 if (cursor_position() == 0) {
204 const size_t start =
GlyphPrevious(content(), cursor_position());
205 const size_t end = cursor_position();
206 content->erase(start, end - start);
207 cursor_position() =
static_cast<int>(start);
208 App::PostEventOrExecute(on_change);
213 if (cursor_position() == (
int)content->size()) {
216 const size_t start = cursor_position();
217 const size_t end =
GlyphNext(content(), cursor_position());
218 content->erase(start, end - start);
222 bool HandleDelete() {
224 App::PostEventOrExecute(on_change);
230 bool HandleArrowLeft() {
231 if (cursor_position() == 0) {
236 static_cast<int>(
GlyphPrevious(content(), cursor_position()));
240 bool HandleArrowRight() {
241 if (cursor_position() == (
int)content->size()) {
246 static_cast<int>(
GlyphNext(content(), cursor_position()));
250 size_t CursorColumn() {
251 size_t iter = cursor_position();
258 if (content()[iter] ==
'\n') {
264 width +=
static_cast<int>(GlyphWidth(content(), iter));
271 void MoveCursorColumn(
int columns) {
272 while (columns > 0) {
273 if (cursor_position() == (
int)content().
size() ||
274 content()[cursor_position()] ==
'\n') {
281 columns -=
static_cast<int>(GlyphWidth(content(), cursor_position()));
284 static_cast<int>(
GlyphNext(content(), cursor_position()));
288 bool HandleArrowUp() {
289 if (cursor_position() == 0) {
293 const size_t columns = CursorColumn();
297 if (cursor_position() == 0) {
300 const size_t previous =
GlyphPrevious(content(), cursor_position());
301 if (content()[previous] ==
'\n') {
304 cursor_position() =
static_cast<int>(previous);
307 static_cast<int>(
GlyphPrevious(content(), cursor_position()));
309 if (cursor_position() == 0) {
312 const size_t previous =
GlyphPrevious(content(), cursor_position());
313 if (content()[previous] ==
'\n') {
316 cursor_position() =
static_cast<int>(previous);
319 MoveCursorColumn(
static_cast<int>(columns));
323 bool HandleArrowDown() {
324 if (cursor_position() == (
int)content->size()) {
328 const size_t columns = CursorColumn();
332 if (content()[cursor_position()] ==
'\n') {
336 static_cast<int>(
GlyphNext(content(), cursor_position()));
337 if (cursor_position() == (
int)content().
size()) {
342 static_cast<int>(
GlyphNext(content(), cursor_position()));
344 MoveCursorColumn(
static_cast<int>(columns));
349 cursor_position() = 0;
354 cursor_position() =
static_cast<int>(content->size());
358 bool HandleReturn() {
360 HandleCharacter(
"\n");
362 App::PostEventOrExecute(on_enter);
366 bool HandleCharacter(
const std::string& character) {
367 if (!insert() && cursor_position() < (
int)content->size() &&
368 content()[cursor_position()] !=
'\n') {
371 content->insert(cursor_position(), character);
372 cursor_position() +=
static_cast<int>(character.size());
373 App::PostEventOrExecute(on_change);
377 bool OnEvent(Event event)
override {
378 cursor_position() =
util::clamp(cursor_position(), 0, (
int)content->size());
380 if (event == Event::Return) {
381 return HandleReturn();
383 if (event.is_character()) {
384 return HandleCharacter(event.character());
386 if (event.is_mouse()) {
387 return HandleMouse(event);
389 if (event == Event::Backspace) {
390 return HandleBackspace();
392 if (event == Event::Delete) {
393 return HandleDelete();
395 if (event == Event::ArrowLeft) {
396 return HandleArrowLeft();
398 if (event == Event::ArrowRight) {
399 return HandleArrowRight();
401 if (event == Event::ArrowUp) {
402 return HandleArrowUp();
404 if (event == Event::ArrowDown) {
405 return HandleArrowDown();
407 if (event == Event::Home) {
410 if (event == Event::End) {
413 if (event == Event::ArrowLeftCtrl) {
414 return HandleLeftCtrl();
416 if (event == Event::ArrowRightCtrl) {
417 return HandleRightCtrl();
419 if (event == Event::Insert) {
420 return HandleInsert();
425 bool HandleLeftCtrl() {
426 if (cursor_position() == 0) {
431 while (cursor_position()) {
432 const size_t previous =
GlyphPrevious(content(), cursor_position());
433 if (IsWordCharacter(content(), previous)) {
436 cursor_position() =
static_cast<int>(previous);
439 while (cursor_position()) {
440 const size_t previous =
GlyphPrevious(content(), cursor_position());
441 if (!IsWordCharacter(content(), previous)) {
444 cursor_position() =
static_cast<int>(previous);
449 bool HandleRightCtrl() {
450 if (cursor_position() == (
int)content().
size()) {
455 while (cursor_position() < (
int)content().
size()) {
457 static_cast<int>(
GlyphNext(content(), cursor_position()));
458 if (IsWordCharacter(content(), cursor_position())) {
463 while (cursor_position() < (
int)content().
size()) {
464 const size_t next =
GlyphNext(content(), cursor_position());
465 if (!IsWordCharacter(content(), cursor_position())) {
468 cursor_position() =
static_cast<int>(next);
474 bool HandleMouse(Event event) {
475 hovered_ = box_.Contain(event.mouse().x,
482 if (event.mouse().button != Mouse::Left) {
485 if (event.mouse().motion != Mouse::Pressed) {
491 if (content->empty()) {
492 cursor_position() = 0;
497 std::vector<std::string> lines = SplitLines(*content);
499 int cursor_char_index = cursor_position();
500 for (
const auto& line : lines) {
501 if (cursor_char_index <= (
int)line.size()) {
505 cursor_char_index -=
static_cast<int>(line.size() + 1);
508 const int cursor_column =
510 ?
GlyphCount(lines[cursor_line].substr(0, cursor_char_index))
511 : string_width(lines[cursor_line].substr(0, cursor_char_index));
513 int new_cursor_column = cursor_column +
event.mouse().x - cursor_box_.x_min;
514 int new_cursor_line = cursor_line + event.mouse().y - cursor_box_.y_min;
517 new_cursor_line = std::max(std::min(new_cursor_line, (
int)lines.size()), 0);
519 const std::string empty_string;
520 const std::string& line = new_cursor_line < (
int)lines.size()
521 ? lines[new_cursor_line]
527 if (new_cursor_column == cursor_column &&
528 new_cursor_line == cursor_line) {
533 cursor_position() = 0;
534 for (
int i = 0; i < new_cursor_line; ++i) {
535 cursor_position() +=
static_cast<int>(lines[i].size() + 1);
537 while (new_cursor_column > 0) {
539 new_cursor_column -= 1;
542 static_cast<int>(GlyphWidth(content(), cursor_position()));
545 static_cast<int>(
GlyphNext(content(), cursor_position()));
548 App::PostEventOrExecute(on_change);
552 bool HandleInsert() {
553 insert() = !insert();
557 bool Focusable() const final {
return true; }
559 bool hovered_ =
false;
619 option.content = std::move(content);
646 option.content = std::move(content);
647 option.placeholder = std::move(placeholder);
Component Input(StringRef content, InputOption options={})
Une boîte de saisie pour éditer du texte.
Element text(std::string_view text)
Affiche un morceau de texte unicode encodé en UTF8.
Element xflex(Element)
Développer/Réduire si possible/nécessaire sur l'axe X.
Decorator size(WidthOrHeight direction, Constraint constraint, int value)
Applique une contrainte sur la taille d'un élément.
Element vbox(Elements children)
Un conteneur affichant les éléments verticalement un par un.
constexpr const T & clamp(const T &v, const T &lo, const T &hi)
L'espace de noms FTXUI ftxui::
WordBreakProperty CodepointToWordBreakProperty(uint32_t codepoint)
size_t GlyphPrevious(std::string_view input, size_t start)
std::shared_ptr< T > Make(Args &&... args)
std::shared_ptr< Node > Element
int string_width(std::string_view input)
Element hbox(Elements children)
Un conteneur affichant les éléments horizontalement un par un.
std::vector< Element > Elements
bool EatCodePoint(std::string_view input, size_t start, size_t *end, uint32_t *ucs)
int GlyphCount(std::string_view input)
Decorator reflect(Box &box)
bool IsFullWidth(uint32_t ucs)
Element frame(Element child)
Permet à un élément d'être affiché dans une zone 'virtuelle'. Sa taille peut être plus grande que son...
std::shared_ptr< ComponentBase > Component
size_t GlyphNext(std::string_view input, size_t start)