FTXUI 7.0.1
C++ functional terminal UI.
Loading...
Searching...
No Matches
src/ftxui/dom/text.cpp
Go to the documentation of this file.
1// Copyright 2020 Arthur Sonzogni. All rights reserved.
2// Use of this source code is governed by the MIT license that can be found in
3// the LICENSE file.
4#include <algorithm> // for min, max
5#include <cstddef>
6#include <memory> // for make_shared
7#include <string> // for string, wstring
8#include <string_view> // for string_view
9#include <utility> // for move
10#include <vector> // for vector
11
12#include "ftxui/dom/deprecated.hpp" // for text, vtext
13#include "ftxui/dom/elements.hpp" // for Element, text, vtext
14#include "ftxui/dom/node.hpp" // for Node
15#include "ftxui/dom/requirement.hpp" // for Requirement
16#include "ftxui/dom/selection.hpp" // for Selection
17#include "ftxui/screen/box.hpp" // for Box
18#include "ftxui/screen/screen.hpp" // for Cell, Screen
19#include "ftxui/screen/string.hpp" // for string_width, Utf8ToGlyphs, to_string
20
21namespace ftxui {
22
23namespace {
24using ftxui::Screen;
25
26class Text : public Node {
27 public:
28 explicit Text(std::string_view text) : glyphs_(Utf8ToGlyphs(text)) {
29 int max_width = 0;
30 int current_width = 0;
31 int lines_count = 1;
32 lines_offsets_.push_back(0);
33
34 for (size_t i = 0; i < glyphs_.size(); ++i) {
35 if (glyphs_[i] == "\n") {
36 max_width = std::max(max_width, current_width);
37 current_width = 0;
38 lines_count++;
39 lines_offsets_.push_back((int)i + 1);
40 } else {
41 current_width++;
42 }
43 }
44 max_width = std::max(max_width, current_width);
45 lines_offsets_.push_back((int)glyphs_.size() + 1);
46
47 requirement_.min_x = max_width;
48 requirement_.min_y = lines_count;
49 }
50
51 void ComputeRequirement() override {
52 // The requirement was computed once in the constructor. This hook still
53 // runs before every frame; use it to clear the selection, which Select()
54 // re-populates while a selection is active.
55 selection_rows_.clear();
56 }
57
58 void Select(Selection& selection) override {
59 const Box selection_box = Box::Intersection(selection.GetBox(), box_);
60 if (selection_box.IsEmpty()) {
61 return;
62 }
63
64 // Only store the selected line range. Sizing per line would allocate one
65 // entry per line of the whole text on every frame.
66 const size_t lines_count = lines_offsets_.size() - 1;
67 const size_t first = selection_box.y_min - box_.y_min;
68 const size_t last =
69 std::min<size_t>(selection_box.y_max - box_.y_min + 1, lines_count);
70 if (first >= last) {
71 return;
72 }
73 selection_first_line_ = first;
74 selection_rows_.assign(last - first, {-1, -1});
75
76 for (size_t i = first; i < last; ++i) {
77 const int y = box_.y_min + (int)i;
78 const Box row_box{box_.x_min, box_.x_max, y, y};
79 const Selection row_sel = selection.SaturateHorizontal(row_box);
80 const int sel_start = row_sel.GetBox().x_min;
81 const int sel_end = row_sel.GetBox().x_max;
82 selection_rows_[i - first] = {sel_start, sel_end};
83
84 std::string part;
85 int x = box_.x_min;
86 const int start = lines_offsets_[i];
87 const int end = lines_offsets_[i + 1] - 1;
88 for (int j = start; j < end; ++j) {
89 if (sel_start <= x && x <= sel_end) {
90 part += glyphs_[j];
91 }
92 x++;
93 }
94 selection.AddPart(std::move(part), y, sel_start, sel_end);
95 }
96 }
97
98 void Render(Screen& screen) override {
99 const auto visible_box = Box::Intersection(screen.stencil, box_);
100 if (visible_box.IsEmpty()) {
101 return;
102 }
103
104 int y = visible_box.y_min;
105
106 const size_t first_line = visible_box.y_min - box_.y_min;
107 const size_t last_line = std::min<size_t>(
108 visible_box.y_max - box_.y_min + 1, lines_offsets_.size() - 1);
109
110 for (size_t line = first_line; line < last_line; ++line, ++y) {
111 int x = box_.x_min;
112
113 for (auto glyph = glyphs_.begin() + lines_offsets_[line];
114 glyph != glyphs_.end() && *glyph != "\n"; ++glyph, ++x) {
115 if (x > box_.x_max) {
116 break;
117 }
118
119 auto& cell = screen.CellAt(x, y);
120 cell.character = *glyph;
121
122 const size_t sel_index = line - selection_first_line_;
123 if (sel_index < selection_rows_.size()) {
124 const auto& [sel_start, sel_end] = selection_rows_[sel_index];
125 if (sel_start != -1 && x >= sel_start && x <= sel_end) {
126 screen.GetSelectionStyle()(cell);
127 }
128 }
129 }
130 }
131 }
132
133 private:
134 std::vector<std::string> glyphs_;
135 std::vector<int> lines_offsets_;
136 // Selection state for the line range [selection_first_line_,
137 // selection_first_line_ + selection_rows_.size()).
138 size_t selection_first_line_ = 0;
139 std::vector<std::pair<int, int>> selection_rows_;
140};
141
142class VText : public Node {
143 public:
144 explicit VText(std::string_view text) : glyphs_(Utf8ToGlyphs(text)) {
145 for (const auto& g : glyphs_) {
146 if (g != "\n") {
147 width_ = 1;
148 break;
149 }
150 }
151 }
152
153 void ComputeRequirement() override {
154 int max_height = 0;
155 int current_height = 0;
156 int columns = 1;
157
158 for (const auto& cell : glyphs_) {
159 if (cell == "\n") {
160 max_height = std::max(max_height, current_height);
161 current_height = 0;
162 columns++;
163 } else {
164 current_height++;
165 }
166 }
167 max_height = std::max(max_height, current_height);
168
169 requirement_.min_x = width_ * columns;
170 requirement_.min_y = max_height;
171 }
172
173 void Render(Screen& screen) override {
174 int x = box_.x_min;
175 int y = box_.y_min;
176 if (x + width_ - 1 > box_.x_max) {
177 return;
178 }
179 for (const auto& it : glyphs_) {
180 if (it == "\n") {
181 x += width_;
182 y = box_.y_min;
183 if (x + width_ - 1 > box_.x_max) {
184 return;
185 }
186 continue;
187 }
188 if (y > box_.y_max) {
189 continue;
190 }
191 screen.CellAt(x, y).character = it;
192 y += 1;
193 }
194 }
195
196 private:
197 std::vector<std::string> glyphs_;
198 int width_ = 0;
199};
200
201} // namespace
202
203/// @brief Display a piece of UTF8 encoded unicode text.
204/// @ingroup dom
205/// @see ftxui::to_wstring
206///
207/// ### Example
208///
209/// ```cpp
210/// Element document = text("Hello world!");
211/// ```
212///
213/// ### Output
214///
215/// ```bash
216/// Hello world!
217/// ```
218Element text(std::string_view text) {
219 return std::make_shared<Text>(std::string(text));
220}
221
222/// @brief Display a piece of unicode text.
223/// @ingroup dom
224/// @see ftxui::to_wstring
225///
226/// ### Example
227///
228/// ```cpp
229/// Element document = text(L"Hello world!");
230/// ```
231///
232/// ### Output
233///
234/// ```bash
235/// Hello world!
236/// ```
237Element text(std::wstring_view text) {
238 return ftxui::text(to_string(text));
239}
240
241/// @brief Display a piece of unicode text vertically.
242/// @ingroup dom
243/// @see ftxui::to_wstring
244///
245/// ### Example
246///
247/// ```cpp
248/// Element document = vtext("Hello world!");
249/// ```
250///
251/// ### Output
252///
253/// ```bash
254/// H
255/// e
256/// l
257/// l
258/// o
259///
260/// w
261/// o
262/// r
263/// l
264/// d
265/// !
266/// ```
267Element vtext(std::string_view text) {
268 return std::make_shared<VText>(text);
269}
270
271/// @brief Display a piece unicode text vertically.
272/// @ingroup dom
273/// @see ftxui::to_wstring
274///
275/// ### Example
276///
277/// ```cpp
278/// Element document = vtext(L"Hello world!");
279/// ```
280///
281/// ### Output
282///
283/// ```bash
284/// H
285/// e
286/// l
287/// l
288/// o
289///
290/// w
291/// o
292/// r
293/// l
294/// d
295/// !
296/// ```
297Element vtext(std::wstring_view text) { // NOLINT
298 return vtext(to_string(text));
299}
300
301} // namespace ftxui
Element text(std::string_view text)
Display a piece of UTF8 encoded unicode text.
Element vtext(std::string_view text)
Display a piece of unicode text vertically.
The FTXUI ftxui:: namespace.
Definition animation.hpp:11
std::shared_ptr< Node > Element
Definition elements.hpp:25
std::string to_string(std::wstring_view s)
Convert a std::wstring into a UTF8 std::string.
Definition string.cpp:1594
int y
Definition elements.hpp:127
void Render(Screen &screen, Node *node, Selection &selection)
Definition node.cpp:105