22 _font_book(
font_book), _text(), _shaped_text(), _current_style(style)
30 for (ttlet &c : _text) {
31 r += to_string(c.grapheme.NFC());
40 cancel_partial_grapheme();
45 _text.reserve(std::ssize(gstr));
46 for (ttlet &g : gstr) {
47 _text.emplace_back(g, _current_style);
50 _selection_index = _cursor_index = 0;
60 auto text_ = make_vector(_text);
64 if (std::ssize(_text) == 0) {
67 text_.emplace_back(
grapheme::PS(), text_.back().style, 0);
70 _shaped_text =
tt::shaped_text{_font_book, text_, _width, alignment::top_left,
false};
77 void set_width(
float width)
noexcept {
82 void set_current_style(text_style style)
noexcept {
83 this->_current_style = style;
89 set_current_style(style);
90 for (
auto &c: _text) {
96 size_t size() const noexcept {
103 tt_axiom(index >= 0);
105 tt_axiom(index < std::ssize(_text));
107 return _text.begin() + index;
113 tt_axiom(index >= 0);
115 tt_axiom(index <= std::ssize(_text));
117 return _text.cbegin() + index;
120 decltype(
auto)
it(
ssize_t index)
const noexcept {
127 tt_axiom(is_valid());
129 if (_has_partial_grapheme) {
130 tt_axiom(_cursor_index != 0);
140 tt_axiom(is_valid());
147 tt_axiom(is_valid());
149 if (_selection_index < _cursor_index) {
151 }
else if (_selection_index > _cursor_index) {
154 tt_axiom(is_valid());
163 tt_axiom(is_valid());
165 if (_selection_index < _cursor_index) {
166 _text.erase(
cit(_selection_index),
cit(_cursor_index));
167 _cursor_index = _selection_index;
169 }
else if (_selection_index > _cursor_index) {
170 _text.erase(
cit(_cursor_index),
cit(_selection_index));
171 _selection_index = _cursor_index;
174 tt_axiom(is_valid());
181 void set_cursor_at_coordinate(
point2 coordinate)
noexcept
183 tt_axiom(is_valid());
185 _selection_index = _cursor_index = *new_cursor_position;
187 tt_axiom(is_valid());
190 void select_word_at_coordinate(
point2 coordinate)
noexcept
192 tt_axiom(is_valid());
198 tt_axiom(is_valid());
201 void select_paragraph_at_coordinate(point2 coordinate)
noexcept
203 tt_axiom(is_valid());
209 tt_axiom(is_valid());
212 void drag_cursor_at_coordinate(point2 coordinate)
noexcept
214 tt_axiom(is_valid());
217 _cursor_index = *new_cursor_position;
219 tt_axiom(is_valid());
222 void drag_word_at_coordinate(point2 coordinate)
noexcept
224 tt_axiom(is_valid());
229 if (_selection_index <= _cursor_index) {
230 if (a < _selection_index) {
232 _selection_index = _cursor_index;
238 if (b > _selection_index) {
240 _selection_index = _cursor_index;
247 tt_axiom(is_valid());
250 void drag_paragraph_at_coordinate(point2 coordinate)
noexcept
252 tt_axiom(is_valid());
257 if (_selection_index <= _cursor_index) {
258 if (a < _selection_index) {
260 _selection_index = _cursor_index;
266 if (b > _selection_index) {
268 _selection_index = _cursor_index;
276 tt_axiom(is_valid());
279 void cancel_partial_grapheme() noexcept {
280 tt_axiom(is_valid());
282 if (_has_partial_grapheme) {
283 tt_axiom(_cursor_index >= 1);
285 _selection_index = --_cursor_index;
287 _text.erase(
cit(_cursor_index));
288 _has_partial_grapheme =
false;
293 tt_axiom(is_valid());
302 tt_axiom(is_valid());
304 cancel_partial_grapheme();
307 _text.emplace_before(
cit(_cursor_index), character, _current_style);
308 _selection_index = ++_cursor_index;
310 _has_partial_grapheme =
true;
313 tt_axiom(is_valid());
320 tt_axiom(is_valid());
322 cancel_partial_grapheme();
326 handle_event(command::text_delete_char_next);
328 _text.emplace_before(
cit(_cursor_index), character, _current_style);
329 _selection_index = ++_cursor_index;
333 tt_axiom(is_valid());
337 tt_axiom(is_valid());
339 cancel_partial_grapheme();
342 gstring gstr = to_gstring(str);
345 str_attr.
reserve(std::ssize(gstr));
346 for (ttlet &g: gstr) {
347 str_attr.emplace_back(g, _current_style);
350 _text.insert_after(
cit(_cursor_index), str_attr.cbegin(), str_attr.cend());
351 _selection_index = _cursor_index += std::ssize(str_attr);
354 tt_axiom(is_valid());
358 tt_axiom(is_valid());
362 if (_selection_index < _cursor_index) {
363 r.
reserve(_cursor_index - _selection_index);
364 for (
auto i =
cit(_selection_index); i !=
cit(_cursor_index); ++i) {
367 }
else if (_selection_index > _cursor_index) {
368 r.reserve(_selection_index - _cursor_index);
369 for (
auto i =
cit(_cursor_index); i !=
cit(_selection_index); ++i) {
374 tt_axiom(is_valid());
379 tt_axiom(is_valid());
381 auto r = handle_copy();
382 cancel_partial_grapheme();
385 tt_axiom(is_valid());
389 bool handle_event(command command)
noexcept {
390 tt_axiom(is_valid());
392 auto handled =
false;
394 case command::text_cursor_char_left:
395 cancel_partial_grapheme();
399 _selection_index = _cursor_index = *new_cursor_position;
403 case command::text_cursor_char_right:
404 cancel_partial_grapheme();
407 _selection_index = _cursor_index = *new_cursor_position;
411 case command::text_cursor_word_left:
412 cancel_partial_grapheme();
415 _selection_index = _cursor_index = *new_cursor_position;
419 case command::text_cursor_word_right:
420 cancel_partial_grapheme();
423 _selection_index = _cursor_index = *new_cursor_position;
427 case command::text_cursor_line_end:
428 cancel_partial_grapheme();
430 _selection_index = _cursor_index = size();
433 case command::text_cursor_line_begin:
434 cancel_partial_grapheme();
436 _selection_index = _cursor_index = 0;
439 case command::text_select_char_left:
440 cancel_partial_grapheme();
443 _cursor_index = *new_cursor_position;
447 case command::text_select_char_right:
448 cancel_partial_grapheme();
451 _cursor_index = *new_cursor_position;
455 case command::text_select_word_left:
456 cancel_partial_grapheme();
459 _cursor_index = *new_cursor_position;
463 case command::text_select_word_right:
464 cancel_partial_grapheme();
467 _cursor_index = *new_cursor_position;
471 case command::text_select_word:
472 cancel_partial_grapheme();
477 case command::text_select_line_end:
478 cancel_partial_grapheme();
480 _cursor_index = size();
483 case command::text_select_line_begin:
484 cancel_partial_grapheme();
489 case command::text_select_document:
490 cancel_partial_grapheme();
492 _selection_index = 0;
493 _cursor_index = size();
496 case command::text_mode_insert:
497 cancel_partial_grapheme();
499 _insert_mode = !_insert_mode;
502 case command::text_delete_char_prev:
503 cancel_partial_grapheme();
505 if (_cursor_index != _selection_index) {
508 }
else if (_cursor_index >= 1) {
509 _selection_index = --_cursor_index;
510 _text.erase(
cit(_cursor_index));
515 case command::text_delete_char_next:
516 cancel_partial_grapheme();
518 if (_cursor_index != _selection_index) {
521 }
else if (_cursor_index < std::ssize(_text)) {
523 _text.erase(
cit(_cursor_index));
529 tt_axiom(is_valid());
533 bool is_valid() const noexcept
535 return _selection_index >= 0 && _selection_index <= std::ssize(_text) && _cursor_index >= 0 && _cursor_index <= std::ssize(_text);
539 font_book
const &_font_book;
540 gap_buffer<attributed_grapheme> _text;
550 bool _insert_mode =
true;
554 ssize_t _cursor_index = 0;
559 ssize_t _selection_index = 0;
561 text_style _current_style;
565 bool _has_partial_grapheme =
false;