29std::vector<std::string> Split(
const std::string&
input) {
30 std::vector<std::string> output;
31 std::stringstream ss(
input);
33 while (std::getline(ss, line)) {
34 output.push_back(line);
36 if (
input.back() ==
'\n') {
37 output.emplace_back(
"");
42size_t GlyphWidth(
const std::string&
input,
size_t iter) {
53bool IsWordCodePoint(uint32_t codepoint) {
81bool IsWordCharacter(
const std::string&
input,
size_t iter) {
87 return IsWordCodePoint(ucs);
91class InputBase :
public ComponentBase,
public InputOption {
94 InputBase(InputOption option) : InputOption(std::move(option)) {}
99 const bool is_focused = Focused();
100 const auto focused = (!is_focused && !hovered_) ? nothing
101 : insert() ? focusCursorBarBlinking
102 : focusCursorBlockBlinking;
104 auto transform_func =
108 if (content->empty()) {
109 auto element =
text(placeholder()) | xflex |
frame;
111 return transform_func({
112 std::move(element), hovered_, is_focused,
119 const std::vector<std::string> lines = Split(*content);
121 cursor_position() =
util::clamp(cursor_position(), 0, (
int)content->size());
125 int cursor_char_index = cursor_position();
126 for (
const auto& line : lines) {
127 if (cursor_char_index <= (
int)line.size()) {
131 cursor_char_index -=
static_cast<int>(line.size() + 1);
136 elements.push_back(
text(
"") | focused);
139 elements.reserve(lines.size());
140 for (
size_t i = 0; i < lines.size(); ++i) {
141 const std::string& line = lines[i];
144 if (
int(i) != cursor_line) {
145 elements.push_back(
Text(line));
150 if (cursor_char_index >= (
int)line.size()) {
151 elements.push_back(
hbox({
160 const int glyph_start = cursor_char_index;
161 const int glyph_end =
static_cast<int>(
GlyphNext(line, glyph_start));
162 const std::string part_before_cursor = line.substr(0, glyph_start);
163 const std::string part_at_cursor =
164 line.substr(glyph_start, glyph_end - glyph_start);
165 const std::string part_after_cursor = line.substr(glyph_end);
166 auto element =
hbox({
167 Text(part_before_cursor),
168 Text(part_at_cursor) | focused |
reflect(cursor_box_),
169 Text(part_after_cursor),
172 elements.push_back(element);
175 auto element =
vbox(std::move(elements)) |
frame;
176 return transform_func({
177 std::move(element), hovered_, is_focused,
189 out.reserve(10 +
input.size() * 3 / 2);
190 for (
size_t i = 0; i <
input.size(); ++i) {
196 bool HandleBackspace() {
197 if (cursor_position() == 0) {
200 const size_t start =
GlyphPrevious(content(), cursor_position());
201 const size_t end = cursor_position();
202 content->erase(start, end - start);
203 cursor_position() =
static_cast<int>(start);
209 if (cursor_position() == (
int)content->size()) {
212 const size_t start = cursor_position();
213 const size_t end =
GlyphNext(content(), cursor_position());
214 content->erase(start, end - start);
218 bool HandleDelete() {
226 bool HandleArrowLeft() {
227 if (cursor_position() == 0) {
232 static_cast<int>(
GlyphPrevious(content(), cursor_position()));
236 bool HandleArrowRight() {
237 if (cursor_position() == (
int)content->size()) {
242 static_cast<int>(
GlyphNext(content(), cursor_position()));
246 size_t CursorColumn() {
247 size_t iter = cursor_position();
254 if (content()[iter] ==
'\n') {
257 width +=
static_cast<int>(GlyphWidth(content(), iter));
263 void MoveCursorColumn(
int columns) {
264 while (columns > 0) {
265 if (cursor_position() == (
int)content().
size() ||
266 content()[cursor_position()] ==
'\n') {
270 columns -=
static_cast<int>(GlyphWidth(content(), cursor_position()));
272 static_cast<int>(
GlyphNext(content(), cursor_position()));
276 bool HandleArrowUp() {
277 if (cursor_position() == 0) {
281 const size_t columns = CursorColumn();
285 if (cursor_position() == 0) {
288 const size_t previous =
GlyphPrevious(content(), cursor_position());
289 if (content()[previous] ==
'\n') {
292 cursor_position() =
static_cast<int>(previous);
295 static_cast<int>(
GlyphPrevious(content(), cursor_position()));
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 MoveCursorColumn(
static_cast<int>(columns));
311 bool HandleArrowDown() {
312 if (cursor_position() == (
int)content->size()) {
316 const size_t columns = CursorColumn();
320 if (content()[cursor_position()] ==
'\n') {
324 static_cast<int>(
GlyphNext(content(), cursor_position()));
325 if (cursor_position() == (
int)content().
size()) {
330 static_cast<int>(
GlyphNext(content(), cursor_position()));
332 MoveCursorColumn(
static_cast<int>(columns));
337 cursor_position() = 0;
342 cursor_position() =
static_cast<int>(content->size());
346 bool HandleReturn() {
348 HandleCharacter(
"\n");
354 bool HandleCharacter(
const std::string& character) {
355 if (!insert() && cursor_position() < (
int)content->size() &&
356 content()[cursor_position()] !=
'\n') {
359 content->insert(cursor_position(), character);
360 cursor_position() +=
static_cast<int>(character.size());
365 bool OnEvent(Event event)
override {
366 cursor_position() =
util::clamp(cursor_position(), 0, (
int)content->size());
369 return HandleReturn();
371 if (event.is_character()) {
372 return HandleCharacter(event.character());
374 if (event.is_mouse()) {
375 return HandleMouse(event);
378 return HandleBackspace();
381 return HandleDelete();
384 return HandleArrowLeft();
387 return HandleArrowRight();
390 return HandleArrowUp();
393 return HandleArrowDown();
402 return HandleLeftCtrl();
405 return HandleRightCtrl();
408 return HandleInsert();
413 bool HandleLeftCtrl() {
414 if (cursor_position() == 0) {
419 while (cursor_position()) {
420 const size_t previous =
GlyphPrevious(content(), cursor_position());
421 if (IsWordCharacter(content(), previous)) {
424 cursor_position() =
static_cast<int>(previous);
427 while (cursor_position()) {
428 const size_t previous =
GlyphPrevious(content(), cursor_position());
429 if (!IsWordCharacter(content(), previous)) {
432 cursor_position() =
static_cast<int>(previous);
437 bool HandleRightCtrl() {
438 if (cursor_position() == (
int)content().
size()) {
443 while (cursor_position() < (
int)content().
size()) {
445 static_cast<int>(
GlyphNext(content(), cursor_position()));
446 if (IsWordCharacter(content(), cursor_position())) {
451 while (cursor_position() < (
int)content().
size()) {
452 const size_t next =
GlyphNext(content(), cursor_position());
453 if (!IsWordCharacter(content(), cursor_position())) {
456 cursor_position() =
static_cast<int>(next);
462 bool HandleMouse(Event event) {
463 hovered_ = box_.Contain(event.mouse().x,
479 if (content->empty()) {
480 cursor_position() = 0;
485 std::vector<std::string> lines = Split(*content);
487 int cursor_char_index = cursor_position();
488 for (
const auto& line : lines) {
489 if (cursor_char_index <= (
int)line.size()) {
493 cursor_char_index -=
static_cast<int>(line.size() + 1);
496 const int cursor_column =
497 string_width(lines[cursor_line].substr(0, cursor_char_index));
499 int new_cursor_column = cursor_column +
event.mouse().x - cursor_box_.x_min;
500 int new_cursor_line = cursor_line +
event.mouse().y - cursor_box_.y_min;
503 new_cursor_line = std::max(std::min(new_cursor_line, (
int)lines.size()), 0);
505 const std::string empty_string;
506 const std::string& line = new_cursor_line < (int)lines.size()
507 ? lines[new_cursor_line]
511 if (new_cursor_column == cursor_column &&
512 new_cursor_line == cursor_line) {
517 cursor_position() = 0;
518 for (
int i = 0; i < new_cursor_line; ++i) {
519 cursor_position() +=
static_cast<int>(lines[i].size() + 1);
521 while (new_cursor_column > 0) {
523 static_cast<int>(GlyphWidth(content(), cursor_position()));
525 static_cast<int>(
GlyphNext(content(), cursor_position()));
532 bool HandleInsert() {
533 insert() = !insert();
537 bool Focusable() const final {
return true; }
539 bool hovered_ =
false;
599 option.
content = std::move(content);
626 option.
content = std::move(content);
一個適配器。擁有或引用一個常數字串。為方便起見,此類別將多個可變字串轉換為共享表示。
static const Event ArrowLeftCtrl
static InputOption Default()
建立預設輸入樣式:
static const Event Backspace
static const Event ArrowUp
std::function< Element(InputState)> transform
static const Event ArrowDown
StringRef placeholder
輸入框為空時的內容。
static const Event Return
static const Event ArrowLeft
static const Event Delete
static const Event Insert
static const Event ArrowRightCtrl
static const Event ArrowRight
Component Input(InputOption options={})
用於編輯文字的輸入框。
Element xflex(Element)
在 X 軸上盡可能擴展/在需要時最小化。
Decorator size(WidthOrHeight, Constraint, int value)
限制元素的大小。
Element text(std::wstring text)
顯示一段 Unicode 文字。
Element vbox(Elements)
一個垂直一個接一個顯示元素的容器。
constexpr const T & clamp(const T &v, const T &lo, const T &hi)
size_t GlyphNext(const std::string &input, size_t start)
WordBreakProperty CodepointToWordBreakProperty(uint32_t codepoint)
std::shared_ptr< T > Make(Args &&... args)
std::shared_ptr< Node > Element
int string_width(const std::string &)
Element hbox(Elements)
一個逐一水平顯示元素的容器。
std::vector< Element > Elements
bool EatCodePoint(const std::string &input, size_t start, size_t *end, uint32_t *ucs)
Decorator reflect(Box &box)
bool IsFullWidth(uint32_t ucs)
Element frame(Element)
允許元素顯示在「虛擬」區域內。其大小可以大於其容器。在這種情況下,只會顯示較小的一部分。視圖可滾動以使聚焦元素可見。
size_t GlyphPrevious(const std::string &input, size_t start)
std::shared_ptr< ComponentBase > Component