1use crate::{inputs::key::Key, util::spaces};
7use helper_enums::{CursorMove, TextBoxEditKind, TextBoxScroll, YankText};
8use helper_structs::{
9 CursorPos, TextBoxEdit, TextBoxHistory, TextBoxRenderer, TextBoxViewport, TextLineFormatter,
10};
11use ratatui::{
12 layout::Alignment,
13 style::{Modifier, Style},
14 text::Line,
15 widgets::{Block, Widget},
16};
17use std::cmp::Ordering;
18use unicode_width::UnicodeWidthChar;
19use utils::{find_word_end_forward, find_word_start_backward};
20
21pub mod helper_enums;
22pub mod helper_structs;
23pub mod utils;
24
25#[derive(Clone, Debug)]
26pub struct TextBox<'a> {
27 lines: Vec<String>,
28 block: Option<Block<'a>>,
29 style: Style,
30 cursor: (usize, usize),
31 tab_len: u8,
32 hard_tab_indent: bool,
33 history: TextBoxHistory,
34 cursor_line_style: Style,
35 line_number_style: Option<Style>,
36 pub(crate) viewport: TextBoxViewport,
37 cursor_style: Style,
38 yank: YankText,
39 alignment: Alignment,
40 pub single_line_mode: bool,
41 pub(crate) placeholder: String,
42 pub(crate) placeholder_style: Style,
43 mask: Option<char>,
44 selection_start: Option<(usize, usize)>,
45 select_style: Style,
46}
47
48impl<'a> TextBox<'a> {
49 pub fn new(mut lines: Vec<String>, single_line_mode: bool) -> Self {
50 if lines.is_empty() {
51 lines.push(String::new());
52 }
53
54 Self {
55 lines,
56 block: None,
57 style: Style::default(),
58 cursor: (0, 0),
59 tab_len: 2,
60 hard_tab_indent: false,
61 history: TextBoxHistory::new(9999),
62 cursor_line_style: Style::default(),
63 line_number_style: None,
64 viewport: TextBoxViewport::default(),
65 cursor_style: Style::default(),
66 yank: YankText::default(),
67 alignment: Alignment::Left,
68 single_line_mode,
69 placeholder: String::new(),
70 placeholder_style: Style::default(),
71 mask: None,
72 selection_start: None,
73 select_style: Style::default().add_modifier(Modifier::REVERSED),
74 }
75 }
76
77 pub fn from_list_of_strings(lines: Vec<String>, single_line_mode: bool) -> Self {
78 Self::new(lines, single_line_mode)
79 }
80
81 pub fn from_list_of_str(lines: Vec<&'a str>, single_line_mode: bool) -> Self {
82 Self::new(
83 lines.into_iter().map(|s| s.to_string()).collect(),
84 single_line_mode,
85 )
86 }
87
88 pub fn from_string_with_newline_sep(s: String, single_line_mode: bool) -> Self {
89 Self::new(
90 s.split('\n').map(|s| s.to_string()).collect(),
91 single_line_mode,
92 )
93 }
94
95 pub fn reset(&mut self) {
96 let single_line_mode = self.single_line_mode;
97 *self = Self::new(vec![String::new()], single_line_mode);
98 }
99
100 pub fn get_joined_lines(&self) -> String {
101 self.lines.join("\n")
102 }
103
104 pub fn get_num_lines(&self) -> usize {
105 self.lines.len()
106 }
107
108 pub fn set_placeholder_text(&mut self, placeholder: impl Into<String>) {
109 self.placeholder = placeholder.into();
110 }
111
112 pub fn set_mask_char(&mut self, mask: char) {
113 self.mask = Some(mask);
114 }
115
116 pub fn clear_mask_char(&mut self) {
117 self.mask = None;
118 }
119
120 pub fn disable_cursor(&mut self) {
121 self.cursor_style = Style::default();
122 }
123
124 pub fn enable_cursor(&mut self, cursor_style: Style) {
125 self.cursor_style = cursor_style;
126 }
127
128 pub fn input(&mut self, input: Key) -> bool {
130 match input {
131 Key::Ctrl('m') | Key::Char('\n' | '\r') | Key::Enter => {
132 if self.single_line_mode {
133 return false;
134 }
135 self.insert_newline();
136 true
137 }
138 Key::Char(c) => {
139 self.insert_char(c);
140 true
141 }
142 Key::Tab => {
143 if self.single_line_mode {
144 return false;
145 }
146 self.insert_tab()
147 }
148 Key::Ctrl('h') | Key::Backspace => self.delete_char(),
149 Key::Ctrl('d') | Key::Delete => self.delete_next_char(),
150 Key::Ctrl('k') => self.delete_line_by_end(),
151 Key::Ctrl('j') => self.delete_line_by_head(),
152 Key::Ctrl('w') | Key::Alt('h') | Key::AltBackspace => self.delete_word(),
153 Key::AltDelete | Key::Alt('d') => self.delete_next_word(),
154 Key::Ctrl('n') | Key::Down => {
155 if self.single_line_mode {
156 return false;
157 }
158 self.move_cursor(CursorMove::Down);
159 false
160 }
161 Key::ShiftDown => {
162 if self.single_line_mode {
163 return false;
164 }
165 self.move_cursor_with_shift(CursorMove::Down, true);
166 false
167 }
168 Key::Ctrl('p') | Key::Up => {
169 if self.single_line_mode {
170 return false;
171 }
172 self.move_cursor(CursorMove::Up);
173 false
174 }
175 Key::ShiftUp => {
176 if self.single_line_mode {
177 return false;
178 }
179 self.move_cursor_with_shift(CursorMove::Up, true);
180 false
181 }
182 Key::Ctrl('f') | Key::Right => {
183 self.move_cursor(CursorMove::Forward);
184 false
185 }
186 Key::ShiftRight => {
187 self.move_cursor_with_shift(CursorMove::Forward, true);
188 false
189 }
190 Key::Ctrl('b') | Key::Left => {
191 self.move_cursor(CursorMove::Back);
192 false
193 }
194 Key::Ctrl('a') => {
195 self.select_all();
196 false
197 }
198 Key::ShiftLeft => {
199 self.move_cursor_with_shift(CursorMove::Back, true);
200 false
201 }
202 Key::Home | Key::CtrlAlt('b') | Key::CtrlAltLeft => {
203 self.move_cursor(CursorMove::Head);
204 false
205 }
206 Key::ShiftHome | Key::CtrlAltShift('b') | Key::CtrlAltShiftLeft => {
207 self.move_cursor_with_shift(CursorMove::Head, true);
208 false
209 }
210 Key::Ctrl('e') | Key::End | Key::CtrlAltRight | Key::CtrlAlt('f') => {
211 self.move_cursor(CursorMove::End);
212 false
213 }
214 Key::CtrlShift('e')
215 | Key::ShiftEnd
216 | Key::CtrlAltShiftRight
217 | Key::CtrlAltShift('f') => {
218 self.move_cursor_with_shift(CursorMove::End, true);
219 false
220 }
221 Key::Alt('<') | Key::CtrlAltUp | Key::CtrlAlt('p') => {
222 if self.single_line_mode {
223 return false;
224 }
225 self.move_cursor(CursorMove::Top);
226 false
227 }
228 Key::AltShift('<') | Key::CtrlAltShiftUp | Key::CtrlAltShift('p') => {
229 if self.single_line_mode {
230 return false;
231 }
232 self.move_cursor_with_shift(CursorMove::Top, true);
233 false
234 }
235 Key::Alt('>') | Key::CtrlAltDown | Key::CtrlAlt('n') => {
236 if self.single_line_mode {
237 return false;
238 }
239 self.move_cursor(CursorMove::Bottom);
240 false
241 }
242 Key::AltShift('>') | Key::CtrlAltShiftDown | Key::CtrlAltShift('n') => {
243 if self.single_line_mode {
244 return false;
245 }
246 self.move_cursor_with_shift(CursorMove::Bottom, true);
247 false
248 }
249 Key::Alt('f') | Key::CtrlRight => {
250 self.move_cursor(CursorMove::WordForward);
251 false
252 }
253 Key::AltShift('f') | Key::CtrlShiftRight => {
254 self.move_cursor_with_shift(CursorMove::WordForward, true);
255 false
256 }
257 Key::Alt('b') | Key::CtrlLeft => {
258 self.move_cursor(CursorMove::WordBack);
259 false
260 }
261 Key::AltShift('b') | Key::CtrlShiftLeft => {
262 self.move_cursor_with_shift(CursorMove::WordBack, true);
263 false
264 }
265 Key::Alt(']') | Key::Alt('n') | Key::CtrlDown => {
266 self.move_cursor(CursorMove::ParagraphForward);
267 false
268 }
269 Key::AltShift(']') | Key::AltShift('n') | Key::CtrlShiftDown => {
270 self.move_cursor_with_shift(CursorMove::ParagraphForward, true);
271 false
272 }
273 Key::Alt('[') | Key::Alt('p') | Key::CtrlUp => {
274 self.move_cursor(CursorMove::ParagraphBack);
275 false
276 }
277 Key::AltShift('[') | Key::AltShift('p') | Key::CtrlShiftUp => {
278 self.move_cursor_with_shift(CursorMove::ParagraphBack, true);
279 false
280 }
281 Key::Ctrl('z') => self.undo(),
282 Key::Ctrl('y') => self.redo(),
283 Key::Ctrl('c') => {
284 self.copy();
285 false
286 }
287 Key::Ctrl('x') => self.cut(),
288 Key::Ctrl('v') => self.paste(),
289 Key::PageDown => {
290 if self.single_line_mode {
291 return false;
292 }
293 self.scroll(TextBoxScroll::PageDown);
294 false
295 }
296 Key::ShiftPageDown => {
297 if self.single_line_mode {
298 return false;
299 }
300 self.scroll_with_shift(TextBoxScroll::PageDown, true);
301 false
302 }
303 Key::PageUp => {
304 if self.single_line_mode {
305 return false;
306 }
307 self.scroll(TextBoxScroll::PageUp);
308 false
309 }
310 Key::ShiftPageUp => {
311 if self.single_line_mode {
312 return false;
313 }
314 self.scroll_with_shift(TextBoxScroll::PageUp, true);
315 false
316 }
317 _ => false,
318 }
319 }
320
321 pub fn input_without_shortcuts(&mut self, input: Key) -> bool {
322 match input {
323 Key::Char(c) => {
324 self.insert_char(c);
325 true
326 }
327 Key::Tab => self.insert_tab(),
328 Key::Backspace => self.delete_char(),
329 Key::Delete => self.delete_next_char(),
330 Key::Enter => {
331 self.insert_newline();
332 true
333 }
334 _ => false,
335 }
336 }
337
338 pub fn set_selection_style(&mut self, style: Style) {
339 self.select_style = style;
340 }
341
342 fn line_offset(&self, row: usize, col: usize) -> usize {
343 let line = self
344 .lines
345 .get(row)
346 .unwrap_or(&self.lines[self.lines.len() - 1]);
347 line.char_indices()
348 .nth(col)
349 .map(|(i, _)| i)
350 .unwrap_or(line.len())
351 }
352
353 pub fn copy(&mut self) {
354 if let Some((start, end)) = self.take_selection_range() {
355 if start.row == end.row {
356 self.yank = self.lines[start.row][start.offset..end.offset]
357 .to_string()
358 .into();
359 return;
360 }
361 let mut chunk = vec![self.lines[start.row][start.offset..].to_string()];
362 chunk.extend(self.lines[start.row + 1..end.row].iter().cloned());
363 chunk.push(self.lines[end.row][..end.offset].to_string());
364 self.yank = YankText::Chunk(chunk);
365 }
366 }
367
368 pub fn cut(&mut self) -> bool {
369 self.delete_selection(true)
370 }
371
372 pub fn paste(&mut self) -> bool {
373 self.delete_selection(false);
374 match self.yank.clone() {
375 YankText::Piece(s) => self.insert_piece(s),
376 YankText::Chunk(c) => self.insert_chunk(c),
377 }
378 }
379
380 fn selection_range(&self) -> Option<(CursorPos, CursorPos)> {
381 let (sr, sc) = self.selection_start?;
382 let (er, ec) = self.cursor;
383 let (so, eo) = (self.line_offset(sr, sc), self.line_offset(er, ec));
384 let s = CursorPos::new(sr, sc, so);
385 let e = CursorPos::new(er, ec, eo);
386 match (sr, so).cmp(&(er, eo)) {
387 Ordering::Less => Some((s, e)),
388 Ordering::Equal => None,
389 Ordering::Greater => Some((e, s)),
390 }
391 }
392
393 fn take_selection_range(&mut self) -> Option<(CursorPos, CursorPos)> {
394 let range = self.selection_range();
395 self.cancel_selection();
396 range
397 }
398
399 fn delete_range(&mut self, start: CursorPos, end: CursorPos, should_yank: bool) {
400 self.cursor = (start.row, start.col);
401
402 if start.row == end.row {
403 let removed = self.lines[start.row]
404 .drain(start.offset..end.offset)
405 .as_str()
406 .to_string();
407 if should_yank {
408 self.yank = removed.clone().into();
409 }
410 self.push_history(TextBoxEditKind::DeleteStr(removed), end, start.offset);
411 return;
412 }
413
414 let mut deleted = vec![self.lines[start.row]
415 .drain(start.offset..)
416 .as_str()
417 .to_string()];
418 deleted.extend(self.lines.drain(start.row + 1..end.row));
419 if start.row + 1 < self.lines.len() {
420 let mut last_line = self.lines.remove(start.row + 1);
421 self.lines[start.row].push_str(&last_line[end.offset..]);
422 last_line.truncate(end.offset);
423 deleted.push(last_line);
424 }
425
426 if should_yank {
427 self.yank = YankText::Chunk(deleted.clone());
428 }
429
430 let edit = if deleted.len() == 1 {
431 TextBoxEditKind::DeleteStr(deleted.remove(0))
432 } else {
433 TextBoxEditKind::DeleteChunk(deleted)
434 };
435 self.push_history(edit, end, start.offset);
436 }
437
438 fn delete_selection(&mut self, should_yank: bool) -> bool {
439 if let Some((s, e)) = self.take_selection_range() {
440 self.delete_range(s, e, should_yank);
441 return true;
442 }
443 false
444 }
445
446 fn push_history(&mut self, kind: TextBoxEditKind, before: CursorPos, after_offset: usize) {
447 let (row, col) = self.cursor;
448 let after = CursorPos::new(row, col, after_offset);
449 let edit = TextBoxEdit::new(kind, before, after);
450 self.history.push(edit);
451 }
452
453 pub fn insert_char(&mut self, c: char) {
454 if c == '\n' || c == '\r' {
455 self.insert_newline();
456 return;
457 }
458
459 self.delete_selection(false);
460 let (row, col) = self.cursor;
461 let line = &mut self.lines[row];
462 let i = line
463 .char_indices()
464 .nth(col)
465 .map(|(i, _)| i)
466 .unwrap_or(line.len());
467 line.insert(i, c);
468 self.cursor.1 += 1;
469 self.push_history(
470 TextBoxEditKind::InsertChar(c),
471 CursorPos::new(row, col, i),
472 i + c.len_utf8(),
473 );
474 }
475
476 pub fn insert_str<S: AsRef<str>>(&mut self, s: S) -> bool {
477 let modified = self.delete_selection(false);
478 let mut lines: Vec<_> = s
479 .as_ref()
480 .split('\n')
481 .map(|s| s.strip_suffix('\r').unwrap_or(s).to_string())
482 .collect();
483 match lines.len() {
484 0 => modified,
485 1 => self.insert_piece(lines.remove(0)),
486 _ => self.insert_chunk(lines),
487 }
488 }
489
490 fn insert_chunk(&mut self, chunk: Vec<String>) -> bool {
491 debug_assert!(chunk.len() > 1, "Chunk size must be > 1: {:?}", chunk);
492
493 let (row, col) = self.cursor;
494 let line = &mut self.lines[row];
495 let i = line
496 .char_indices()
497 .nth(col)
498 .map(|(i, _)| i)
499 .unwrap_or(line.len());
500 let before = CursorPos::new(row, col, i);
501
502 let (row, col) = (
503 row + chunk.len() - 1,
504 chunk[chunk.len() - 1].chars().count(),
505 );
506 self.cursor = (row, col);
507
508 let end_offset = chunk.last().unwrap().len();
509
510 let edit = TextBoxEditKind::InsertChunk(chunk);
511 edit.apply(
512 &mut self.lines,
513 &before,
514 &CursorPos::new(row, col, end_offset),
515 );
516
517 self.push_history(edit, before, end_offset);
518 true
519 }
520
521 fn insert_piece(&mut self, s: String) -> bool {
522 if s.is_empty() {
523 return false;
524 }
525
526 let (row, col) = self.cursor;
527 let line = &mut self.lines[row];
528 debug_assert!(
529 !s.contains('\n'),
530 "string given to TextArea::insert_piece must not contain newline: {:?}",
531 line,
532 );
533
534 let i = line
535 .char_indices()
536 .nth(col)
537 .map(|(i, _)| i)
538 .unwrap_or(line.len());
539 line.insert_str(i, &s);
540 let end_offset = i + s.len();
541
542 self.cursor.1 += s.chars().count();
543 self.push_history(
544 TextBoxEditKind::InsertStr(s),
545 CursorPos::new(row, col, i),
546 end_offset,
547 );
548 true
549 }
550
551 pub fn delete_str(&mut self, chars: usize) -> bool {
552 if self.delete_selection(false) {
553 return true;
554 }
555 if chars == 0 {
556 return false;
557 }
558
559 let (start_row, start_col) = self.cursor;
560
561 let mut remaining = chars;
562 let mut find_end = move |line: &str| {
563 let mut col = 0usize;
564 for (i, _) in line.char_indices() {
565 if remaining == 0 {
566 return Some((i, col));
567 }
568 col += 1;
569 remaining -= 1;
570 }
571 if remaining == 0 {
572 Some((line.len(), col))
573 } else {
574 remaining -= 1;
575 None
576 }
577 };
578
579 let line = &self.lines[start_row];
580 let start_offset = {
581 line.char_indices()
582 .nth(start_col)
583 .map(|(i, _)| i)
584 .unwrap_or(line.len())
585 };
586
587 if let Some((offset_delta, col_delta)) = find_end(&line[start_offset..]) {
589 let end_offset = start_offset + offset_delta;
590 let end_col = start_col + col_delta;
591 let removed = self.lines[start_row]
592 .drain(start_offset..end_offset)
593 .as_str()
594 .to_string();
595 self.yank = removed.clone().into();
596 self.push_history(
597 TextBoxEditKind::DeleteStr(removed),
598 CursorPos::new(start_row, end_col, end_offset),
599 start_offset,
600 );
601 return true;
602 }
603
604 let mut r = start_row + 1;
605 let mut offset = 0;
606 let mut col = 0;
607
608 while r < self.lines.len() {
609 let line = &self.lines[r];
610 if let Some((o, c)) = find_end(line) {
611 offset = o;
612 col = c;
613 break;
614 }
615 r += 1;
616 }
617
618 let start = CursorPos::new(start_row, start_col, start_offset);
619 let end = CursorPos::new(r, col, offset);
620 self.delete_range(start, end, true);
621 true
622 }
623
624 fn delete_piece(&mut self, col: usize, chars: usize) -> bool {
625 if chars == 0 {
626 return false;
627 }
628
629 #[inline]
630 fn bytes_and_chars(claimed: usize, s: &str) -> (usize, usize) {
631 let mut last_col = 0;
633 for (col, (bytes, _)) in s.char_indices().enumerate() {
634 if col == claimed {
635 return (bytes, claimed);
636 }
637 last_col = col;
638 }
639 (s.len(), last_col + 1)
640 }
641
642 let (row, _) = self.cursor;
643 let line = &mut self.lines[row];
644 if let Some((i, _)) = line.char_indices().nth(col) {
645 let (bytes, chars) = bytes_and_chars(chars, &line[i..]);
646 let removed = line.drain(i..i + bytes).as_str().to_string();
647
648 self.cursor = (row, col);
649 self.push_history(
650 TextBoxEditKind::DeleteStr(removed.clone()),
651 CursorPos::new(row, col + chars, i + bytes),
652 i,
653 );
654 self.yank = removed.into();
655 true
656 } else {
657 false
658 }
659 }
660
661 pub fn insert_tab(&mut self) -> bool {
662 let modified = self.delete_selection(false);
663 if self.tab_len == 0 {
664 return modified;
665 }
666
667 if self.hard_tab_indent {
668 self.insert_char('\t');
669 return true;
670 }
671
672 let (row, col) = self.cursor;
673 let width: usize = self.lines[row]
674 .chars()
675 .take(col)
676 .map(|c| c.width().unwrap_or(0))
677 .sum();
678 let len = self.tab_len - (width % self.tab_len as usize) as u8;
679 self.insert_piece(spaces(len).to_string())
680 }
681
682 pub fn insert_newline(&mut self) {
683 self.delete_selection(false);
684
685 let (row, col) = self.cursor;
686 let line = &mut self.lines[row];
687 let offset = line
688 .char_indices()
689 .nth(col)
690 .map(|(i, _)| i)
691 .unwrap_or(line.len());
692 let next_line = line[offset..].to_string();
693 line.truncate(offset);
694
695 self.lines.insert(row + 1, next_line);
696 self.cursor = (row + 1, 0);
697 self.push_history(
698 TextBoxEditKind::InsertNewline,
699 CursorPos::new(row, col, offset),
700 0,
701 );
702 }
703
704 pub fn delete_newline(&mut self) -> bool {
705 if self.delete_selection(false) {
706 return true;
707 }
708
709 let (row, _) = self.cursor;
710 if row == 0 {
711 return false;
712 }
713
714 let line = self.lines.remove(row);
715 let prev_line = &mut self.lines[row - 1];
716 let prev_line_end = prev_line.len();
717
718 self.cursor = (row - 1, prev_line.chars().count());
719 prev_line.push_str(&line);
720 self.push_history(
721 TextBoxEditKind::DeleteNewline,
722 CursorPos::new(row, 0, 0),
723 prev_line_end,
724 );
725 true
726 }
727
728 pub fn delete_char(&mut self) -> bool {
729 if self.delete_selection(false) {
730 return true;
731 }
732
733 let (row, col) = self.cursor;
734 if col == 0 {
735 return self.delete_newline();
736 }
737
738 let line = &mut self.lines[row];
739 if let Some((offset, c)) = line.char_indices().nth(col - 1) {
740 line.remove(offset);
741 self.cursor.1 -= 1;
742 self.push_history(
743 TextBoxEditKind::DeleteChar(c),
744 CursorPos::new(row, col, offset + c.len_utf8()),
745 offset,
746 );
747 true
748 } else {
749 false
750 }
751 }
752
753 pub fn delete_next_char(&mut self) -> bool {
754 if self.delete_selection(false) {
755 return true;
756 }
757
758 let before = self.cursor;
759 self.move_cursor_with_shift(CursorMove::Forward, false);
760 if before == self.cursor {
761 return false; }
763
764 self.delete_char()
765 }
766
767 pub fn delete_line_by_end(&mut self) -> bool {
768 if self.delete_selection(false) {
769 return true;
770 }
771 if self.delete_piece(self.cursor.1, usize::MAX) {
772 return true;
773 }
774 self.delete_next_char() }
776
777 pub fn delete_line_by_head(&mut self) -> bool {
778 if self.delete_selection(false) {
779 return true;
780 }
781 if self.delete_piece(0, self.cursor.1) {
782 return true;
783 }
784 self.delete_newline()
785 }
786
787 pub fn delete_word(&mut self) -> bool {
788 if self.delete_selection(false) {
789 return true;
790 }
791 let (r, c) = self.cursor;
792 if let Some(col) = find_word_start_backward(&self.lines[r], c) {
793 self.delete_piece(col, c - col)
794 } else if c > 0 {
795 self.delete_piece(0, c)
796 } else {
797 self.delete_newline()
798 }
799 }
800
801 pub fn delete_next_word(&mut self) -> bool {
802 if self.delete_selection(false) {
803 return true;
804 }
805 let (r, c) = self.cursor;
806 let line = &self.lines[r];
807 if let Some(col) = find_word_end_forward(line, c) {
808 self.delete_piece(c, col - c)
809 } else {
810 let end_col = line.chars().count();
811 if c < end_col {
812 self.delete_piece(c, end_col - c)
813 } else if r + 1 < self.lines.len() {
814 self.cursor = (r + 1, 0);
815 self.delete_newline()
816 } else {
817 false
818 }
819 }
820 }
821
822 pub fn select_all(&mut self) {
823 self.move_cursor(CursorMove::Jump(u16::MAX, u16::MAX));
824 self.selection_start = Some((0, 0));
825 }
826
827 pub fn move_cursor(&mut self, m: CursorMove) {
828 self.move_cursor_with_shift(m, self.selection_start.is_some());
829 }
830
831 pub fn undo(&mut self) -> bool {
832 if let Some(cursor) = self.history.undo(&mut self.lines) {
833 self.cancel_selection();
834 self.cursor = cursor;
835 true
836 } else {
837 false
838 }
839 }
840
841 pub fn redo(&mut self) -> bool {
842 if let Some(cursor) = self.history.redo(&mut self.lines) {
843 self.cancel_selection();
844 self.cursor = cursor;
845 true
846 } else {
847 false
848 }
849 }
850
851 pub(crate) fn get_formatted_line<'b>(
852 &'b self,
853 line: &'b str,
854 row: usize,
855 line_num_len: u8,
856 ) -> Line<'b> {
857 let mut hl = TextLineFormatter::new(
858 line,
859 self.cursor_style,
860 self.tab_len,
861 self.mask,
862 self.select_style,
863 );
864
865 if let Some(style) = self.line_number_style {
866 hl.line_number(row, line_num_len, style);
867 }
868
869 if row == self.cursor.0 {
870 hl.cursor_line(self.cursor.1, self.cursor_line_style);
871 }
872
873 if let Some((start, end)) = self.selection_range() {
874 hl.selection(row, start.row, start.offset, end.row, end.offset);
875 }
876
877 hl.into_line()
878 }
879
880 pub fn widget(&'a self) -> impl Widget + 'a {
881 TextBoxRenderer::new(self)
882 }
883
884 pub fn style(&self) -> Style {
885 self.style
886 }
887
888 pub fn set_block(&mut self, block: Block<'a>) {
889 self.block = Some(block);
890 }
891
892 pub fn block<'s>(&'s self) -> Option<&'s Block<'a>> {
893 self.block.as_ref()
894 }
895
896 pub fn set_line_number_style(&mut self, style: Style) {
897 self.line_number_style = Some(style);
898 }
899
900 pub fn remove_line_number(&mut self) {
901 self.line_number_style = None;
902 }
903
904 pub fn lines(&'a self) -> &'a [String] {
905 &self.lines
906 }
907
908 pub fn cursor(&self) -> (usize, usize) {
909 self.cursor
910 }
911
912 pub fn set_alignment(&mut self, alignment: Alignment) {
913 if let Alignment::Center | Alignment::Right = alignment {
914 self.line_number_style = None;
915 }
916 self.alignment = alignment;
917 }
918
919 pub fn alignment(&self) -> Alignment {
920 self.alignment
921 }
922
923 pub fn is_empty(&self) -> bool {
924 self.lines == [""]
925 }
926
927 pub fn scroll(&mut self, scrolling: impl Into<TextBoxScroll>) {
928 self.scroll_with_shift(scrolling.into(), self.selection_start.is_some());
929 }
930
931 fn scroll_with_shift(&mut self, scrolling: TextBoxScroll, shift: bool) {
932 if shift && self.selection_start.is_none() {
933 self.selection_start = Some(self.cursor);
934 }
935 scrolling.scroll(&mut self.viewport);
936 self.move_cursor_with_shift(CursorMove::InViewport, shift);
937 }
938
939 pub fn start_selection(&mut self) {
940 self.selection_start = Some(self.cursor);
941 }
942
943 pub fn cancel_selection(&mut self) {
944 self.selection_start = None;
945 }
946
947 fn move_cursor_with_shift(&mut self, m: CursorMove, shift: bool) {
948 if let Some(cursor) = m.next_cursor(self.cursor, &self.lines, &self.viewport) {
949 if shift {
950 if self.selection_start.is_none() {
951 self.start_selection();
952 }
953 } else {
954 self.cancel_selection();
955 }
956 self.cursor = cursor;
957 } else {
958 log::debug!("Cursor move failed: {:?}", m);
959 }
960 }
961
962 pub fn get_non_ascii_aware_cursor_x_pos(&self) -> usize {
963 let (row, col) = self.cursor;
964 let line = &self.lines[row];
965 let mut raw_length = 0;
966 for c in line.chars().take(col) {
967 raw_length += c.width().unwrap_or_default();
968 }
969 raw_length
970 }
971}