1use crate::_private::NonExhaustive;
7use crate::clipboard::{Clipboard, global_clipboard};
8use crate::event::{ReadOnly, TextOutcome};
9#[allow(deprecated)]
10use crate::glyph::Glyph;
11use crate::glyph2::{GlyphIter2, TextWrap2};
12use crate::grapheme::Grapheme;
13use crate::text_core::TextCore;
14use crate::text_store::TextStore;
15use crate::text_store::text_rope::TextRope;
16use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
17use crate::{
18 Cursor, HasScreenCursor, TextError, TextPosition, TextRange, TextStyle, ipos_type, upos_type,
19};
20use crossterm::event::KeyModifiers;
21use rat_event::util::MouseFlags;
22use rat_event::{HandleEvent, MouseOnly, Outcome, Regular, ct_event, flow};
23use rat_focus::event::FocusTraversal;
24use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
25use rat_reloc::{RelocatableState, relocate_area, relocate_dark_offset, relocate_pos_tuple};
26use rat_scrolled::event::ScrollOutcome;
27use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState};
28use ratatui::buffer::Buffer;
29use ratatui::layout::{Rect, Size};
30use ratatui::style::{Style, Stylize};
31use ratatui::widgets::{Block, StatefulWidget};
32use ropey::Rope;
33use std::borrow::Cow;
34use std::cmp::{max, min};
35use std::collections::HashMap;
36use std::ops::Range;
37
38#[derive(Debug, Default, Clone)]
78pub struct TextArea<'a> {
79 block: Option<Block<'a>>,
80 hscroll: Option<Scroll<'a>>,
81 h_max_offset: Option<usize>,
82 h_overscroll: Option<usize>,
83 vscroll: Option<Scroll<'a>>,
84
85 style: Style,
86 focus_style: Option<Style>,
87 select_style: Option<Style>,
88 text_style: HashMap<usize, Style>,
89}
90
91#[derive(Debug)]
93pub struct TextAreaState {
94 pub area: Rect,
97 pub inner: Rect,
100 pub rendered: Size,
104 pub screen_cursor: Option<(u16, u16)>,
106
107 pub hscroll: ScrollState,
111 pub vscroll: ScrollState,
114 pub sub_row_offset: upos_type,
119 pub dark_offset: (u16, u16),
122 pub scroll_to_cursor: bool,
128
129 pub value: TextCore<TextRope>,
131
132 pub move_col: Option<i16>,
140 pub auto_indent: bool,
142 pub auto_quote: bool,
144 pub text_wrap: TextWrap,
146
147 pub focus: FocusFlag,
149
150 pub mouse: MouseFlags,
153
154 pub non_exhaustive: NonExhaustive,
155}
156
157impl Clone for TextAreaState {
158 fn clone(&self) -> Self {
159 Self {
160 area: self.area,
161 inner: self.inner,
162 rendered: self.rendered,
163 screen_cursor: self.screen_cursor,
164 hscroll: self.hscroll.clone(),
165 vscroll: self.vscroll.clone(),
166 sub_row_offset: self.sub_row_offset,
167 dark_offset: self.dark_offset,
168 scroll_to_cursor: self.scroll_to_cursor,
169 value: self.value.clone(),
170 move_col: None,
171 auto_indent: self.auto_indent,
172 auto_quote: self.auto_quote,
173 text_wrap: self.text_wrap,
174 focus: FocusFlag::named(self.focus.name()),
175 mouse: Default::default(),
176 non_exhaustive: NonExhaustive,
177 }
178 }
179}
180
181#[derive(Debug, Clone, Copy)]
183#[non_exhaustive]
184pub enum TextWrap {
185 Shift,
187 Hard,
189 Word(u16),
199}
200
201impl<'a> TextArea<'a> {
202 pub fn new() -> Self {
204 Self::default()
205 }
206
207 #[inline]
209 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
210 if let Some(styles) = styles {
211 self.styles(styles)
212 } else {
213 self
214 }
215 }
216
217 #[inline]
219 pub fn styles(mut self, styles: TextStyle) -> Self {
220 self.style = styles.style;
221 if styles.focus.is_some() {
222 self.focus_style = styles.focus;
223 }
224 if styles.select.is_some() {
225 self.select_style = styles.select;
226 }
227 if let Some(border_style) = styles.border_style {
228 self.block = self.block.map(|v| v.border_style(border_style));
229 }
230 self.block = self.block.map(|v| v.style(self.style));
231 if styles.block.is_some() {
232 self.block = styles.block;
233 }
234 if let Some(styles) = styles.scroll {
235 self.hscroll = self.hscroll.map(|v| v.styles(styles.clone()));
236 self.vscroll = self.vscroll.map(|v| v.styles(styles));
237 }
238 self
239 }
240
241 pub fn style(mut self, style: Style) -> Self {
243 self.style = style;
244 self
245 }
246
247 pub fn focus_style(mut self, style: Style) -> Self {
249 self.focus_style = Some(style);
250 self.block = self.block.map(|v| v.style(self.style));
251 self
252 }
253
254 pub fn select_style(mut self, style: Style) -> Self {
256 self.select_style = Some(style);
257 self
258 }
259
260 pub fn text_style_idx(mut self, idx: usize, style: Style) -> Self {
265 self.text_style.insert(idx, style);
266 self
267 }
268
269 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
274 for (i, s) in styles.into_iter().enumerate() {
275 self.text_style.insert(i, s);
276 }
277 self
278 }
279
280 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
285 for (i, s) in styles.into_iter() {
286 self.text_style.insert(i, s.into());
287 }
288 self
289 }
290
291 #[inline]
293 pub fn block(mut self, block: Block<'a>) -> Self {
294 self.block = Some(block);
295 self
296 }
297
298 pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
300 self.hscroll = Some(scroll.clone().override_horizontal());
301 self.vscroll = Some(scroll.override_vertical());
302 self
303 }
304
305 pub fn hscroll(mut self, scroll: Scroll<'a>) -> Self {
307 self.hscroll = Some(scroll.override_horizontal());
308 self
309 }
310
311 pub fn set_horizontal_max_offset(mut self, offset: usize) -> Self {
328 self.h_max_offset = Some(offset);
329 self
330 }
331
332 pub fn set_horizontal_overscroll(mut self, overscroll: usize) -> Self {
339 self.h_overscroll = Some(overscroll);
340 self
341 }
342
343 pub fn vscroll(mut self, scroll: Scroll<'a>) -> Self {
345 self.vscroll = Some(scroll.override_vertical());
346 self
347 }
348}
349
350impl<'a> StatefulWidget for &TextArea<'a> {
351 type State = TextAreaState;
352
353 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
354 render_text_area(self, area, buf, state);
355 }
356}
357
358impl StatefulWidget for TextArea<'_> {
359 type State = TextAreaState;
360
361 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
362 render_text_area(&self, area, buf, state);
363 }
364}
365
366fn render_text_area(
367 widget: &TextArea<'_>,
368 area: Rect,
369 buf: &mut Buffer,
370 state: &mut TextAreaState,
371) {
372 state.area = area;
373 state.screen_cursor = None;
374
375 let style = widget.style;
376 let select_style = if let Some(select_style) = widget.select_style {
377 if select_style.fg.is_none() && select_style.bg.is_none() {
379 select_style
380 } else {
381 style.patch(select_style)
382 }
383 } else {
384 Style::default().black().on_yellow()
385 };
386
387 state.area = area;
389 state.screen_cursor = None;
390 state.inner = ScrollArea::new()
391 .block(widget.block.as_ref())
392 .h_scroll(widget.hscroll.as_ref())
393 .v_scroll(widget.vscroll.as_ref())
394 .inner(area, Some(&state.hscroll), Some(&state.vscroll));
395 state.rendered = state.inner.as_size();
396
397 if let Some(h_max_offset) = widget.h_max_offset {
398 state.hscroll.set_max_offset(h_max_offset);
399 }
400 if let Some(h_overscroll) = widget.h_overscroll {
401 state.hscroll.set_overscroll_by(Some(h_overscroll));
402 }
403 state.hscroll.set_page_len(state.inner.width as usize);
404 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
405 state
406 .vscroll
407 .set_max_offset(state.len_lines().saturating_sub(1) as usize);
408 } else {
409 state.vscroll.set_max_offset(
410 state
411 .len_lines()
412 .saturating_sub(state.inner.height as upos_type) as usize,
413 );
414 }
415 state.vscroll.set_page_len(state.inner.height as usize);
416
417 if state.scroll_to_cursor {
418 state.scroll_to_cursor();
419 }
420
421 ScrollArea::new()
423 .block(widget.block.as_ref())
424 .h_scroll(widget.hscroll.as_ref())
425 .v_scroll(widget.vscroll.as_ref())
426 .style(style)
427 .render(
428 area,
429 buf,
430 &mut ScrollAreaState::new()
431 .h_scroll(&mut state.hscroll)
432 .v_scroll(&mut state.vscroll),
433 );
434
435 if state.inner.width == 0 || state.inner.height == 0 {
436 return;
438 }
439 if state.vscroll.offset() > state.value.len_lines() as usize {
440 return;
442 }
443
444 let (shift_left, sub_row_offset, start_row) = state.clean_offset();
445 let page_rows = start_row
446 ..min(
447 start_row + state.inner.height as upos_type,
448 state.value.len_lines(),
449 );
450 let page_bytes = state
451 .try_bytes_at_range(TextRange::new(
452 (sub_row_offset, page_rows.start),
453 (0, page_rows.end),
454 ))
455 .expect("valid_rows");
456 let mut screen_cursor = None;
457 let selection = state.selection();
458 let mut styles = Vec::new();
459
460 for g in state
461 .glyphs2(shift_left, sub_row_offset, page_rows)
462 .expect("valid_offset")
463 {
464 let screen_pos = g.screen_pos();
466
467 if screen_pos.1 >= state.inner.height {
468 break;
469 }
470
471 if g.pos() == state.cursor() && !g.soft_break() {
473 screen_cursor = Some(g.screen_pos());
474 }
475
476 if g.screen_width() > 0 {
477 let mut style = style;
478 state
480 .value
481 .styles_at_page(g.text_bytes().start, page_bytes.clone(), &mut styles);
482 for style_nr in &styles {
483 if let Some(s) = widget.text_style.get(style_nr) {
484 style = style.patch(*s);
485 }
486 }
487 if selection.contains_pos(g.pos()) {
489 style = style.patch(select_style);
490 };
491
492 if let Some(cell) =
494 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
495 {
496 cell.set_symbol(g.glyph());
497 cell.set_style(style);
498 }
499 for d in 1..g.screen_width() {
501 if let Some(cell) = buf.cell_mut((
502 state.inner.x + screen_pos.0 + d,
503 state.inner.y + screen_pos.1,
504 )) {
505 cell.reset();
506 cell.set_style(style);
507 }
508 }
509 }
510 }
511
512 state.screen_cursor = screen_cursor.map(|v| (state.inner.x + v.0, state.inner.y + v.1));
513}
514
515impl Default for TextAreaState {
516 fn default() -> Self {
517 let mut s = Self {
518 area: Default::default(),
519 inner: Default::default(),
520 rendered: Default::default(),
521 screen_cursor: Default::default(),
522 hscroll: Default::default(),
523 vscroll: Default::default(),
524 sub_row_offset: 0,
525 dark_offset: Default::default(),
526 scroll_to_cursor: Default::default(),
527 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
528 move_col: Default::default(),
529 auto_indent: true,
530 auto_quote: true,
531 text_wrap: TextWrap::Shift,
532 focus: Default::default(),
533 mouse: Default::default(),
534 non_exhaustive: NonExhaustive,
535 };
536 s.hscroll.set_max_offset(255);
537 s.hscroll.set_overscroll_by(Some(16384));
538 s
539 }
540}
541
542impl HasFocus for TextAreaState {
543 fn build(&self, builder: &mut FocusBuilder) {
544 builder.leaf_widget(self);
545 }
546
547 fn focus(&self) -> FocusFlag {
548 self.focus.clone()
549 }
550
551 fn area(&self) -> Rect {
552 self.area
553 }
554
555 fn navigable(&self) -> Navigation {
556 Navigation::Reach
557 }
558}
559
560impl TextAreaState {
561 #[inline]
563 pub fn new() -> Self {
564 Self::default()
565 }
566
567 #[inline]
569 pub fn named(name: &str) -> Self {
570 Self {
571 focus: FocusFlag::named(name),
572 ..Default::default()
573 }
574 }
575
576 #[inline]
582 pub fn set_newline(&mut self, br: impl Into<String>) {
583 self.value.set_newline(br.into());
584 }
585
586 #[inline]
588 pub fn newline(&self) -> &str {
589 self.value.newline()
590 }
591
592 #[inline]
594 pub fn set_auto_indent(&mut self, indent: bool) {
595 self.auto_indent = indent;
596 }
597
598 #[inline]
600 pub fn set_auto_quote(&mut self, quote: bool) {
601 self.auto_quote = quote;
602 }
603
604 #[inline]
606 pub fn set_tab_width(&mut self, tabs: u16) {
607 self.value.set_tab_width(tabs);
608 }
609
610 #[inline]
612 pub fn tab_width(&self) -> u16 {
613 self.value.tab_width()
614 }
615
616 #[inline]
618 pub fn set_expand_tabs(&mut self, expand: bool) {
619 self.value.set_expand_tabs(expand);
620 }
621
622 #[inline]
624 pub fn expand_tabs(&self) -> bool {
625 self.value.expand_tabs()
626 }
627
628 #[inline]
630 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
631 self.value.set_glyph_ctrl(show_ctrl);
632 }
633
634 pub fn show_ctrl(&self) -> bool {
636 self.value.glyph_ctrl()
637 }
638
639 #[inline]
642 pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
643 self.value.set_wrap_ctrl(wrap_ctrl);
644 }
645
646 pub fn wrap_ctrl(&self) -> bool {
649 self.value.wrap_ctrl()
650 }
651
652 pub fn set_text_wrap(&mut self, text_wrap: TextWrap) {
665 self.text_wrap = text_wrap;
666 }
667
668 pub fn text_wrap(&self) -> TextWrap {
670 self.text_wrap
671 }
672
673 #[inline]
683 pub fn set_move_col(&mut self, col: Option<i16>) {
684 self.move_col = col;
685 }
686
687 #[inline]
689 pub fn move_col(&mut self) -> Option<i16> {
690 self.move_col
691 }
692}
693
694impl TextAreaState {
695 #[inline]
698 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
699 match clip {
700 None => self.value.set_clipboard(None),
701 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
702 }
703 }
704
705 #[inline]
708 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
709 self.value.clipboard()
710 }
711
712 #[inline]
714 pub fn copy_to_clip(&mut self) -> bool {
715 let Some(clip) = self.value.clipboard() else {
716 return false;
717 };
718
719 _ = clip.set_string(self.selected_text().as_ref());
720 false
721 }
722
723 #[inline]
725 pub fn cut_to_clip(&mut self) -> bool {
726 let Some(clip) = self.value.clipboard() else {
727 return false;
728 };
729
730 match clip.set_string(self.selected_text().as_ref()) {
731 Ok(_) => self.delete_range(self.selection()),
732 Err(_) => false,
733 }
734 }
735
736 #[inline]
738 pub fn paste_from_clip(&mut self) -> bool {
739 let Some(clip) = self.value.clipboard() else {
740 return false;
741 };
742
743 if let Ok(text) = clip.get_string() {
744 self.insert_str(text)
745 } else {
746 false
747 }
748 }
749}
750
751impl TextAreaState {
752 #[inline]
759 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
760 match undo {
761 None => self.value.set_undo_buffer(None),
762 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
763 }
764 }
765
766 #[inline]
768 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
769 self.value.undo_buffer()
770 }
771
772 #[inline]
774 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
775 self.value.undo_buffer_mut()
776 }
777
778 #[inline]
784 pub fn begin_undo_seq(&mut self) {
785 self.value.begin_undo_seq()
786 }
787
788 #[inline]
790 pub fn end_undo_seq(&mut self) {
791 self.value.end_undo_seq()
792 }
793
794 #[inline]
799 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
800 self.value.recent_replay_log()
801 }
802
803 #[inline]
807 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
808 self.value.replay_log(replay)
809 }
810
811 #[inline]
813 pub fn undo(&mut self) -> bool {
814 self.value.undo()
815 }
816
817 #[inline]
819 pub fn redo(&mut self) -> bool {
820 self.value.redo()
821 }
822}
823
824impl TextAreaState {
825 #[inline]
842 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
843 self.value.set_styles(styles);
844 }
845
846 #[inline]
857 pub fn set_range_styles(&mut self, styles: Vec<(TextRange, usize)>) -> Result<(), TextError> {
858 self.value.set_range_styles(styles)
859 }
860
861 #[inline]
866 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
867 self.value.add_style(range, style);
868 }
869
870 #[inline]
874 pub fn add_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
875 let r = self.value.bytes_at_range(range)?;
876 self.value.add_style(r, style);
877 Ok(())
878 }
879
880 #[inline]
882 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
883 self.value.remove_style(range, style);
884 }
885
886 #[inline]
888 pub fn remove_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
889 let r = self.value.bytes_at_range(range)?;
890 self.value.remove_style(r, style);
891 Ok(())
892 }
893
894 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
896 self.value.styles_in(range, buf)
897 }
898
899 #[inline]
901 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
902 self.value.styles_at(byte_pos, buf)
903 }
904
905 #[inline]
908 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
909 self.value.style_match(byte_pos, style)
910 }
911
912 #[inline]
914 pub fn styles(&self) -> impl Iterator<Item = (Range<usize>, usize)> + '_ {
915 self.value.styles().expect("styles")
916 }
917}
918
919impl TextAreaState {
920 #[inline]
922 pub fn offset(&self) -> (usize, usize) {
923 (self.hscroll.offset(), self.vscroll.offset())
924 }
925
926 #[inline]
931 pub fn set_offset(&mut self, offset: (usize, usize)) -> bool {
932 self.scroll_to_cursor = false;
933 let c = self.hscroll.set_offset(offset.0);
934 let r = self.vscroll.set_offset(offset.1);
935 r || c
936 }
937
938 pub fn set_sub_row_offset(&mut self, sub_row_offset: upos_type) -> bool {
950 self.scroll_to_cursor = false;
951 let old = self.sub_row_offset;
952 self.sub_row_offset = sub_row_offset;
953 sub_row_offset != old
954 }
955
956 pub fn sub_row_offset(&self) -> upos_type {
961 self.sub_row_offset
962 }
963
964 fn clean_offset(&self) -> (upos_type, upos_type, upos_type) {
969 let ox = self.hscroll.offset as upos_type;
970 let mut oy = self.vscroll.offset as upos_type;
971
972 if oy >= self.len_lines() {
974 oy = 0;
975 }
976
977 match self.text_wrap {
978 TextWrap::Shift => (ox, 0, oy),
979 TextWrap::Hard | TextWrap::Word(_) => {
980 if let Ok(max_col) = self.try_line_width(oy) {
982 (0, min(self.sub_row_offset, max_col), oy)
983 } else {
984 (0, 0, oy)
985 }
986 }
987 }
988 }
989
990 #[inline]
992 pub fn cursor(&self) -> TextPosition {
993 self.value.cursor()
994 }
995
996 #[inline]
998 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
999 self.scroll_cursor_to_visible();
1000 self.value.set_cursor(cursor.into(), extend_selection)
1001 }
1002
1003 #[inline]
1005 pub fn anchor(&self) -> TextPosition {
1006 self.value.anchor()
1007 }
1008
1009 #[inline]
1011 pub fn has_selection(&self) -> bool {
1012 self.value.has_selection()
1013 }
1014
1015 #[inline]
1017 pub fn selection(&self) -> TextRange {
1018 self.value.selection()
1019 }
1020
1021 #[inline]
1024 pub fn set_selection(
1025 &mut self,
1026 anchor: impl Into<TextPosition>,
1027 cursor: impl Into<TextPosition>,
1028 ) -> bool {
1029 self.scroll_cursor_to_visible();
1030 self.value.set_selection(anchor.into(), cursor.into())
1031 }
1032
1033 #[inline]
1036 pub fn select_all(&mut self) -> bool {
1037 self.scroll_cursor_to_visible();
1038 self.value.select_all()
1039 }
1040
1041 #[inline]
1043 pub fn selected_text(&self) -> Cow<'_, str> {
1044 self.value
1045 .str_slice(self.value.selection())
1046 .expect("valid_selection")
1047 }
1048}
1049
1050impl TextAreaState {
1051 #[inline]
1053 pub fn is_empty(&self) -> bool {
1054 self.value.is_empty()
1055 }
1056
1057 #[inline]
1059 pub fn rope(&self) -> &Rope {
1060 self.value.text().rope()
1061 }
1062
1063 #[inline]
1065 pub fn text(&self) -> String {
1066 self.value.text().string()
1067 }
1068
1069 #[inline]
1073 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1074 self.value.str_slice_byte(range).expect("valid_range")
1075 }
1076
1077 #[inline]
1079 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1080 self.value.str_slice_byte(range)
1081 }
1082
1083 #[inline]
1087 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
1088 self.value.str_slice(range.into()).expect("valid_range")
1089 }
1090
1091 #[inline]
1093 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
1094 self.value.str_slice(range.into())
1095 }
1096
1097 #[inline]
1099 pub fn len_lines(&self) -> upos_type {
1100 self.value.len_lines()
1101 }
1102
1103 #[inline]
1107 pub fn line_width(&self, row: upos_type) -> upos_type {
1108 self.try_line_width(row).expect("valid_row")
1109 }
1110
1111 #[inline]
1113 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1114 self.value.line_width(row)
1115 }
1116
1117 #[inline]
1122 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
1123 self.value.line_at(row).expect("valid_row")
1124 }
1125
1126 #[inline]
1129 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1130 self.value.line_at(row)
1131 }
1132
1133 #[inline]
1137 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
1138 self.value.lines_at(row).expect("valid_row")
1139 }
1140
1141 #[inline]
1143 pub fn try_lines_at(
1144 &self,
1145 row: upos_type,
1146 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1147 self.value.lines_at(row)
1148 }
1149
1150 #[inline]
1153 #[allow(deprecated)]
1154 #[deprecated(since = "1.1.0", note = "discontinued api")]
1155 pub fn glyphs(
1156 &self,
1157 rows: Range<upos_type>,
1158 screen_offset: u16,
1159 screen_width: u16,
1160 ) -> impl Iterator<Item = Glyph<'_>> {
1161 self.value
1162 .glyphs(rows, screen_offset, screen_width)
1163 .expect("valid_rows")
1164 }
1165
1166 #[inline]
1169 #[allow(deprecated)]
1170 #[deprecated(since = "1.1.0", note = "discontinued api")]
1171 pub fn try_glyphs(
1172 &self,
1173 rows: Range<upos_type>,
1174 screen_offset: u16,
1175 screen_width: u16,
1176 ) -> Result<impl Iterator<Item = Glyph<'_>>, TextError> {
1177 self.value.glyphs(rows, screen_offset, screen_width)
1178 }
1179
1180 #[inline]
1185 pub fn line_graphemes(&self, row: upos_type) -> impl Iterator<Item = Grapheme<'_>> {
1186 self.value.line_graphemes(row).expect("valid_row")
1187 }
1188
1189 #[inline]
1192 pub fn try_line_graphemes(
1193 &self,
1194 row: upos_type,
1195 ) -> Result<impl Iterator<Item = Grapheme<'_>>, TextError> {
1196 self.value.line_graphemes(row)
1197 }
1198
1199 #[inline]
1203 pub fn text_graphemes(&self, pos: TextPosition) -> impl Cursor<Item = Grapheme<'_>> {
1204 self.value.text_graphemes(pos).expect("valid_pos")
1205 }
1206
1207 #[inline]
1209 pub fn try_text_graphemes(
1210 &self,
1211 pos: TextPosition,
1212 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1213 self.value.text_graphemes(pos)
1214 }
1215
1216 #[inline]
1220 pub fn graphemes(
1221 &self,
1222 range: TextRange,
1223 pos: TextPosition,
1224 ) -> impl Cursor<Item = Grapheme<'_>> {
1225 self.value.graphemes(range, pos).expect("valid_args")
1226 }
1227
1228 #[inline]
1230 pub fn try_graphemes(
1231 &self,
1232 range: TextRange,
1233 pos: TextPosition,
1234 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1235 self.value.graphemes(range, pos)
1236 }
1237
1238 #[inline]
1243 pub fn byte_at(&self, pos: TextPosition) -> Range<usize> {
1244 self.value.byte_at(pos).expect("valid_pos")
1245 }
1246
1247 #[inline]
1250 pub fn try_byte_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError> {
1251 self.value.byte_at(pos)
1252 }
1253
1254 #[inline]
1258 pub fn bytes_at_range(&self, range: TextRange) -> Range<usize> {
1259 self.value.bytes_at_range(range).expect("valid_range")
1260 }
1261
1262 #[inline]
1264 pub fn try_bytes_at_range(&self, range: TextRange) -> Result<Range<usize>, TextError> {
1265 self.value.bytes_at_range(range)
1266 }
1267
1268 #[inline]
1273 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1274 self.value.byte_pos(byte).expect("valid_pos")
1275 }
1276
1277 #[inline]
1280 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1281 self.value.byte_pos(byte)
1282 }
1283
1284 #[inline]
1288 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1289 self.value.byte_range(bytes).expect("valid_range")
1290 }
1291
1292 #[inline]
1294 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1295 self.value.byte_range(bytes)
1296 }
1297}
1298
1299impl TextAreaState {
1300 #[inline]
1302 pub fn clear(&mut self) -> bool {
1303 if !self.is_empty() {
1304 self.value.clear();
1305 true
1306 } else {
1307 false
1308 }
1309 }
1310
1311 #[inline]
1315 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1316 self.scroll_to_cursor = false;
1317 self.vscroll.set_offset(0);
1318 self.hscroll.set_offset(0);
1319 self.set_sub_row_offset(0);
1320 self.set_move_col(None);
1321
1322 self.value.set_text(TextRope::new_text(s.as_ref()));
1323 }
1324
1325 #[inline]
1328 pub fn set_rope(&mut self, r: Rope) {
1329 self.scroll_to_cursor = false;
1330 self.vscroll.set_offset(0);
1331 self.hscroll.set_offset(0);
1332 self.set_sub_row_offset(0);
1333 self.set_move_col(None);
1334
1335 self.value.set_text(TextRope::new_rope(r));
1336 }
1337
1338 pub fn insert_char(&mut self, c: char) -> bool {
1349 let mut insert = true;
1350 if self.has_selection() {
1351 if self.auto_quote
1352 && (c == '\''
1353 || c == '"'
1354 || c == '`'
1355 || c == '<'
1356 || c == '['
1357 || c == '('
1358 || c == '{')
1359 {
1360 self.value
1361 .insert_quotes(self.selection(), c)
1362 .expect("valid_selection");
1363 insert = false;
1364 } else {
1365 self.value
1366 .remove_str_range(self.selection())
1367 .expect("valid_selection");
1368 }
1369 }
1370
1371 if insert {
1372 if c == '\n' {
1373 self.value
1374 .insert_newline(self.cursor())
1375 .expect("valid_cursor");
1376 } else if c == '\t' {
1377 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1378 } else {
1379 self.value
1380 .insert_char(self.cursor(), c)
1381 .expect("valid_cursor");
1382 }
1383 }
1384
1385 self.scroll_cursor_to_visible();
1386
1387 true
1388 }
1389
1390 pub fn insert_tab(&mut self) -> bool {
1396 if self.has_selection() {
1397 if self.auto_indent {
1398 let sel = self.selection();
1399 let indent = " ".repeat(self.tab_width() as usize);
1400
1401 self.value.begin_undo_seq();
1402 for r in sel.start.y..=sel.end.y {
1403 self.value
1404 .insert_str(TextPosition::new(0, r), &indent)
1405 .expect("valid_row");
1406 }
1407 self.value.end_undo_seq();
1408
1409 true
1410 } else {
1411 false
1412 }
1413 } else {
1414 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1415 self.scroll_cursor_to_visible();
1416
1417 true
1418 }
1419 }
1420
1421 pub fn insert_backtab(&mut self) -> bool {
1426 if self.has_selection() {
1427 let sel = self.selection();
1428
1429 self.value.begin_undo_seq();
1430 for r in sel.start.y..=sel.end.y {
1431 let mut idx = 0;
1432 let g_it = self
1433 .value
1434 .graphemes(TextRange::new((0, r), (0, r + 1)), TextPosition::new(0, r))
1435 .expect("valid_range")
1436 .take(self.tab_width() as usize);
1437 for g in g_it {
1438 if g != " " && g != "\t" {
1439 break;
1440 }
1441 idx += 1;
1442 }
1443
1444 self.value
1445 .remove_str_range(TextRange::new((0, r), (idx, r)))
1446 .expect("valid_range");
1447 }
1448 self.value.end_undo_seq();
1449
1450 true
1451 } else {
1452 false
1453 }
1454 }
1455
1456 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1459 let t = t.as_ref();
1460 if self.has_selection() {
1461 self.value
1462 .remove_str_range(self.selection())
1463 .expect("valid_selection");
1464 }
1465 self.value
1466 .insert_str(self.cursor(), t)
1467 .expect("valid_cursor");
1468 self.scroll_cursor_to_visible();
1469 true
1470 }
1471
1472 pub fn insert_newline(&mut self) -> bool {
1477 if self.has_selection() {
1478 self.value
1479 .remove_str_range(self.selection())
1480 .expect("valid_selection");
1481 }
1482 self.value
1483 .insert_newline(self.cursor())
1484 .expect("valid_cursor");
1485
1486 if self.auto_indent {
1488 let cursor = self.cursor();
1489 if cursor.y > 0 {
1490 let mut blanks = String::new();
1491 for g in self.line_graphemes(cursor.y - 1) {
1492 if g == " " || g == "\t" {
1493 blanks.push_str(g.grapheme());
1494 } else {
1495 break;
1496 }
1497 }
1498 if !blanks.is_empty() {
1499 self.value
1500 .insert_str(cursor, &blanks)
1501 .expect("valid_cursor");
1502 }
1503 }
1504 }
1505
1506 self.scroll_cursor_to_visible();
1507 true
1508 }
1509
1510 #[inline]
1514 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1515 self.try_delete_range(range).expect("valid_range")
1516 }
1517
1518 #[inline]
1520 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1521 let range = range.into();
1522 if !range.is_empty() {
1523 self.value.remove_str_range(range)?;
1524 self.scroll_cursor_to_visible();
1525 Ok(true)
1526 } else {
1527 Ok(false)
1528 }
1529 }
1530}
1531
1532impl TextAreaState {
1533 pub fn duplicate_text(&mut self) -> bool {
1536 if self.has_selection() {
1537 let sel_range = self.selection();
1538 if !sel_range.is_empty() {
1539 let v = self.str_slice(sel_range).to_string();
1540 self.value
1541 .insert_str(sel_range.end, &v)
1542 .expect("valid_selection");
1543 true
1544 } else {
1545 false
1546 }
1547 } else {
1548 let pos = self.cursor();
1549 let row_range = TextRange::new((0, pos.y), (0, pos.y + 1));
1550 let v = self.str_slice(row_range).to_string();
1551 self.value
1552 .insert_str(row_range.start, &v)
1553 .expect("valid_cursor");
1554 true
1555 }
1556 }
1557
1558 pub fn delete_line(&mut self) -> bool {
1561 let pos = self.cursor();
1562 if pos.y + 1 < self.len_lines() {
1563 self.delete_range(TextRange::new((0, pos.y), (0, pos.y + 1)))
1564 } else {
1565 let width = self.line_width(pos.y);
1566 self.delete_range(TextRange::new((0, pos.y), (width, pos.y)))
1567 }
1568 }
1569
1570 pub fn delete_next_char(&mut self) -> bool {
1573 if self.has_selection() {
1574 self.delete_range(self.selection())
1575 } else {
1576 let r = self
1577 .value
1578 .remove_next_char(self.cursor())
1579 .expect("valid_cursor");
1580 self.scroll_cursor_to_visible();
1581 r
1582 }
1583 }
1584
1585 pub fn delete_prev_char(&mut self) -> bool {
1588 if self.has_selection() {
1589 self.delete_range(self.selection())
1590 } else {
1591 let r = self
1592 .value
1593 .remove_prev_char(self.cursor())
1594 .expect("valid_cursor");
1595 self.scroll_cursor_to_visible();
1596 r
1597 }
1598 }
1599
1600 #[inline]
1605 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1606 self.value.next_word_start(pos.into()).expect("valid_pos")
1607 }
1608
1609 #[inline]
1612 pub fn try_next_word_start(
1613 &self,
1614 pos: impl Into<TextPosition>,
1615 ) -> Result<TextPosition, TextError> {
1616 self.value.next_word_start(pos.into())
1617 }
1618
1619 #[inline]
1624 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1625 self.value.next_word_end(pos.into()).expect("valid_pos")
1626 }
1627
1628 #[inline]
1631 pub fn try_next_word_end(
1632 &self,
1633 pos: impl Into<TextPosition>,
1634 ) -> Result<TextPosition, TextError> {
1635 self.value.next_word_end(pos.into())
1636 }
1637
1638 #[inline]
1646 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1647 self.value.prev_word_start(pos.into()).expect("valid_pos")
1648 }
1649
1650 #[inline]
1656 pub fn try_prev_word_start(
1657 &self,
1658 pos: impl Into<TextPosition>,
1659 ) -> Result<TextPosition, TextError> {
1660 self.value.prev_word_start(pos.into())
1661 }
1662
1663 #[inline]
1669 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1670 self.value.prev_word_end(pos.into()).expect("valid_pos")
1671 }
1672
1673 #[inline]
1677 pub fn try_prev_word_end(
1678 &self,
1679 pos: impl Into<TextPosition>,
1680 ) -> Result<TextPosition, TextError> {
1681 self.value.prev_word_end(pos.into())
1682 }
1683
1684 #[inline]
1688 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1689 self.value.is_word_boundary(pos.into()).expect("valid_pos")
1690 }
1691
1692 #[inline]
1694 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1695 self.value.is_word_boundary(pos.into())
1696 }
1697
1698 #[inline]
1703 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1704 self.value.word_start(pos.into()).expect("valid_pos")
1705 }
1706
1707 #[inline]
1710 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1711 self.value.word_start(pos.into())
1712 }
1713
1714 #[inline]
1719 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1720 self.value.word_end(pos.into()).expect("valid_pos")
1721 }
1722
1723 #[inline]
1726 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1727 self.value.word_end(pos.into())
1728 }
1729
1730 pub fn delete_next_word(&mut self) -> bool {
1735 if self.has_selection() {
1736 self.delete_range(self.selection())
1737 } else {
1738 let cursor = self.cursor();
1739
1740 let start = self.next_word_start(cursor);
1741 if start != cursor {
1742 self.delete_range(cursor..start)
1743 } else {
1744 let end = self.next_word_end(cursor);
1745 self.delete_range(cursor..end)
1746 }
1747 }
1748 }
1749
1750 pub fn delete_prev_word(&mut self) -> bool {
1755 if self.has_selection() {
1756 self.delete_range(self.selection())
1757 } else {
1758 let cursor = self.cursor();
1759
1760 let till_line_start = if cursor.x != 0 {
1762 self.graphemes(TextRange::new((0, cursor.y), cursor), cursor)
1763 .rev_cursor()
1764 .all(|v| v.is_whitespace())
1765 } else {
1766 false
1767 };
1768
1769 if till_line_start {
1770 self.delete_range(TextRange::new((0, cursor.y), cursor))
1771 } else {
1772 let end = self.prev_word_end(cursor);
1773 if end != cursor {
1774 self.delete_range(end..cursor)
1775 } else {
1776 let start = self.prev_word_start(cursor);
1777 self.delete_range(start..cursor)
1778 }
1779 }
1780 }
1781 }
1782
1783 pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1786 let mut cursor = self.cursor();
1787 if cursor.x == 0 {
1788 if cursor.y > 0 {
1789 cursor.y = cursor.y.saturating_sub(1);
1790 cursor.x = self.line_width(cursor.y);
1791 }
1792 } else {
1793 cursor.x = cursor.x.saturating_sub(n as upos_type);
1794 }
1795
1796 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
1797 self.set_move_col(Some(scr_cursor.0));
1798 }
1799
1800 self.set_cursor(cursor, extend_selection)
1801 }
1802
1803 pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1806 let mut cursor = self.cursor();
1807 let c_line_width = self.line_width(cursor.y);
1808 if cursor.x == c_line_width {
1809 if cursor.y + 1 < self.len_lines() {
1810 cursor.y += 1;
1811 cursor.x = 0;
1812 }
1813 } else {
1814 cursor.x = min(cursor.x + n as upos_type, c_line_width)
1815 }
1816
1817 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
1818 self.set_move_col(Some(scr_cursor.0));
1819 }
1820 self.set_cursor(cursor, extend_selection)
1821 }
1822
1823 pub fn move_up(&mut self, n: u16, extend_selection: bool) -> bool {
1826 let cursor = self.cursor();
1827 if let Some(mut scr_cursor) = self.pos_to_relative_screen(cursor) {
1828 if let Some(move_col) = self.move_col() {
1829 scr_cursor.0 = move_col;
1830 }
1831 scr_cursor.1 -= n as i16;
1832
1833 if let Some(new_cursor) = self.relative_screen_to_pos(scr_cursor) {
1834 self.set_cursor(new_cursor, extend_selection)
1835 } else {
1836 self.scroll_cursor_to_visible();
1837 true
1838 }
1839 } else {
1840 self.scroll_cursor_to_visible();
1841 true
1842 }
1843 }
1844
1845 pub fn move_down(&mut self, n: u16, extend_selection: bool) -> bool {
1848 let cursor = self.cursor();
1849 if let Some(mut scr_cursor) = self.pos_to_relative_screen(cursor) {
1850 if let Some(move_col) = self.move_col() {
1851 scr_cursor.0 = move_col;
1852 }
1853 scr_cursor.1 += n as i16;
1854
1855 if let Some(new_cursor) = self.relative_screen_to_pos(scr_cursor) {
1856 self.set_cursor(new_cursor, extend_selection)
1857 } else {
1858 self.scroll_cursor_to_visible();
1859 true
1860 }
1861 } else {
1862 self.scroll_cursor_to_visible();
1863 true
1864 }
1865 }
1866
1867 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1871 let cursor = self.cursor();
1872
1873 let mut line_start = self.pos_to_line_start(cursor);
1874 for g in self
1875 .glyphs2(
1876 0,
1877 line_start.x,
1878 line_start.y..min(line_start.y + 1, self.len_lines()),
1879 )
1880 .expect("valid-pos")
1881 {
1882 if g.glyph() != " " && g.glyph() != "\t" {
1883 if g.pos().x != cursor.x {
1884 line_start.x = g.pos().x;
1885 }
1886 break;
1887 }
1888 }
1889
1890 if let Some(scr_pos) = self.pos_to_relative_screen(line_start) {
1891 self.set_move_col(Some(scr_pos.0));
1892 }
1893 self.set_cursor(line_start, extend_selection)
1894 }
1895
1896 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1900 let cursor = self.cursor();
1901 let line_end = self.pos_to_line_end(cursor);
1902 if let Some(scr_pos) = self.pos_to_relative_screen(line_end) {
1903 self.set_move_col(Some(scr_pos.0));
1904 }
1905 self.set_cursor(line_end, extend_selection)
1906 }
1907
1908 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1910 let cursor = TextPosition::new(0, 0);
1911
1912 self.set_move_col(Some(0));
1913 self.set_cursor(cursor, extend_selection)
1914 }
1915
1916 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1918 let cursor = TextPosition::new(
1919 self.line_width(self.len_lines().saturating_sub(1)),
1920 self.len_lines().saturating_sub(1),
1921 );
1922
1923 let line_start = self.pos_to_line_start(cursor);
1924 self.set_move_col(Some(0));
1925 self.set_cursor(line_start, extend_selection)
1926 }
1927
1928 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1930 let (ox, oy) = self.offset();
1931
1932 let cursor = TextPosition::new(ox as upos_type, oy as upos_type);
1933
1934 self.set_move_col(Some(0));
1935 self.set_cursor(cursor, extend_selection)
1936 }
1937
1938 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1940 let scr_end = (0, (self.inner.height as i16).saturating_sub(1));
1941 if let Some(pos) = self.relative_screen_to_pos(scr_end) {
1942 self.set_move_col(Some(0));
1943 self.set_cursor(pos, extend_selection)
1944 } else {
1945 self.scroll_cursor_to_visible();
1946 true
1947 }
1948 }
1949
1950 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1952 let cursor = self.cursor();
1953
1954 let word = self.next_word_end(cursor);
1955
1956 if let Some(scr_pos) = self.pos_to_relative_screen(word) {
1957 self.set_move_col(Some(scr_pos.0));
1958 }
1959 self.set_cursor(word, extend_selection)
1960 }
1961
1962 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1964 let cursor = self.cursor();
1965
1966 let word = self.prev_word_start(cursor);
1967
1968 if let Some(scr_pos) = self.pos_to_relative_screen(word) {
1969 self.set_move_col(Some(scr_pos.0));
1970 }
1971 self.set_cursor(word, extend_selection)
1972 }
1973}
1974
1975impl HasScreenCursor for TextAreaState {
1976 #[allow(clippy::question_mark)]
1978 fn screen_cursor(&self) -> Option<(u16, u16)> {
1979 if self.is_focused() {
1980 if self.has_selection() {
1981 None
1982 } else {
1983 let Some(scr_cursor) = self.screen_cursor else {
1984 return None;
1985 };
1986
1987 if !(scr_cursor.0 >= self.inner.x
1988 && scr_cursor.0 <= self.inner.right()
1989 && scr_cursor.1 >= self.inner.y
1990 && scr_cursor.1 < self.inner.bottom())
1991 {
1992 return None;
1993 }
1994 Some(scr_cursor)
1995 }
1996 } else {
1997 None
1998 }
1999 }
2000}
2001
2002impl RelocatableState for TextAreaState {
2003 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
2004 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
2006 self.area = relocate_area(self.area, shift, clip);
2007 self.inner = relocate_area(self.inner, shift, clip);
2008 if let Some(screen_cursor) = self.screen_cursor {
2009 self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
2010 }
2011 }
2012}
2013
2014impl TextAreaState {
2015 fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
2016 match self.text_wrap {
2017 TextWrap::Shift => (
2018 TextWrap2::Shift,
2019 shift_left,
2020 shift_left + self.rendered.width as upos_type,
2021 shift_left + self.rendered.width as upos_type,
2022 ),
2023 TextWrap::Hard => (
2024 TextWrap2::Hard,
2025 0,
2026 self.rendered.width as upos_type,
2027 self.rendered.width as upos_type,
2028 ),
2029 TextWrap::Word(margin) => (
2030 TextWrap2::Word,
2031 0,
2032 self.rendered.width as upos_type,
2033 self.rendered.width.saturating_sub(margin) as upos_type,
2034 ),
2035 }
2036 }
2037
2038 fn fill_cache(
2041 &self,
2042 shift_left: upos_type,
2043 sub_row_offset: upos_type,
2044 rows: Range<upos_type>,
2045 ) -> Result<(), TextError> {
2046 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
2047 self.value.fill_cache(
2048 self.rendered,
2049 sub_row_offset,
2050 rows,
2051 text_wrap,
2052 self.wrap_ctrl() | self.show_ctrl(),
2053 left_margin,
2054 right_margin,
2055 word_margin,
2056 )
2057 }
2058
2059 fn glyphs2(
2060 &self,
2061 shift_left: upos_type,
2062 sub_row_offset: upos_type,
2063 rows: Range<upos_type>,
2064 ) -> Result<GlyphIter2<<TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
2065 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
2066 self.value.glyphs2(
2067 self.rendered,
2068 sub_row_offset,
2069 rows,
2070 text_wrap,
2071 self.wrap_ctrl() | self.show_ctrl(),
2072 left_margin,
2073 right_margin,
2074 word_margin,
2075 )
2076 }
2077
2078 pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
2080 let scr_pos = (
2081 scr_pos.0 as i16 - self.inner.x as i16,
2082 scr_pos.1 as i16 - self.inner.y as i16,
2083 );
2084 self.relative_screen_to_pos(scr_pos)
2085 }
2086
2087 pub fn pos_to_screen(&self, pos: TextPosition) -> Option<(u16, u16)> {
2089 let scr_pos = self.pos_to_relative_screen(pos)?;
2090 if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
2091 Some((
2092 (scr_pos.0 + self.inner.x as i16) as u16,
2093 (scr_pos.1 + self.inner.y as i16) as u16,
2094 ))
2095 } else {
2096 None
2097 }
2098 }
2099
2100 pub fn pos_to_line_start(&self, pos: TextPosition) -> TextPosition {
2102 match self.text_wrap {
2103 TextWrap::Shift => {
2104 TextPosition::new(0, pos.y)
2106 }
2107 TextWrap::Hard | TextWrap::Word(_) => {
2108 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2109 .expect("valid-row");
2110
2111 let mut start_pos = TextPosition::new(0, pos.y);
2112 for (break_pos, _) in self.value.cache().line_break.borrow().range(
2113 TextPosition::new(0, pos.y)
2114 ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
2115 ) {
2116 if pos >= start_pos && &pos <= break_pos {
2117 break;
2118 }
2119 start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2120 }
2121
2122 start_pos
2123 }
2124 }
2125 }
2126
2127 pub fn pos_to_line_end(&self, pos: TextPosition) -> TextPosition {
2129 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2130 .expect("valid-row");
2131
2132 let mut end_pos = TextPosition::new(0, pos.y);
2133 for (break_pos, _) in self
2134 .value
2135 .cache()
2136 .line_break
2137 .borrow()
2138 .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2139 {
2140 if pos >= end_pos && &pos <= break_pos {
2141 end_pos = *break_pos;
2142 break;
2143 }
2144 end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2145 }
2146
2147 end_pos
2148 }
2149
2150 fn scroll_to_cursor(&mut self) {
2151 let cursor = self.cursor();
2152
2153 let nox;
2154 let noy;
2155 let mut nsub_row_offset;
2156 match self.text_wrap {
2157 TextWrap::Shift => {
2158 let (ox, _, oy) = self.clean_offset();
2159
2160 let height = self.rendered.height as upos_type;
2161 let width = self.rendered.width as upos_type;
2162 let width = if self.show_ctrl() || self.wrap_ctrl() {
2163 width.saturating_sub(1)
2164 } else {
2165 width
2166 };
2167
2168 noy = if cursor.y < oy {
2169 cursor.y
2170 } else if cursor.y >= oy + height {
2171 cursor.y.saturating_sub(height) + 1
2172 } else {
2173 oy
2174 };
2175
2176 nox = if cursor.x < ox {
2177 cursor.x
2178 } else if cursor.x >= ox + width {
2179 cursor.x.saturating_sub(width) + 1
2180 } else {
2181 ox
2182 };
2183
2184 nsub_row_offset = 0;
2185 }
2186 TextWrap::Hard | TextWrap::Word(_) => {
2187 let (ox, sub_row_offset, oy) = self.clean_offset();
2188
2189 'f: {
2192 let mut sub_row_offsets = Vec::new();
2193 for g in self
2194 .glyphs2(
2195 0,
2196 sub_row_offset,
2197 oy..min(oy + 2 * self.rendered.height as upos_type, self.len_lines()),
2198 )
2199 .expect("valid_offset")
2200 {
2201 if g.screen_pos().0 == 0 {
2202 sub_row_offsets.push((g.pos().x, g.pos().y));
2203 }
2204 if g.screen_pos().1 >= 2 * self.rendered.height {
2205 break;
2206 }
2207 if g.pos() == cursor {
2208 if g.screen_pos().1 >= self.rendered.height {
2209 (nsub_row_offset, noy) = sub_row_offsets
2210 [(g.screen_pos().1 - self.rendered.height + 1) as usize];
2211 nox = ox;
2212 } else {
2213 nsub_row_offset = sub_row_offset;
2214 nox = ox;
2215 noy = oy;
2216 }
2217
2218 break 'f;
2219 }
2220 }
2221
2222 nsub_row_offset = 0;
2228 for g in self
2229 .glyphs2(0, 0, cursor.y..min(cursor.y + 1, self.len_lines()))
2230 .expect("valid_offset")
2231 {
2232 if g.screen_pos().0 == 0 {
2233 nsub_row_offset = g.pos().x;
2234 }
2235 if g.pos() == cursor {
2236 break; }
2238 }
2239
2240 nox = 0;
2241 noy = cursor.y;
2242 }
2243 }
2244 }
2245
2246 self.set_offset((nox as usize, noy as usize));
2247 self.set_sub_row_offset(nsub_row_offset);
2248 }
2249
2250 #[allow(clippy::explicit_counter_loop)]
2260 pub fn pos_to_relative_screen(&self, pos: TextPosition) -> Option<(i16, i16)> {
2261 match self.text_wrap {
2262 TextWrap::Shift => {
2263 let (ox, _, oy) = self.clean_offset();
2264
2265 if oy > self.len_lines() {
2266 return None;
2267 }
2268
2269 if pos.y < oy {
2270 return None;
2271 }
2272 if pos.y > self.len_lines() {
2273 return None;
2274 }
2275 if pos.y - oy >= self.rendered.height as u32 {
2276 return None;
2277 }
2278
2279 let screen_y = (pos.y - oy) as u16;
2280
2281 let screen_x = 'f: {
2282 for g in self
2283 .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2284 .expect("valid-row")
2285 {
2286 if g.pos().x == pos.x {
2287 break 'f g.screen_pos().0;
2288 } else if g.line_break() {
2289 break 'f g.screen_pos().0;
2290 }
2291 }
2292 return None;
2293 };
2294 assert!(screen_x <= self.rendered.width);
2295
2296 Some((
2297 screen_x as i16 - self.dark_offset.0 as i16,
2298 screen_y as i16 - self.dark_offset.1 as i16,
2299 ))
2300 }
2301 TextWrap::Hard | TextWrap::Word(_) => {
2302 let (_, sub_row_offset, oy) = self.clean_offset();
2303
2304 if oy > self.len_lines() {
2305 return None;
2306 }
2307 if pos.y < oy {
2308 return None;
2309 }
2310 if pos.y > self.len_lines() {
2311 return None;
2312 }
2313
2314 self.fill_cache(
2315 0,
2316 sub_row_offset,
2317 oy..min(oy + self.rendered.height as upos_type, self.len_lines()),
2318 )
2319 .expect("valid-rows");
2320
2321 let (screen_y, start_pos) = 'f: {
2322 let mut prev_screen_y = 0;
2323 let mut prev_pos = TextPosition::new(sub_row_offset, oy);
2324 let mut screen_y = 0;
2325 let mut start_pos = TextPosition::new(sub_row_offset, oy);
2326
2327 for (_, cache) in self.value.cache().line_break.borrow().range(
2328 TextPosition::new(sub_row_offset, oy)
2329 ..TextPosition::new(
2330 0,
2331 min(oy + self.rendered.height as upos_type, self.len_lines()),
2332 ),
2333 ) {
2334 if screen_y >= self.rendered.height {
2335 return None;
2336 }
2337 if pos < cache.start_pos {
2338 break 'f (screen_y, start_pos);
2339 }
2340
2341 prev_pos = start_pos;
2342 prev_screen_y = screen_y;
2343
2344 screen_y += 1;
2345 start_pos = cache.start_pos;
2346 }
2347
2348 if pos == start_pos {
2350 break 'f (prev_screen_y, prev_pos);
2351 }
2352 return None;
2353 };
2354
2355 let screen_x = 'f: {
2356 for g in self
2357 .glyphs2(
2358 0,
2359 start_pos.x,
2360 start_pos.y..min(start_pos.y + 1, self.len_lines()),
2361 )
2362 .expect("valid-row")
2363 {
2364 if g.pos().x == pos.x {
2365 break 'f g.screen_pos().0;
2366 } else if g.line_break() {
2367 break 'f g.screen_pos().0;
2368 }
2369 }
2370 return None;
2371 };
2372 assert!(screen_x <= self.rendered.width);
2373
2374 Some((
2375 screen_x as i16 - self.dark_offset.0 as i16,
2376 screen_y as i16 - self.dark_offset.1 as i16,
2377 ))
2378 }
2379 }
2380 }
2381
2382 #[allow(clippy::needless_return)]
2384 pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
2385 let scr_pos = (
2386 scr_pos.0 + self.dark_offset.0 as i16,
2387 scr_pos.1 + self.dark_offset.1 as i16,
2388 );
2389
2390 match self.text_wrap {
2391 TextWrap::Shift => {
2392 let (ox, _, oy) = self.clean_offset();
2393
2394 if oy >= self.len_lines() {
2395 return None;
2396 }
2397 if scr_pos.1 < 0 {
2398 return Some(TextPosition::new(
2400 0,
2401 oy.saturating_add_signed(scr_pos.1 as ipos_type),
2402 ));
2403 }
2404 if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
2405 return Some(TextPosition::new(
2407 self.line_width(self.len_lines().saturating_sub(1)),
2408 self.len_lines().saturating_sub(1),
2409 ));
2410 }
2411
2412 let pos_y = oy + scr_pos.1 as upos_type;
2413
2414 if scr_pos.0 < 0 {
2415 return Some(TextPosition::new(
2416 ox.saturating_add_signed(scr_pos.0 as ipos_type),
2417 pos_y,
2418 ));
2419 } else if scr_pos.0 as u16 >= self.rendered.width {
2420 return Some(TextPosition::new(
2421 min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
2422 pos_y,
2423 ));
2424 } else {
2425 for g in self
2426 .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
2427 .expect("valid-position")
2428 {
2429 if g.contains_screen_x(scr_pos.0 as u16) {
2430 return Some(TextPosition::new(g.pos().x, pos_y));
2431 }
2432 }
2433 unreachable!();
2434 }
2435 }
2436 TextWrap::Hard | TextWrap::Word(_) => {
2437 let (_, sub_row_offset, oy) = self.clean_offset();
2438
2439 if oy >= self.len_lines() {
2440 return None;
2441 }
2442
2443 if scr_pos.1 < 0 {
2444 let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2453
2454 self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2456
2457 let n_start_pos = 'f: {
2458 let mut nrows = scr_pos.1.unsigned_abs();
2459 for (_break_pos, cache) in self
2460 .value
2461 .cache()
2462 .line_break
2463 .borrow()
2464 .range(TextPosition::new(0, ry)..TextPosition::new(sub_row_offset, oy))
2465 .rev()
2466 {
2467 if nrows == 0 {
2468 break 'f cache.start_pos;
2469 }
2470 nrows -= 1;
2471 }
2472 TextPosition::new(0, 0)
2473 };
2474
2475 if scr_pos.0 < 0 {
2477 return Some(n_start_pos);
2478 }
2479
2480 for g in self
2481 .glyphs2(
2482 0,
2483 n_start_pos.x,
2484 n_start_pos.y..min(n_start_pos.y + 1, self.len_lines()),
2485 )
2486 .expect("valid-rows")
2487 {
2488 if g.contains_screen_x(scr_pos.0 as u16) {
2489 return Some(g.pos());
2490 }
2491 }
2492
2493 unreachable!("impossible screen-pos");
2494 } else {
2495 let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2496
2497 let n_start_pos = if scr_pos.1 == 0 {
2499 TextPosition::new(sub_row_offset, oy)
2500 } else {
2501 self.fill_cache(
2504 0,
2505 sub_row_offset,
2506 oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2507 )
2508 .expect("valid-rows");
2509
2510 'f: {
2511 let mut nrows = scr_pos.1 - 1;
2512 let mut fallback = TextPosition::new(sub_row_offset, oy);
2513 for (_break_pos, cache) in self.value.cache().line_break.borrow().range(
2514 TextPosition::new(sub_row_offset, oy)
2515 ..TextPosition::new(0, self.len_lines()),
2516 ) {
2517 if nrows == 0 {
2518 break 'f cache.start_pos;
2519 }
2520 fallback = cache.start_pos;
2521 nrows -= 1;
2522 }
2523 fallback
2524 }
2525 };
2526
2527 for g in self
2528 .glyphs2(
2529 0,
2530 n_start_pos.x,
2531 n_start_pos.y..min(n_start_pos.y + 1, self.len_lines()),
2532 )
2533 .expect("valid-rows")
2534 {
2535 if g.contains_screen_x(scr_pos.0) {
2536 return Some(g.pos());
2537 }
2538 }
2539 unreachable!("impossible screen-pos");
2540 }
2541 }
2542 }
2543 }
2544}
2545
2546impl TextAreaState {
2547 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2550 pub fn screen_to_row(&self, scy: i16) -> upos_type {
2551 let (_, oy) = self.offset();
2552 let oy = oy as upos_type + self.dark_offset.1 as upos_type;
2553
2554 if scy < 0 {
2555 oy.saturating_sub((scy as ipos_type).unsigned_abs())
2556 } else if scy as u16 >= (self.inner.height + self.dark_offset.1) {
2557 min(oy + scy as upos_type, self.len_lines().saturating_sub(1))
2558 } else {
2559 let scy = oy + scy as upos_type;
2560 let len = self.len_lines();
2561 if scy < len {
2562 scy
2563 } else {
2564 len.saturating_sub(1)
2565 }
2566 }
2567 }
2568
2569 #[allow(deprecated)]
2576 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2577 pub fn screen_to_col(&self, row: upos_type, scx: i16) -> upos_type {
2578 self.try_screen_to_col(row, scx).expect("valid_row")
2579 }
2580
2581 #[allow(deprecated)]
2588 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2589 pub fn try_screen_to_col(&self, row: upos_type, scx: i16) -> Result<upos_type, TextError> {
2590 let (ox, _) = self.offset();
2591
2592 let ox = ox as upos_type + self.dark_offset.0 as upos_type;
2593
2594 if scx < 0 {
2595 Ok(ox.saturating_sub((scx as ipos_type).unsigned_abs()))
2596 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
2597 Ok(min(ox + scx as upos_type, self.line_width(row)))
2598 } else {
2599 let scx = scx as u16;
2600
2601 let line = self.try_glyphs(
2602 row..row + 1,
2603 ox as u16,
2604 self.inner.width + self.dark_offset.0,
2605 )?;
2606
2607 let mut col = ox;
2608 for g in line {
2609 if scx < g.screen_pos().0 + g.screen_width() {
2610 break;
2611 }
2612 col = g.pos().x + 1;
2613 }
2614 Ok(col)
2615 }
2616 }
2617
2618 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2621 pub fn row_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
2622 let pos = pos.into();
2623 let (_, oy) = self.offset();
2624
2625 if pos.y < oy as upos_type {
2626 return None;
2627 }
2628
2629 let screen_y = pos.y - oy as upos_type;
2630
2631 if screen_y >= self.dark_offset.1 as upos_type {
2632 Some(screen_y as u16 - self.dark_offset.1)
2633 } else {
2634 None
2635 }
2636 }
2637
2638 #[allow(deprecated)]
2641 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2642 pub fn col_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
2643 self.try_col_to_screen(pos).expect("valid_pos")
2644 }
2645
2646 #[allow(deprecated)]
2649 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2650 pub fn try_col_to_screen(
2651 &self,
2652 pos: impl Into<TextPosition>,
2653 ) -> Result<Option<u16>, TextError> {
2654 let pos = pos.into();
2655 let (ox, _) = self.offset();
2656
2657 if pos.x < ox as upos_type {
2658 return Ok(None);
2659 }
2660
2661 let line = self.try_glyphs(
2662 pos.y..pos.y + 1,
2663 ox as u16,
2664 self.inner.width + self.dark_offset.0,
2665 )?;
2666 let mut screen_x = 0;
2667 for g in line {
2668 if g.pos().x == pos.x {
2669 break;
2670 }
2671 screen_x = g.screen_pos().0 + g.screen_width();
2672 }
2673
2674 if screen_x >= self.dark_offset.0 {
2675 Ok(Some(screen_x - self.dark_offset.0))
2676 } else {
2677 Ok(None)
2678 }
2679 }
2680
2681 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2687 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2688 return false;
2689 };
2690 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2691 self.set_move_col(Some(scr_cursor.0));
2692 }
2693 self.set_cursor(cursor, extend_selection)
2694 }
2695
2696 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2703 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2704 return false;
2705 };
2706
2707 let anchor = self.anchor();
2708 let cursor = if cursor < anchor {
2709 self.word_start(cursor)
2710 } else {
2711 self.word_end(cursor)
2712 };
2713
2714 if !self.is_word_boundary(anchor) {
2716 if cursor < anchor {
2717 self.set_cursor(self.word_end(anchor), false);
2718 } else {
2719 self.set_cursor(self.word_start(anchor), false);
2720 }
2721 }
2722
2723 self.set_cursor(cursor, extend_selection)
2724 }
2725}
2726
2727impl TextAreaState {
2728 pub fn vertical_max_offset(&self) -> usize {
2733 self.vscroll.max_offset()
2734 }
2735
2736 pub fn vertical_offset(&self) -> usize {
2738 self.vscroll.offset()
2739 }
2740
2741 pub fn vertical_page(&self) -> usize {
2743 self.vscroll.page_len()
2744 }
2745
2746 pub fn vertical_scroll(&self) -> usize {
2748 self.vscroll.scroll_by()
2749 }
2750
2751 pub fn horizontal_max_offset(&self) -> usize {
2755 self.hscroll.max_offset()
2756 }
2757
2758 pub fn horizontal_offset(&self) -> usize {
2760 self.hscroll.offset()
2761 }
2762
2763 pub fn horizontal_page(&self) -> usize {
2765 self.hscroll.page_len()
2766 }
2767
2768 pub fn horizontal_scroll(&self) -> usize {
2770 self.hscroll.scroll_by()
2771 }
2772
2773 pub fn set_vertical_offset(&mut self, row_offset: usize) -> bool {
2780 self.scroll_to_cursor = false;
2781 self.sub_row_offset = 0;
2782 self.vscroll.set_offset(row_offset)
2783 }
2784
2785 pub fn set_horizontal_offset(&mut self, col_offset: usize) -> bool {
2794 self.scroll_to_cursor = false;
2795 self.hscroll.set_offset(col_offset)
2796 }
2797
2798 pub fn scroll_to_row(&mut self, pos: usize) -> bool {
2800 self.scroll_to_cursor = false;
2801 self.vscroll.set_offset(pos)
2802 }
2803
2804 pub fn scroll_to_col(&mut self, pos: usize) -> bool {
2808 self.scroll_to_cursor = false;
2809 self.hscroll.set_offset(pos)
2810 }
2811
2812 pub fn scroll_up(&mut self, delta: usize) -> bool {
2814 if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2815 self.sub_row_offset = pos.x;
2816 self.vscroll.set_offset(pos.y as usize);
2817 true
2818 } else {
2819 self.sub_row_offset = 0;
2820
2821 let old = self.vscroll.offset;
2824 self.vscroll.offset = min(
2825 self.vscroll.offset.saturating_sub(delta),
2826 self.vscroll
2827 .max_offset
2828 .saturating_add(self.vscroll.page_len())
2829 .saturating_add(self.vscroll.overscroll_by()),
2830 );
2831 old != self.vscroll.offset
2832 }
2833 }
2834
2835 pub fn scroll_down(&mut self, delta: usize) -> bool {
2837 if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2838 self.sub_row_offset = pos.x;
2839 self.vscroll.set_offset(pos.y as usize);
2840 true
2841 } else {
2842 self.sub_row_offset = 0;
2843
2844 let old = self.vscroll.offset;
2847 self.vscroll.offset = min(
2848 self.vscroll.offset.saturating_add(delta),
2849 self.vscroll
2850 .max_offset
2851 .saturating_add(self.vscroll.page_len())
2852 .saturating_add(self.vscroll.overscroll_by()),
2853 );
2854
2855 old != self.vscroll.offset
2856 }
2857 }
2858
2859 pub fn scroll_left(&mut self, delta: usize) -> bool {
2863 self.hscroll.scroll_left(delta)
2864 }
2865
2866 pub fn scroll_right(&mut self, delta: usize) -> bool {
2870 self.hscroll.scroll_right(delta)
2871 }
2872
2873 pub fn scroll_sub_row_offset(&mut self, col: upos_type) -> bool {
2879 if let Ok(max_col) = self.try_line_width(self.offset().1 as upos_type) {
2880 self.sub_row_offset = min(col as upos_type, max_col);
2881 } else {
2882 self.sub_row_offset = 0;
2883 }
2884 true
2885 }
2886}
2887
2888impl TextAreaState {
2889 pub fn scroll_cursor_to_visible(&mut self) {
2892 self.scroll_to_cursor = true;
2893 }
2894}
2895
2896impl<'a> HandleEvent<crossterm::event::Event, FocusTraversal<'a>, Outcome> for TextAreaState {
2904 fn handle(
2905 &mut self,
2906 event: &crossterm::event::Event,
2907 qualifier: FocusTraversal<'a>,
2908 ) -> Outcome {
2909 if self.is_focused() {
2910 match event {
2911 ct_event!(keycode press Esc) => {
2912 qualifier.0.next_force();
2913 Outcome::Changed
2914 }
2915 ct_event!(keycode press SHIFT-Esc) => {
2916 qualifier.0.prev_force();
2917 Outcome::Changed
2918 }
2919 _ => Outcome::Continue,
2920 }
2921 } else {
2922 Outcome::Continue
2923 }
2924 }
2925}
2926
2927impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2928 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2929 fn tc(r: bool) -> TextOutcome {
2931 if r {
2932 TextOutcome::TextChanged
2933 } else {
2934 TextOutcome::Unchanged
2935 }
2936 }
2937
2938 let mut r = if self.is_focused() {
2939 match event {
2940 ct_event!(key press c)
2941 | ct_event!(key press SHIFT-c)
2942 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2943 ct_event!(keycode press Tab) => {
2944 tc(if !self.focus.gained() {
2946 self.insert_tab()
2947 } else {
2948 false
2949 })
2950 }
2951 ct_event!(keycode press SHIFT-BackTab) => {
2952 tc(if !self.focus.gained() {
2954 self.insert_backtab()
2955 } else {
2956 false
2957 })
2958 }
2959 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2960 ct_event!(keycode press Backspace) => tc(self.delete_prev_char()),
2961 ct_event!(keycode press Delete) => tc(self.delete_next_char()),
2962 ct_event!(keycode press CONTROL-Backspace)
2963 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2964 ct_event!(keycode press CONTROL-Delete) | ct_event!(keycode press ALT-Delete) => {
2965 tc(self.delete_next_word())
2966 }
2967 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2968 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2969 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2970 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2971 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2972 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2973
2974 ct_event!(key release _)
2975 | ct_event!(key release SHIFT-_)
2976 | ct_event!(key release CONTROL_ALT-_)
2977 | ct_event!(keycode release Tab)
2978 | ct_event!(keycode release Enter)
2979 | ct_event!(keycode release Backspace)
2980 | ct_event!(keycode release Delete)
2981 | ct_event!(keycode release CONTROL-Backspace)
2982 | ct_event!(keycode release ALT-Backspace)
2983 | ct_event!(keycode release CONTROL-Delete)
2984 | ct_event!(key release CONTROL-'x')
2985 | ct_event!(key release CONTROL-'v')
2986 | ct_event!(key release CONTROL-'d')
2987 | ct_event!(key release CONTROL-'y')
2988 | ct_event!(key release CONTROL-'z')
2989 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2990 _ => TextOutcome::Continue,
2991 }
2992 } else {
2993 TextOutcome::Continue
2994 };
2995 if r == TextOutcome::Continue {
2996 r = self.handle(event, ReadOnly);
2997 }
2998 r
2999 }
3000}
3001
3002impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
3003 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
3004 let mut r = if self.is_focused() {
3005 match event {
3006 ct_event!(keycode press Left) => self.move_left(1, false).into(),
3007 ct_event!(keycode press Right) => self.move_right(1, false).into(),
3008 ct_event!(keycode press Up) => self.move_up(1, false).into(),
3009 ct_event!(keycode press Down) => self.move_down(1, false).into(),
3010 ct_event!(keycode press PageUp) => {
3011 self.move_up(self.vertical_page() as u16, false).into()
3012 }
3013 ct_event!(keycode press PageDown) => {
3014 self.move_down(self.vertical_page() as u16, false).into()
3015 }
3016 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
3017 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
3018 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
3019 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
3020 ct_event!(keycode press CONTROL-Up) => false.into(),
3021 ct_event!(keycode press CONTROL-Down) => false.into(),
3022 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
3023 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
3024 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
3025 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
3026
3027 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
3028 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
3029 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
3030 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
3031 ct_event!(keycode press ALT-PageUp) => {
3032 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
3033 }
3034 ct_event!(keycode press ALT-PageDown) => {
3035 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
3036 }
3037 ct_event!(keycode press ALT_SHIFT-PageUp) => {
3038 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
3039 }
3040 ct_event!(keycode press ALT_SHIFT-PageDown) => {
3041 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
3042 }
3043
3044 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
3045 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
3046 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
3047 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
3048 ct_event!(keycode press SHIFT-PageUp) => {
3049 self.move_up(self.vertical_page() as u16, true).into()
3050 }
3051 ct_event!(keycode press SHIFT-PageDown) => {
3052 self.move_down(self.vertical_page() as u16, true).into()
3053 }
3054 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
3055 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
3056 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
3057 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
3058 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
3059 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
3060 ct_event!(key press CONTROL-'a') => self.select_all().into(),
3061 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
3062
3063 ct_event!(keycode release Left)
3064 | ct_event!(keycode release Right)
3065 | ct_event!(keycode release Up)
3066 | ct_event!(keycode release Down)
3067 | ct_event!(keycode release PageUp)
3068 | ct_event!(keycode release PageDown)
3069 | ct_event!(keycode release Home)
3070 | ct_event!(keycode release End)
3071 | ct_event!(keycode release CONTROL-Left)
3072 | ct_event!(keycode release CONTROL-Right)
3073 | ct_event!(keycode release CONTROL-Up)
3074 | ct_event!(keycode release CONTROL-Down)
3075 | ct_event!(keycode release CONTROL-PageUp)
3076 | ct_event!(keycode release CONTROL-PageDown)
3077 | ct_event!(keycode release CONTROL-Home)
3078 | ct_event!(keycode release CONTROL-End)
3079 | ct_event!(keycode release ALT-Left)
3080 | ct_event!(keycode release ALT-Right)
3081 | ct_event!(keycode release ALT-Up)
3082 | ct_event!(keycode release ALT-Down)
3083 | ct_event!(keycode release ALT-PageUp)
3084 | ct_event!(keycode release ALT-PageDown)
3085 | ct_event!(keycode release ALT_SHIFT-PageUp)
3086 | ct_event!(keycode release ALT_SHIFT-PageDown)
3087 | ct_event!(keycode release SHIFT-Left)
3088 | ct_event!(keycode release SHIFT-Right)
3089 | ct_event!(keycode release SHIFT-Up)
3090 | ct_event!(keycode release SHIFT-Down)
3091 | ct_event!(keycode release SHIFT-PageUp)
3092 | ct_event!(keycode release SHIFT-PageDown)
3093 | ct_event!(keycode release SHIFT-Home)
3094 | ct_event!(keycode release SHIFT-End)
3095 | ct_event!(keycode release CONTROL_SHIFT-Left)
3096 | ct_event!(keycode release CONTROL_SHIFT-Right)
3097 | ct_event!(keycode release CONTROL_SHIFT-Home)
3098 | ct_event!(keycode release CONTROL_SHIFT-End)
3099 | ct_event!(key release CONTROL-'a')
3100 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
3101 _ => TextOutcome::Continue,
3102 }
3103 } else {
3104 TextOutcome::Continue
3105 };
3106
3107 if r == TextOutcome::Continue {
3108 r = self.handle(event, MouseOnly);
3109 }
3110 r
3111 }
3112}
3113
3114impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
3115 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
3116 flow!(match event {
3117 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
3118 let cx = m.column as i16 - self.inner.x as i16;
3119 let cy = m.row as i16 - self.inner.y as i16;
3120 self.set_screen_cursor((cx, cy), true).into()
3121 }
3122 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
3123 let cx = m.column as i16 - self.inner.x as i16;
3124 let cy = m.row as i16 - self.inner.y as i16;
3125 self.set_screen_cursor_words((cx, cy), true).into()
3126 }
3127 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
3128 if let Some(test) = self.screen_to_pos((m.column, m.row)) {
3129 let start = self.word_start(test);
3130 let end = self.word_end(test);
3131 self.set_selection(start, end).into()
3132 } else {
3133 TextOutcome::Unchanged
3134 }
3135 }
3136 ct_event!(mouse down Left for column,row) => {
3137 if self.inner.contains((*column, *row).into()) {
3138 let cx = (column - self.inner.x) as i16;
3139 let cy = (row - self.inner.y) as i16;
3140 self.set_screen_cursor((cx, cy), false).into()
3141 } else {
3142 TextOutcome::Continue
3143 }
3144 }
3145 ct_event!(mouse down SHIFT-Left for column,row) => {
3146 if self.inner.contains((*column, *row).into()) {
3147 let cx = (column - self.inner.x) as i16;
3148 let cy = (row - self.inner.y) as i16;
3149 self.set_screen_cursor((cx, cy), true).into()
3150 } else {
3151 TextOutcome::Continue
3152 }
3153 }
3154 ct_event!(mouse down CONTROL-Left for column,row) => {
3155 if self.inner.contains((*column, *row).into()) {
3156 let cx = (column - self.inner.x) as i16;
3157 let cy = (row - self.inner.y) as i16;
3158 self.set_screen_cursor((cx, cy), true).into()
3159 } else {
3160 TextOutcome::Continue
3161 }
3162 }
3163 ct_event!(mouse down ALT-Left for column,row) => {
3164 if self.inner.contains((*column, *row).into()) {
3165 let cx = (column - self.inner.x) as i16;
3166 let cy = (row - self.inner.y) as i16;
3167 self.set_screen_cursor_words((cx, cy), true).into()
3168 } else {
3169 TextOutcome::Continue
3170 }
3171 }
3172 _ => TextOutcome::Continue,
3173 });
3174
3175 let mut sas = ScrollAreaState::new()
3176 .area(self.inner)
3177 .h_scroll(&mut self.hscroll)
3178 .v_scroll(&mut self.vscroll);
3179 let r = match sas.handle(event, MouseOnly) {
3180 ScrollOutcome::Up(v) => self.scroll_up(v),
3181 ScrollOutcome::Down(v) => self.scroll_down(v),
3182 ScrollOutcome::Left(v) => self.scroll_left(v),
3183 ScrollOutcome::Right(v) => self.scroll_right(v),
3184 ScrollOutcome::VPos(v) => self.set_vertical_offset(v),
3185 ScrollOutcome::HPos(v) => self.set_horizontal_offset(v),
3186 _ => false,
3187 };
3188 if r {
3189 return TextOutcome::Changed;
3190 }
3191
3192 TextOutcome::Continue
3193 }
3194}
3195
3196pub fn handle_events(
3200 state: &mut TextAreaState,
3201 focus: bool,
3202 event: &crossterm::event::Event,
3203) -> TextOutcome {
3204 state.focus.set(focus);
3205 state.handle(event, Regular)
3206}
3207
3208pub fn handle_readonly_events(
3212 state: &mut TextAreaState,
3213 focus: bool,
3214 event: &crossterm::event::Event,
3215) -> TextOutcome {
3216 state.focus.set(focus);
3217 state.handle(event, ReadOnly)
3218}
3219
3220pub fn handle_mouse_events(
3222 state: &mut TextAreaState,
3223 event: &crossterm::event::Event,
3224) -> TextOutcome {
3225 state.handle(event, MouseOnly)
3226}