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, Regular, ct_event, flow};
23use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
24use rat_reloc::{RelocatableState, relocate_area, relocate_dark_offset, relocate_pos_tuple};
25use rat_scrolled::event::ScrollOutcome;
26use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState};
27use ratatui::buffer::Buffer;
28use ratatui::layout::{Rect, Size};
29use ratatui::style::{Style, Stylize};
30use ratatui::widgets::{Block, StatefulWidget};
31use ropey::Rope;
32use std::borrow::Cow;
33use std::cmp::{max, min};
34use std::collections::HashMap;
35use std::ops::Range;
36
37#[derive(Debug, Default, Clone)]
77pub struct TextArea<'a> {
78 block: Option<Block<'a>>,
79 hscroll: Option<Scroll<'a>>,
80 h_max_offset: Option<usize>,
81 h_overscroll: Option<usize>,
82 vscroll: Option<Scroll<'a>>,
83
84 text_wrap: Option<TextWrap>,
85
86 style: Style,
87 focus_style: Option<Style>,
88 select_style: Option<Style>,
89 text_style: HashMap<usize, Style>,
90}
91
92#[derive(Debug)]
94pub struct TextAreaState {
95 pub area: Rect,
98 pub inner: Rect,
101 pub rendered: Size,
105 pub screen_cursor: Option<(u16, u16)>,
107
108 pub hscroll: ScrollState,
112 pub vscroll: ScrollState,
115 pub sub_row_offset: upos_type,
120 pub dark_offset: (u16, u16),
123 pub scroll_to_cursor: bool,
129
130 pub value: TextCore<TextRope>,
132
133 pub move_col: Option<i16>,
141 pub auto_indent: bool,
143 pub auto_quote: bool,
145 pub text_wrap: TextWrap,
147
148 pub focus: FocusFlag,
150
151 pub mouse: MouseFlags,
154
155 pub non_exhaustive: NonExhaustive,
156}
157
158impl Clone for TextAreaState {
159 fn clone(&self) -> Self {
160 Self {
161 area: self.area,
162 inner: self.inner,
163 rendered: self.rendered,
164 screen_cursor: self.screen_cursor,
165 hscroll: self.hscroll.clone(),
166 vscroll: self.vscroll.clone(),
167 sub_row_offset: self.sub_row_offset,
168 dark_offset: self.dark_offset,
169 scroll_to_cursor: self.scroll_to_cursor,
170 value: self.value.clone(),
171 move_col: None,
172 auto_indent: self.auto_indent,
173 auto_quote: self.auto_quote,
174 text_wrap: self.text_wrap,
175 focus: FocusFlag::named(self.focus.name()),
176 mouse: Default::default(),
177 non_exhaustive: NonExhaustive,
178 }
179 }
180}
181
182#[derive(Debug, Default, Clone, Copy)]
184#[non_exhaustive]
185pub enum TextWrap {
186 #[default]
188 Shift,
189 Hard,
191 Word(u16),
201}
202
203impl<'a> TextArea<'a> {
204 pub fn new() -> Self {
206 Self::default()
207 }
208
209 #[inline]
211 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
212 if let Some(styles) = styles {
213 self.styles(styles)
214 } else {
215 self
216 }
217 }
218
219 #[inline]
221 pub fn styles(mut self, styles: TextStyle) -> Self {
222 self.style = styles.style;
223 if styles.focus.is_some() {
224 self.focus_style = styles.focus;
225 }
226 if styles.select.is_some() {
227 self.select_style = styles.select;
228 }
229 if let Some(border_style) = styles.border_style {
230 self.block = self.block.map(|v| v.border_style(border_style));
231 }
232 self.block = self.block.map(|v| v.style(self.style));
233 if styles.block.is_some() {
234 self.block = styles.block;
235 }
236 if let Some(styles) = styles.scroll {
237 self.hscroll = self.hscroll.map(|v| v.styles(styles.clone()));
238 self.vscroll = self.vscroll.map(|v| v.styles(styles));
239 }
240 self
241 }
242
243 pub fn style(mut self, style: Style) -> Self {
245 self.style = style;
246 self
247 }
248
249 pub fn focus_style(mut self, style: Style) -> Self {
251 self.focus_style = Some(style);
252 self.block = self.block.map(|v| v.style(self.style));
253 self
254 }
255
256 pub fn select_style(mut self, style: Style) -> Self {
258 self.select_style = Some(style);
259 self
260 }
261
262 pub fn text_style_idx(mut self, idx: usize, style: Style) -> Self {
267 self.text_style.insert(idx, style);
268 self
269 }
270
271 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
276 for (i, s) in styles.into_iter().enumerate() {
277 self.text_style.insert(i, s);
278 }
279 self
280 }
281
282 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
287 for (i, s) in styles.into_iter() {
288 self.text_style.insert(i, s.into());
289 }
290 self
291 }
292
293 #[inline]
295 pub fn block(mut self, block: Block<'a>) -> Self {
296 self.block = Some(block);
297 self
298 }
299
300 pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
302 self.hscroll = Some(scroll.clone().override_horizontal());
303 self.vscroll = Some(scroll.override_vertical());
304 self
305 }
306
307 pub fn hscroll(mut self, scroll: Scroll<'a>) -> Self {
309 self.hscroll = Some(scroll.override_horizontal());
310 self
311 }
312
313 pub fn text_wrap(mut self, wrap: TextWrap) -> Self {
315 self.text_wrap = Some(wrap);
316 self
317 }
318
319 pub fn set_horizontal_max_offset(mut self, offset: usize) -> Self {
336 self.h_max_offset = Some(offset);
337 self
338 }
339
340 pub fn set_horizontal_overscroll(mut self, overscroll: usize) -> Self {
347 self.h_overscroll = Some(overscroll);
348 self
349 }
350
351 pub fn vscroll(mut self, scroll: Scroll<'a>) -> Self {
353 self.vscroll = Some(scroll.override_vertical());
354 self
355 }
356}
357
358impl<'a> StatefulWidget for &TextArea<'a> {
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
366impl StatefulWidget for TextArea<'_> {
367 type State = TextAreaState;
368
369 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
370 render_text_area(&self, area, buf, state);
371 }
372}
373
374fn render_text_area(
375 widget: &TextArea<'_>,
376 area: Rect,
377 buf: &mut Buffer,
378 state: &mut TextAreaState,
379) {
380 state.area = area;
381 state.screen_cursor = None;
382 if let Some(text_wrap) = widget.text_wrap {
383 state.text_wrap = text_wrap;
384 }
385
386 let style = widget.style;
387 let focus_style = if let Some(focus_style) = widget.focus_style {
388 focus_style
389 } else {
390 style
391 };
392 let select_style = if let Some(select_style) = widget.select_style {
393 select_style
394 } else {
395 Style::default().black().on_yellow()
396 };
397 let (style, select_style) = if state.is_focused() {
398 (
399 style.patch(focus_style),
400 style.patch(focus_style).patch(select_style),
401 )
402 } else {
403 (style, style.patch(select_style))
404 };
405
406 state.area = area;
408 state.screen_cursor = None;
409 state.inner = ScrollArea::new()
410 .block(widget.block.as_ref())
411 .h_scroll(widget.hscroll.as_ref())
412 .v_scroll(widget.vscroll.as_ref())
413 .inner(area, Some(&state.hscroll), Some(&state.vscroll));
414 state.rendered = state.inner.as_size();
415
416 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
417 state.hscroll.set_max_offset(0);
418 state.hscroll.set_overscroll_by(None);
419 } else {
420 if let Some(h_max_offset) = widget.h_max_offset {
421 state.hscroll.set_max_offset(h_max_offset);
422 }
423 if let Some(h_overscroll) = widget.h_overscroll {
424 state.hscroll.set_overscroll_by(Some(h_overscroll));
425 }
426 }
427 state.hscroll.set_page_len(state.inner.width as usize);
428
429 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
430 state
431 .vscroll
432 .set_max_offset(state.len_lines().saturating_sub(1) as usize);
433 } else {
434 state.vscroll.set_max_offset(
435 state
436 .len_lines()
437 .saturating_sub(state.inner.height as upos_type) as usize,
438 );
439 }
440 state.vscroll.set_page_len(state.inner.height as usize);
441
442 if state.scroll_to_cursor {
443 state.scroll_to_cursor();
444 }
445
446 ScrollArea::new()
448 .block(widget.block.as_ref())
449 .h_scroll(widget.hscroll.as_ref())
450 .v_scroll(widget.vscroll.as_ref())
451 .style(style)
452 .render(
453 area,
454 buf,
455 &mut ScrollAreaState::new()
456 .h_scroll(&mut state.hscroll)
457 .v_scroll(&mut state.vscroll),
458 );
459
460 if state.inner.width == 0 || state.inner.height == 0 {
461 return;
463 }
464 if state.vscroll.offset() > state.value.len_lines() as usize {
465 return;
467 }
468
469 let (shift_left, sub_row_offset, start_row) = state.clean_offset();
470 let page_rows = start_row
471 ..min(
472 start_row + state.inner.height as upos_type,
473 state.value.len_lines(),
474 );
475 let page_bytes = state
476 .try_bytes_at_range(TextRange::new(
477 (sub_row_offset, page_rows.start),
478 (0, page_rows.end),
479 ))
480 .expect("valid_rows");
481 let selection = state.selection();
483 let mut styles = Vec::new();
484
485 for g in state
486 .glyphs2(shift_left, sub_row_offset, page_rows)
487 .expect("valid_offset")
488 {
489 let screen_pos = g.screen_pos();
491
492 if screen_pos.1 >= state.inner.height {
493 break;
494 }
495
496 if g.screen_width() > 0 {
497 let mut style = style;
498 state
500 .value
501 .styles_at_page(g.text_bytes().start, page_bytes.clone(), &mut styles);
502 for style_nr in &styles {
503 if let Some(s) = widget.text_style.get(style_nr) {
504 style = style.patch(*s);
505 }
506 }
507 if selection.contains_pos(g.pos()) {
509 style = style.patch(select_style);
510 };
511
512 if let Some(cell) =
514 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
515 {
516 cell.set_symbol(g.glyph());
517 cell.set_style(style);
518 }
519 for d in 1..g.screen_width() {
521 if let Some(cell) = buf.cell_mut((
522 state.inner.x + screen_pos.0 + d,
523 state.inner.y + screen_pos.1,
524 )) {
525 cell.reset();
526 cell.set_style(style);
527 }
528 }
529 }
530 }
531
532 state.screen_cursor = state.pos_to_screen(state.cursor());
533 }
535
536impl Default for TextAreaState {
537 fn default() -> Self {
538 let mut s = Self {
539 area: Default::default(),
540 inner: Default::default(),
541 rendered: Default::default(),
542 screen_cursor: Default::default(),
543 hscroll: Default::default(),
544 vscroll: Default::default(),
545 sub_row_offset: 0,
546 dark_offset: Default::default(),
547 scroll_to_cursor: Default::default(),
548 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
549 move_col: Default::default(),
550 auto_indent: true,
551 auto_quote: true,
552 text_wrap: TextWrap::Shift,
553 focus: Default::default(),
554 mouse: Default::default(),
555 non_exhaustive: NonExhaustive,
556 };
557 s.hscroll.set_max_offset(255);
558 s.hscroll.set_overscroll_by(Some(16384));
559 s
560 }
561}
562
563impl HasFocus for TextAreaState {
564 fn build(&self, builder: &mut FocusBuilder) {
565 builder.leaf_widget(self);
566 }
567
568 fn focus(&self) -> FocusFlag {
569 self.focus.clone()
570 }
571
572 fn area(&self) -> Rect {
573 self.area
574 }
575
576 fn navigable(&self) -> Navigation {
577 Navigation::Reach
578 }
579}
580
581impl TextAreaState {
582 #[inline]
584 pub fn new() -> Self {
585 Self::default()
586 }
587
588 #[inline]
590 pub fn named(name: &str) -> Self {
591 Self {
592 focus: FocusFlag::named(name),
593 ..Default::default()
594 }
595 }
596
597 #[inline]
603 pub fn set_newline(&mut self, br: impl Into<String>) {
604 self.value.set_newline(br.into());
605 }
606
607 #[inline]
609 pub fn newline(&self) -> &str {
610 self.value.newline()
611 }
612
613 #[inline]
615 pub fn set_auto_indent(&mut self, indent: bool) {
616 self.auto_indent = indent;
617 }
618
619 #[inline]
621 pub fn set_auto_quote(&mut self, quote: bool) {
622 self.auto_quote = quote;
623 }
624
625 #[inline]
627 pub fn set_tab_width(&mut self, tabs: u16) {
628 self.value.set_tab_width(tabs);
629 }
630
631 #[inline]
633 pub fn tab_width(&self) -> u16 {
634 self.value.tab_width()
635 }
636
637 #[inline]
639 pub fn set_expand_tabs(&mut self, expand: bool) {
640 self.value.set_expand_tabs(expand);
641 }
642
643 #[inline]
645 pub fn expand_tabs(&self) -> bool {
646 self.value.expand_tabs()
647 }
648
649 #[inline]
651 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
652 self.value.set_glyph_ctrl(show_ctrl);
653 }
654
655 pub fn show_ctrl(&self) -> bool {
657 self.value.glyph_ctrl()
658 }
659
660 #[inline]
663 pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
664 self.value.set_wrap_ctrl(wrap_ctrl);
665 }
666
667 pub fn wrap_ctrl(&self) -> bool {
670 self.value.wrap_ctrl()
671 }
672
673 pub fn set_text_wrap(&mut self, text_wrap: TextWrap) {
686 self.text_wrap = text_wrap;
687 }
688
689 pub fn text_wrap(&self) -> TextWrap {
691 self.text_wrap
692 }
693
694 #[inline]
704 pub fn set_move_col(&mut self, col: Option<i16>) {
705 self.move_col = col;
706 }
707
708 #[inline]
710 pub fn move_col(&mut self) -> Option<i16> {
711 self.move_col
712 }
713}
714
715impl TextAreaState {
716 #[inline]
719 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
720 match clip {
721 None => self.value.set_clipboard(None),
722 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
723 }
724 }
725
726 #[inline]
729 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
730 self.value.clipboard()
731 }
732
733 #[inline]
735 pub fn copy_to_clip(&mut self) -> bool {
736 let Some(clip) = self.value.clipboard() else {
737 return false;
738 };
739
740 _ = clip.set_string(self.selected_text().as_ref());
741 false
742 }
743
744 #[inline]
746 pub fn cut_to_clip(&mut self) -> bool {
747 let Some(clip) = self.value.clipboard() else {
748 return false;
749 };
750
751 match clip.set_string(self.selected_text().as_ref()) {
752 Ok(_) => self.delete_range(self.selection()),
753 Err(_) => false,
754 }
755 }
756
757 #[inline]
759 pub fn paste_from_clip(&mut self) -> bool {
760 let Some(clip) = self.value.clipboard() else {
761 return false;
762 };
763
764 if let Ok(text) = clip.get_string() {
765 self.insert_str(text)
766 } else {
767 false
768 }
769 }
770}
771
772impl TextAreaState {
773 #[inline]
780 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
781 match undo {
782 None => self.value.set_undo_buffer(None),
783 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
784 }
785 }
786
787 #[inline]
789 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
790 self.value.undo_buffer()
791 }
792
793 #[inline]
795 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
796 self.value.undo_buffer_mut()
797 }
798
799 #[inline]
805 pub fn begin_undo_seq(&mut self) {
806 self.value.begin_undo_seq()
807 }
808
809 #[inline]
811 pub fn end_undo_seq(&mut self) {
812 self.value.end_undo_seq()
813 }
814
815 #[inline]
820 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
821 self.value.recent_replay_log()
822 }
823
824 #[inline]
828 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
829 self.value.replay_log(replay)
830 }
831
832 #[inline]
834 pub fn undo(&mut self) -> bool {
835 self.value.undo()
836 }
837
838 #[inline]
840 pub fn redo(&mut self) -> bool {
841 self.value.redo()
842 }
843}
844
845impl TextAreaState {
846 #[inline]
863 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
864 self.value.set_styles(styles);
865 }
866
867 #[inline]
878 pub fn set_range_styles(&mut self, styles: Vec<(TextRange, usize)>) -> Result<(), TextError> {
879 self.value.set_range_styles(styles)
880 }
881
882 #[inline]
887 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
888 self.value.add_style(range, style);
889 }
890
891 #[inline]
895 pub fn add_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
896 let r = self.value.bytes_at_range(range)?;
897 self.value.add_style(r, style);
898 Ok(())
899 }
900
901 #[inline]
903 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
904 self.value.remove_style(range, style);
905 }
906
907 #[inline]
909 pub fn remove_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
910 let r = self.value.bytes_at_range(range)?;
911 self.value.remove_style(r, style);
912 Ok(())
913 }
914
915 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
917 self.value.styles_in(range, buf)
918 }
919
920 #[inline]
922 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
923 self.value.styles_at(byte_pos, buf)
924 }
925
926 #[inline]
929 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
930 self.value.style_match(byte_pos, style)
931 }
932
933 #[inline]
935 pub fn styles(&self) -> impl Iterator<Item = (Range<usize>, usize)> + '_ {
936 self.value.styles().expect("styles")
937 }
938}
939
940impl TextAreaState {
941 #[inline]
943 pub fn offset(&self) -> (usize, usize) {
944 (self.hscroll.offset(), self.vscroll.offset())
945 }
946
947 #[inline]
952 pub fn set_offset(&mut self, offset: (usize, usize)) -> bool {
953 self.scroll_to_cursor = false;
954 let c = self.hscroll.set_offset(offset.0);
955 let r = self.vscroll.set_offset(offset.1);
956 r || c
957 }
958
959 pub fn set_sub_row_offset(&mut self, sub_row_offset: upos_type) -> bool {
971 self.scroll_to_cursor = false;
972 let old = self.sub_row_offset;
973 self.sub_row_offset = sub_row_offset;
974 sub_row_offset != old
975 }
976
977 pub fn sub_row_offset(&self) -> upos_type {
982 self.sub_row_offset
983 }
984
985 fn clean_offset(&self) -> (upos_type, upos_type, upos_type) {
990 let ox = self.hscroll.offset as upos_type;
991 let mut oy = self.vscroll.offset as upos_type;
992
993 if oy >= self.len_lines() {
995 oy = 0;
996 }
997
998 match self.text_wrap {
999 TextWrap::Shift => (ox, 0, oy),
1000 TextWrap::Hard | TextWrap::Word(_) => {
1001 if let Ok(max_col) = self.try_line_width(oy) {
1003 (0, min(self.sub_row_offset, max_col), oy)
1004 } else {
1005 (0, 0, oy)
1006 }
1007 }
1008 }
1009 }
1010
1011 #[inline]
1013 pub fn cursor(&self) -> TextPosition {
1014 self.value.cursor()
1015 }
1016
1017 #[inline]
1019 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
1020 self.scroll_cursor_to_visible();
1021 self.value.set_cursor(cursor.into(), extend_selection)
1022 }
1023
1024 #[inline]
1026 pub fn anchor(&self) -> TextPosition {
1027 self.value.anchor()
1028 }
1029
1030 #[inline]
1032 pub fn has_selection(&self) -> bool {
1033 self.value.has_selection()
1034 }
1035
1036 #[inline]
1038 pub fn selection(&self) -> TextRange {
1039 self.value.selection()
1040 }
1041
1042 #[inline]
1045 pub fn set_selection(
1046 &mut self,
1047 anchor: impl Into<TextPosition>,
1048 cursor: impl Into<TextPosition>,
1049 ) -> bool {
1050 self.scroll_cursor_to_visible();
1051 self.value.set_selection(anchor.into(), cursor.into())
1052 }
1053
1054 #[inline]
1057 pub fn select_all(&mut self) -> bool {
1058 self.scroll_cursor_to_visible();
1059 self.value.select_all()
1060 }
1061
1062 #[inline]
1064 pub fn selected_text(&self) -> Cow<'_, str> {
1065 self.value
1066 .str_slice(self.value.selection())
1067 .expect("valid_selection")
1068 }
1069}
1070
1071impl TextAreaState {
1072 #[inline]
1074 pub fn is_empty(&self) -> bool {
1075 self.value.is_empty()
1076 }
1077
1078 #[inline]
1080 pub fn rope(&self) -> &Rope {
1081 self.value.text().rope()
1082 }
1083
1084 #[inline]
1086 pub fn text(&self) -> String {
1087 self.value.text().string()
1088 }
1089
1090 #[inline]
1094 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1095 self.value.str_slice_byte(range).expect("valid_range")
1096 }
1097
1098 #[inline]
1100 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1101 self.value.str_slice_byte(range)
1102 }
1103
1104 #[inline]
1108 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
1109 self.value.str_slice(range.into()).expect("valid_range")
1110 }
1111
1112 #[inline]
1114 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
1115 self.value.str_slice(range.into())
1116 }
1117
1118 #[inline]
1120 pub fn len_lines(&self) -> upos_type {
1121 self.value.len_lines()
1122 }
1123
1124 #[inline]
1128 pub fn line_width(&self, row: upos_type) -> upos_type {
1129 self.try_line_width(row).expect("valid_row")
1130 }
1131
1132 #[inline]
1134 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1135 self.value.line_width(row)
1136 }
1137
1138 #[inline]
1143 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
1144 self.value.line_at(row).expect("valid_row")
1145 }
1146
1147 #[inline]
1150 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1151 self.value.line_at(row)
1152 }
1153
1154 #[inline]
1158 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
1159 self.value.lines_at(row).expect("valid_row")
1160 }
1161
1162 #[inline]
1164 pub fn try_lines_at(
1165 &self,
1166 row: upos_type,
1167 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1168 self.value.lines_at(row)
1169 }
1170
1171 #[inline]
1174 #[allow(deprecated)]
1175 #[deprecated(since = "1.1.0", note = "discontinued api")]
1176 pub fn glyphs(
1177 &self,
1178 rows: Range<upos_type>,
1179 screen_offset: u16,
1180 screen_width: u16,
1181 ) -> impl Iterator<Item = Glyph<'_>> {
1182 self.value
1183 .glyphs(rows, screen_offset, screen_width)
1184 .expect("valid_rows")
1185 }
1186
1187 #[inline]
1190 #[allow(deprecated)]
1191 #[deprecated(since = "1.1.0", note = "discontinued api")]
1192 pub fn try_glyphs(
1193 &self,
1194 rows: Range<upos_type>,
1195 screen_offset: u16,
1196 screen_width: u16,
1197 ) -> Result<impl Iterator<Item = Glyph<'_>>, TextError> {
1198 self.value.glyphs(rows, screen_offset, screen_width)
1199 }
1200
1201 #[inline]
1206 pub fn line_graphemes(&self, row: upos_type) -> impl Iterator<Item = Grapheme<'_>> {
1207 self.value.line_graphemes(row).expect("valid_row")
1208 }
1209
1210 #[inline]
1213 pub fn try_line_graphemes(
1214 &self,
1215 row: upos_type,
1216 ) -> Result<impl Iterator<Item = Grapheme<'_>>, TextError> {
1217 self.value.line_graphemes(row)
1218 }
1219
1220 #[inline]
1224 pub fn text_graphemes(&self, pos: TextPosition) -> impl Cursor<Item = Grapheme<'_>> {
1225 self.value.text_graphemes(pos).expect("valid_pos")
1226 }
1227
1228 #[inline]
1230 pub fn try_text_graphemes(
1231 &self,
1232 pos: TextPosition,
1233 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1234 self.value.text_graphemes(pos)
1235 }
1236
1237 #[inline]
1241 pub fn graphemes(
1242 &self,
1243 range: TextRange,
1244 pos: TextPosition,
1245 ) -> impl Cursor<Item = Grapheme<'_>> {
1246 self.value.graphemes(range, pos).expect("valid_args")
1247 }
1248
1249 #[inline]
1251 pub fn try_graphemes(
1252 &self,
1253 range: TextRange,
1254 pos: TextPosition,
1255 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1256 self.value.graphemes(range, pos)
1257 }
1258
1259 #[inline]
1264 pub fn byte_at(&self, pos: TextPosition) -> Range<usize> {
1265 self.value.byte_at(pos).expect("valid_pos")
1266 }
1267
1268 #[inline]
1271 pub fn try_byte_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError> {
1272 self.value.byte_at(pos)
1273 }
1274
1275 #[inline]
1279 pub fn bytes_at_range(&self, range: TextRange) -> Range<usize> {
1280 self.value.bytes_at_range(range).expect("valid_range")
1281 }
1282
1283 #[inline]
1285 pub fn try_bytes_at_range(&self, range: TextRange) -> Result<Range<usize>, TextError> {
1286 self.value.bytes_at_range(range)
1287 }
1288
1289 #[inline]
1294 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1295 self.value.byte_pos(byte).expect("valid_pos")
1296 }
1297
1298 #[inline]
1301 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1302 self.value.byte_pos(byte)
1303 }
1304
1305 #[inline]
1309 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1310 self.value.byte_range(bytes).expect("valid_range")
1311 }
1312
1313 #[inline]
1315 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1316 self.value.byte_range(bytes)
1317 }
1318}
1319
1320impl TextAreaState {
1321 #[inline]
1323 pub fn clear(&mut self) -> bool {
1324 if !self.is_empty() {
1325 self.value.clear();
1326 true
1327 } else {
1328 false
1329 }
1330 }
1331
1332 #[inline]
1336 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1337 self.scroll_to_cursor = false;
1338 self.vscroll.set_offset(0);
1339 self.hscroll.set_offset(0);
1340 self.set_sub_row_offset(0);
1341 self.set_move_col(None);
1342
1343 self.value.set_text(TextRope::new_text(s.as_ref()));
1344 }
1345
1346 #[inline]
1349 pub fn set_rope(&mut self, r: Rope) {
1350 self.scroll_to_cursor = false;
1351 self.vscroll.set_offset(0);
1352 self.hscroll.set_offset(0);
1353 self.set_sub_row_offset(0);
1354 self.set_move_col(None);
1355
1356 self.value.set_text(TextRope::new_rope(r));
1357 }
1358
1359 pub fn insert_char(&mut self, c: char) -> bool {
1370 let mut insert = true;
1371 if self.has_selection() {
1372 if self.auto_quote
1373 && (c == '\''
1374 || c == '"'
1375 || c == '`'
1376 || c == '<'
1377 || c == '['
1378 || c == '('
1379 || c == '{')
1380 {
1381 self.value
1382 .insert_quotes(self.selection(), c)
1383 .expect("valid_selection");
1384 insert = false;
1385 } else {
1386 self.value
1387 .remove_str_range(self.selection())
1388 .expect("valid_selection");
1389 }
1390 }
1391
1392 if insert {
1393 if c == '\n' {
1394 self.value
1395 .insert_newline(self.cursor())
1396 .expect("valid_cursor");
1397 } else if c == '\t' {
1398 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1399 } else {
1400 self.value
1401 .insert_char(self.cursor(), c)
1402 .expect("valid_cursor");
1403 }
1404 }
1405
1406 self.scroll_cursor_to_visible();
1407
1408 true
1409 }
1410
1411 pub fn insert_tab(&mut self) -> bool {
1417 if self.has_selection() {
1418 if self.auto_indent {
1419 let sel = self.selection();
1420 let indent = " ".repeat(self.tab_width() as usize);
1421
1422 self.value.begin_undo_seq();
1423 for r in sel.start.y..=sel.end.y {
1424 self.value
1425 .insert_str(TextPosition::new(0, r), &indent)
1426 .expect("valid_row");
1427 }
1428 self.value.end_undo_seq();
1429
1430 true
1431 } else {
1432 false
1433 }
1434 } else {
1435 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1436 self.scroll_cursor_to_visible();
1437
1438 true
1439 }
1440 }
1441
1442 pub fn insert_backtab(&mut self) -> bool {
1447 if self.has_selection() {
1448 let sel = self.selection();
1449
1450 self.value.begin_undo_seq();
1451 for r in sel.start.y..=sel.end.y {
1452 let mut idx = 0;
1453 let g_it = self
1454 .value
1455 .graphemes(TextRange::new((0, r), (0, r + 1)), TextPosition::new(0, r))
1456 .expect("valid_range")
1457 .take(self.tab_width() as usize);
1458 for g in g_it {
1459 if g != " " && g != "\t" {
1460 break;
1461 }
1462 idx += 1;
1463 }
1464
1465 self.value
1466 .remove_str_range(TextRange::new((0, r), (idx, r)))
1467 .expect("valid_range");
1468 }
1469 self.value.end_undo_seq();
1470
1471 true
1472 } else {
1473 false
1474 }
1475 }
1476
1477 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1480 let t = t.as_ref();
1481 if self.has_selection() {
1482 self.value
1483 .remove_str_range(self.selection())
1484 .expect("valid_selection");
1485 }
1486 self.value
1487 .insert_str(self.cursor(), t)
1488 .expect("valid_cursor");
1489 self.scroll_cursor_to_visible();
1490 true
1491 }
1492
1493 pub fn insert_newline(&mut self) -> bool {
1498 if self.has_selection() {
1499 self.value
1500 .remove_str_range(self.selection())
1501 .expect("valid_selection");
1502 }
1503 self.value
1504 .insert_newline(self.cursor())
1505 .expect("valid_cursor");
1506
1507 if self.auto_indent {
1509 let cursor = self.cursor();
1510 if cursor.y > 0 {
1511 let mut blanks = String::new();
1512 for g in self.line_graphemes(cursor.y - 1) {
1513 if g == " " || g == "\t" {
1514 blanks.push_str(g.grapheme());
1515 } else {
1516 break;
1517 }
1518 }
1519 if !blanks.is_empty() {
1520 self.value
1521 .insert_str(cursor, &blanks)
1522 .expect("valid_cursor");
1523 }
1524 }
1525 }
1526
1527 self.scroll_cursor_to_visible();
1528 true
1529 }
1530
1531 #[inline]
1535 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1536 self.try_delete_range(range).expect("valid_range")
1537 }
1538
1539 #[inline]
1541 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1542 let range = range.into();
1543 if !range.is_empty() {
1544 self.value.remove_str_range(range)?;
1545 self.scroll_cursor_to_visible();
1546 Ok(true)
1547 } else {
1548 Ok(false)
1549 }
1550 }
1551}
1552
1553impl TextAreaState {
1554 pub fn duplicate_text(&mut self) -> bool {
1557 if self.has_selection() {
1558 let sel_range = self.selection();
1559 if !sel_range.is_empty() {
1560 let v = self.str_slice(sel_range).to_string();
1561 self.value
1562 .insert_str(sel_range.end, &v)
1563 .expect("valid_selection");
1564 true
1565 } else {
1566 false
1567 }
1568 } else {
1569 let pos = self.cursor();
1570 let row_range = TextRange::new((0, pos.y), (0, pos.y + 1));
1571 let v = self.str_slice(row_range).to_string();
1572 self.value
1573 .insert_str(row_range.start, &v)
1574 .expect("valid_cursor");
1575 true
1576 }
1577 }
1578
1579 pub fn delete_line(&mut self) -> bool {
1582 let pos = self.cursor();
1583 if pos.y + 1 < self.len_lines() {
1584 self.delete_range(TextRange::new((0, pos.y), (0, pos.y + 1)))
1585 } else {
1586 let width = self.line_width(pos.y);
1587 self.delete_range(TextRange::new((0, pos.y), (width, pos.y)))
1588 }
1589 }
1590
1591 pub fn delete_next_char(&mut self) -> bool {
1594 if self.has_selection() {
1595 self.delete_range(self.selection())
1596 } else {
1597 let r = self
1598 .value
1599 .remove_next_char(self.cursor())
1600 .expect("valid_cursor");
1601 self.scroll_cursor_to_visible();
1602 r
1603 }
1604 }
1605
1606 pub fn delete_prev_char(&mut self) -> bool {
1609 if self.has_selection() {
1610 self.delete_range(self.selection())
1611 } else {
1612 let r = self
1613 .value
1614 .remove_prev_char(self.cursor())
1615 .expect("valid_cursor");
1616 self.scroll_cursor_to_visible();
1617 r
1618 }
1619 }
1620
1621 #[inline]
1626 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1627 self.value.next_word_start(pos.into()).expect("valid_pos")
1628 }
1629
1630 #[inline]
1633 pub fn try_next_word_start(
1634 &self,
1635 pos: impl Into<TextPosition>,
1636 ) -> Result<TextPosition, TextError> {
1637 self.value.next_word_start(pos.into())
1638 }
1639
1640 #[inline]
1645 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1646 self.value.next_word_end(pos.into()).expect("valid_pos")
1647 }
1648
1649 #[inline]
1652 pub fn try_next_word_end(
1653 &self,
1654 pos: impl Into<TextPosition>,
1655 ) -> Result<TextPosition, TextError> {
1656 self.value.next_word_end(pos.into())
1657 }
1658
1659 #[inline]
1667 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1668 self.value.prev_word_start(pos.into()).expect("valid_pos")
1669 }
1670
1671 #[inline]
1677 pub fn try_prev_word_start(
1678 &self,
1679 pos: impl Into<TextPosition>,
1680 ) -> Result<TextPosition, TextError> {
1681 self.value.prev_word_start(pos.into())
1682 }
1683
1684 #[inline]
1690 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1691 self.value.prev_word_end(pos.into()).expect("valid_pos")
1692 }
1693
1694 #[inline]
1698 pub fn try_prev_word_end(
1699 &self,
1700 pos: impl Into<TextPosition>,
1701 ) -> Result<TextPosition, TextError> {
1702 self.value.prev_word_end(pos.into())
1703 }
1704
1705 #[inline]
1709 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1710 self.value.is_word_boundary(pos.into()).expect("valid_pos")
1711 }
1712
1713 #[inline]
1715 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1716 self.value.is_word_boundary(pos.into())
1717 }
1718
1719 #[inline]
1724 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1725 self.value.word_start(pos.into()).expect("valid_pos")
1726 }
1727
1728 #[inline]
1731 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1732 self.value.word_start(pos.into())
1733 }
1734
1735 #[inline]
1740 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1741 self.value.word_end(pos.into()).expect("valid_pos")
1742 }
1743
1744 #[inline]
1747 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1748 self.value.word_end(pos.into())
1749 }
1750
1751 pub fn delete_next_word(&mut self) -> bool {
1756 if self.has_selection() {
1757 self.delete_range(self.selection())
1758 } else {
1759 let cursor = self.cursor();
1760
1761 let start = self.next_word_start(cursor);
1762 if start != cursor {
1763 self.delete_range(cursor..start)
1764 } else {
1765 let end = self.next_word_end(cursor);
1766 self.delete_range(cursor..end)
1767 }
1768 }
1769 }
1770
1771 pub fn delete_prev_word(&mut self) -> bool {
1776 if self.has_selection() {
1777 self.delete_range(self.selection())
1778 } else {
1779 let cursor = self.cursor();
1780
1781 let till_line_start = if cursor.x != 0 {
1783 self.graphemes(TextRange::new((0, cursor.y), cursor), cursor)
1784 .rev_cursor()
1785 .all(|v| v.is_whitespace())
1786 } else {
1787 false
1788 };
1789
1790 if till_line_start {
1791 self.delete_range(TextRange::new((0, cursor.y), cursor))
1792 } else {
1793 let end = self.prev_word_end(cursor);
1794 if end != cursor {
1795 self.delete_range(end..cursor)
1796 } else {
1797 let start = self.prev_word_start(cursor);
1798 self.delete_range(start..cursor)
1799 }
1800 }
1801 }
1802 }
1803
1804 pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1807 let mut cursor = self.cursor();
1808 if cursor.x == 0 {
1809 if cursor.y > 0 {
1810 cursor.y = cursor.y.saturating_sub(1);
1811 cursor.x = self.line_width(cursor.y);
1812 }
1813 } else {
1814 cursor.x = cursor.x.saturating_sub(n as upos_type);
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
1821 self.set_cursor(cursor, extend_selection)
1822 }
1823
1824 pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1827 let mut cursor = self.cursor();
1828 let c_line_width = self.line_width(cursor.y);
1829 if cursor.x == c_line_width {
1830 if cursor.y + 1 < self.len_lines() {
1831 cursor.y += 1;
1832 cursor.x = 0;
1833 }
1834 } else {
1835 cursor.x = min(cursor.x + n as upos_type, c_line_width)
1836 }
1837
1838 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
1839 self.set_move_col(Some(scr_cursor.0));
1840 }
1841 self.set_cursor(cursor, extend_selection)
1842 }
1843
1844 pub fn move_up(&mut self, n: u16, extend_selection: bool) -> bool {
1847 let cursor = self.cursor();
1848 if let Some(mut scr_cursor) = self.pos_to_relative_screen(cursor) {
1849 if let Some(move_col) = self.move_col() {
1850 scr_cursor.0 = move_col;
1851 }
1852 scr_cursor.1 -= n as i16;
1853
1854 if let Some(new_cursor) = self.relative_screen_to_pos(scr_cursor) {
1855 self.set_cursor(new_cursor, extend_selection)
1856 } else {
1857 self.scroll_cursor_to_visible();
1858 true
1859 }
1860 } else {
1861 self.scroll_cursor_to_visible();
1862 true
1863 }
1864 }
1865
1866 pub fn move_down(&mut self, n: u16, extend_selection: bool) -> bool {
1869 let cursor = self.cursor();
1870 if let Some(mut scr_cursor) = self.pos_to_relative_screen(cursor) {
1871 if let Some(move_col) = self.move_col() {
1872 scr_cursor.0 = move_col;
1873 }
1874 scr_cursor.1 += n as i16;
1875
1876 if let Some(new_cursor) = self.relative_screen_to_pos(scr_cursor) {
1877 self.set_cursor(new_cursor, extend_selection)
1878 } else {
1879 self.scroll_cursor_to_visible();
1880 true
1881 }
1882 } else {
1883 self.scroll_cursor_to_visible();
1884 true
1885 }
1886 }
1887
1888 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1892 let cursor = self.cursor();
1893
1894 let mut line_start = self.pos_to_line_start(cursor);
1895 for g in self
1896 .glyphs2(
1897 0,
1898 line_start.x,
1899 line_start.y..min(line_start.y + 1, self.len_lines()),
1900 )
1901 .expect("valid-pos")
1902 {
1903 if g.glyph() != " " && g.glyph() != "\t" {
1904 if g.pos().x != cursor.x {
1905 line_start.x = g.pos().x;
1906 }
1907 break;
1908 }
1909 }
1910
1911 if let Some(scr_pos) = self.pos_to_relative_screen(line_start) {
1912 self.set_move_col(Some(scr_pos.0));
1913 }
1914 self.set_cursor(line_start, extend_selection)
1915 }
1916
1917 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1921 let cursor = self.cursor();
1922 let line_end = self.pos_to_line_end(cursor);
1923 if let Some(scr_pos) = self.pos_to_relative_screen(line_end) {
1924 self.set_move_col(Some(scr_pos.0));
1925 }
1926 self.set_cursor(line_end, extend_selection)
1927 }
1928
1929 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1931 let cursor = TextPosition::new(0, 0);
1932
1933 self.set_move_col(Some(0));
1934 self.set_cursor(cursor, extend_selection)
1935 }
1936
1937 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1939 let cursor = TextPosition::new(
1940 self.line_width(self.len_lines().saturating_sub(1)),
1941 self.len_lines().saturating_sub(1),
1942 );
1943
1944 let line_start = self.pos_to_line_start(cursor);
1945 self.set_move_col(Some(0));
1946 self.set_cursor(line_start, extend_selection)
1947 }
1948
1949 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1951 let (ox, oy) = self.offset();
1952
1953 let cursor = TextPosition::new(ox as upos_type, oy as upos_type);
1954
1955 self.set_move_col(Some(0));
1956 self.set_cursor(cursor, extend_selection)
1957 }
1958
1959 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1961 let scr_end = (0, (self.inner.height as i16).saturating_sub(1));
1962 if let Some(pos) = self.relative_screen_to_pos(scr_end) {
1963 self.set_move_col(Some(0));
1964 self.set_cursor(pos, extend_selection)
1965 } else {
1966 self.scroll_cursor_to_visible();
1967 true
1968 }
1969 }
1970
1971 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1973 let cursor = self.cursor();
1974
1975 let word = self.next_word_end(cursor);
1976
1977 if let Some(scr_pos) = self.pos_to_relative_screen(word) {
1978 self.set_move_col(Some(scr_pos.0));
1979 }
1980 self.set_cursor(word, extend_selection)
1981 }
1982
1983 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1985 let cursor = self.cursor();
1986
1987 let word = self.prev_word_start(cursor);
1988
1989 if let Some(scr_pos) = self.pos_to_relative_screen(word) {
1990 self.set_move_col(Some(scr_pos.0));
1991 }
1992 self.set_cursor(word, extend_selection)
1993 }
1994}
1995
1996impl HasScreenCursor for TextAreaState {
1997 #[allow(clippy::question_mark)]
1999 fn screen_cursor(&self) -> Option<(u16, u16)> {
2000 if self.is_focused() {
2001 if self.has_selection() {
2002 None
2003 } else {
2004 let Some(scr_cursor) = self.screen_cursor else {
2005 return None;
2006 };
2007
2008 if !(scr_cursor.0 >= self.inner.x
2009 && scr_cursor.0 <= self.inner.right()
2010 && scr_cursor.1 >= self.inner.y
2011 && scr_cursor.1 < self.inner.bottom())
2012 {
2013 return None;
2014 }
2015 Some(scr_cursor)
2016 }
2017 } else {
2018 None
2019 }
2020 }
2021}
2022
2023impl RelocatableState for TextAreaState {
2024 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
2025 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
2027 self.area = relocate_area(self.area, shift, clip);
2028 self.inner = relocate_area(self.inner, shift, clip);
2029 if let Some(screen_cursor) = self.screen_cursor {
2030 self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
2031 }
2032 }
2033}
2034
2035impl TextAreaState {
2036 fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
2037 match self.text_wrap {
2038 TextWrap::Shift => (
2039 TextWrap2::Shift,
2040 shift_left,
2041 shift_left + self.rendered.width as upos_type,
2042 shift_left + self.rendered.width as upos_type,
2043 ),
2044 TextWrap::Hard => (
2045 TextWrap2::Hard,
2046 0,
2047 self.rendered.width as upos_type,
2048 self.rendered.width as upos_type,
2049 ),
2050 TextWrap::Word(margin) => (
2051 TextWrap2::Word,
2052 0,
2053 self.rendered.width as upos_type,
2054 self.rendered.width.saturating_sub(margin) as upos_type,
2055 ),
2056 }
2057 }
2058
2059 fn fill_cache(
2062 &self,
2063 shift_left: upos_type,
2064 sub_row_offset: upos_type,
2065 rows: Range<upos_type>,
2066 ) -> Result<(), TextError> {
2067 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
2068 self.value.fill_cache(
2069 self.rendered,
2070 sub_row_offset,
2071 rows,
2072 text_wrap,
2073 self.wrap_ctrl() | self.show_ctrl(),
2074 left_margin,
2075 right_margin,
2076 word_margin,
2077 )
2078 }
2079
2080 fn glyphs2(
2081 &self,
2082 shift_left: upos_type,
2083 sub_row_offset: upos_type,
2084 rows: Range<upos_type>,
2085 ) -> Result<GlyphIter2<'_, <TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
2086 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
2087 self.value.glyphs2(
2088 self.rendered,
2089 sub_row_offset,
2090 rows,
2091 text_wrap,
2092 self.wrap_ctrl() | self.show_ctrl(),
2093 left_margin,
2094 right_margin,
2095 word_margin,
2096 )
2097 }
2098
2099 pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
2101 let scr_pos = (
2102 scr_pos.0 as i16 - self.inner.x as i16,
2103 scr_pos.1 as i16 - self.inner.y as i16,
2104 );
2105 self.relative_screen_to_pos(scr_pos)
2106 }
2107
2108 pub fn pos_to_screen(&self, pos: TextPosition) -> Option<(u16, u16)> {
2110 let scr_pos = self.pos_to_relative_screen(pos)?;
2111 if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
2112 Some((
2113 (scr_pos.0 + self.inner.x as i16) as u16,
2114 (scr_pos.1 + self.inner.y as i16) as u16,
2115 ))
2116 } else {
2117 None
2118 }
2119 }
2120
2121 pub fn pos_to_line_start(&self, pos: TextPosition) -> TextPosition {
2123 match self.text_wrap {
2124 TextWrap::Shift => {
2125 TextPosition::new(0, pos.y)
2127 }
2128 TextWrap::Hard | TextWrap::Word(_) => {
2129 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2130 .expect("valid-row");
2131
2132 let mut start_pos = TextPosition::new(0, pos.y);
2133 for (break_pos, _) in self.value.cache().line_break.borrow().range(
2134 TextPosition::new(0, pos.y)
2135 ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
2136 ) {
2137 if pos >= start_pos && &pos <= break_pos {
2138 break;
2139 }
2140 start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2141 }
2142
2143 start_pos
2144 }
2145 }
2146 }
2147
2148 pub fn pos_to_line_end(&self, pos: TextPosition) -> TextPosition {
2150 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2151 .expect("valid-row");
2152
2153 let mut end_pos = TextPosition::new(0, pos.y);
2154 for (break_pos, _) in self
2155 .value
2156 .cache()
2157 .line_break
2158 .borrow()
2159 .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2160 {
2161 if pos >= end_pos && &pos <= break_pos {
2162 end_pos = *break_pos;
2163 break;
2164 }
2165 end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2166 }
2167
2168 end_pos
2169 }
2170
2171 fn scroll_to_cursor(&mut self) {
2172 let pos = self.cursor();
2173
2174 match self.text_wrap {
2175 TextWrap::Shift => {
2176 let (ox, _, oy) = self.clean_offset();
2177
2178 let height = self.rendered.height as upos_type;
2179 let width = self.rendered.width as upos_type;
2180 let width = if self.show_ctrl() || self.wrap_ctrl() {
2181 width.saturating_sub(1)
2182 } else {
2183 width
2184 };
2185
2186 let noy = if pos.y < oy.saturating_sub(height) {
2187 pos.y.saturating_sub(height * 4 / 10)
2188 } else if pos.y < oy {
2189 pos.y
2190 } else if pos.y >= oy + 2 * height {
2191 pos.y.saturating_sub(height * 6 / 10)
2192 } else if pos.y >= oy + height {
2193 pos.y.saturating_sub(height.saturating_sub(1))
2194 } else {
2195 oy
2196 };
2197
2198 let nox = if pos.x < ox {
2199 pos.x
2200 } else if pos.x >= ox + width {
2201 pos.x.saturating_sub(width) + 1
2202 } else {
2203 ox
2204 };
2205
2206 self.set_offset((nox as usize, noy as usize));
2207 self.set_sub_row_offset(0);
2208 }
2209 TextWrap::Hard | TextWrap::Word(_) => {
2210 let (_ox, sub_row_offset, oy) = self.clean_offset();
2211 let page = self.rendered.height as upos_type;
2212
2213 let scr = (0, oy.saturating_sub(page), 3 * page);
2215 self.stc_fill_screen_cache(scr);
2216 if let Some(off_row) =
2217 self.stc_screen_row(scr, TextPosition::new(sub_row_offset, oy))
2218 {
2219 if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2220 if pos_row < off_row && pos_row >= off_row.saturating_sub(page) {
2221 let noff_row = pos_row;
2222 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2223 self.set_offset((0, noy as usize));
2224 self.set_sub_row_offset(nsub_row_offset);
2225 return;
2226 } else if pos_row >= off_row + page && pos_row < off_row + 2 * page {
2227 let noff_row = pos_row.saturating_sub(page.saturating_sub(1));
2228 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2229 self.set_offset((0, noy as usize));
2230 self.set_sub_row_offset(nsub_row_offset);
2231 return;
2232 } else if pos_row >= off_row && pos_row < off_row + page {
2233 return;
2234 }
2235 }
2236 }
2237
2238 let alt_scr = (0, pos.y.saturating_sub(page), 3 * page);
2240 self.stc_fill_screen_cache(alt_scr);
2241 if let Some(alt_scr_row) = self.stc_screen_row(alt_scr, pos) {
2242 let noff_row = alt_scr_row.saturating_sub(page * 4 / 10);
2243 let (nsub_row_offset, noy) = self.stc_sub_row_offset(alt_scr, noff_row);
2244 self.set_offset((0, noy as usize));
2245 self.set_sub_row_offset(nsub_row_offset);
2246 } else {
2247 self.set_offset((0, pos.y as usize));
2248 self.set_sub_row_offset(0);
2249 }
2250 }
2251 }
2252 }
2253
2254 fn stc_fill_screen_cache(&self, scr: (upos_type, upos_type, upos_type)) {
2256 let y2 = scr.1 + scr.2;
2257 self.fill_cache(0, scr.0, scr.1..min(y2, self.len_lines()))
2258 .expect("valid-rows");
2259 }
2260
2261 fn stc_screen_row(
2263 &self,
2264 scr: (upos_type, upos_type, upos_type),
2265 pos: TextPosition,
2266 ) -> Option<upos_type> {
2267 if pos < TextPosition::new(scr.0, scr.1) {
2268 return None;
2269 }
2270
2271 let line_breaks = self.value.cache().line_break.borrow();
2272 let range_start = TextPosition::new(scr.0, scr.1);
2273 let y2 = scr.1 + scr.2;
2274 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
2275
2276 let mut start_pos = TextPosition::new(scr.0, scr.1);
2277 let mut scr_row = 0;
2278 for (_key, cache) in line_breaks.range(range_start..range_end) {
2279 if pos < cache.start_pos {
2280 return Some(scr_row);
2281 }
2282 scr_row += 1;
2283 start_pos = cache.start_pos;
2284 }
2285
2286 if pos == start_pos {
2288 return Some(scr_row);
2289 }
2290
2291 None
2292 }
2293
2294 fn stc_sub_row_offset(
2297 &self,
2298 scr: (upos_type, upos_type, upos_type),
2299 mut scr_row: upos_type,
2300 ) -> (upos_type, upos_type) {
2301 let line_breaks = self.value.cache().line_break.borrow();
2302 let range_start = TextPosition::new(scr.0, scr.1);
2303 let y2 = scr.1 + scr.2;
2304 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
2305
2306 let mut start_pos = (scr.0, scr.1);
2307 for (_key, cache) in line_breaks.range(range_start..range_end) {
2308 if scr_row == 0 {
2309 return start_pos;
2310 }
2311 scr_row -= 1;
2312 start_pos = (cache.start_pos.x, cache.start_pos.y);
2313 }
2314
2315 start_pos
2317 }
2318
2319 #[allow(clippy::explicit_counter_loop)]
2329 pub fn pos_to_relative_screen(&self, pos: TextPosition) -> Option<(i16, i16)> {
2330 match self.text_wrap {
2331 TextWrap::Shift => {
2332 let (ox, _, oy) = self.clean_offset();
2333
2334 if oy > self.len_lines() {
2335 return None;
2336 }
2337 if pos.y < oy {
2338 return None;
2339 }
2340 if pos.y > self.len_lines() {
2341 return None;
2342 }
2343 if pos.y - oy >= self.rendered.height as u32 {
2344 return None;
2345 }
2346
2347 let screen_y = (pos.y - oy) as u16;
2348
2349 let screen_x = 'f: {
2350 for g in self
2351 .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2352 .expect("valid-row")
2353 {
2354 if g.pos().x == pos.x {
2355 break 'f g.screen_pos().0;
2356 } else if g.line_break() {
2357 break 'f g.screen_pos().0;
2358 }
2359 }
2360 0
2362 };
2363 assert!(screen_x <= self.rendered.width);
2364
2365 let scr = Some((
2366 screen_x as i16 - self.dark_offset.0 as i16,
2367 screen_y as i16 - self.dark_offset.1 as i16,
2368 ));
2369 scr
2370 }
2371 TextWrap::Hard | TextWrap::Word(_) => {
2372 let (_, sub_row_offset, oy) = self.clean_offset();
2373
2374 if oy > self.len_lines() {
2375 return None;
2376 }
2377 if pos.y < oy {
2378 return None;
2379 }
2380 if pos.y > self.len_lines() {
2381 return None;
2382 }
2383
2384 let page = self.rendered.height as upos_type;
2385 let scr = (sub_row_offset, oy, page);
2386 self.stc_fill_screen_cache(scr);
2387 let (screen_y, start_pos) = if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2388 if pos_row >= page {
2389 return None;
2391 }
2392 let start_pos = self.stc_sub_row_offset(scr, pos_row);
2393 (pos_row, start_pos)
2394 } else {
2395 return None;
2397 };
2398
2399 let screen_x = 'f: {
2400 for g in self
2401 .glyphs2(
2402 0,
2403 start_pos.0,
2404 start_pos.1..min(start_pos.1 + 1, self.len_lines()),
2405 )
2406 .expect("valid-row")
2407 {
2408 if g.pos().x == pos.x {
2409 break 'f g.screen_pos().0;
2410 } else if g.line_break() {
2411 break 'f g.screen_pos().0;
2412 }
2413 }
2414 0
2416 };
2417 assert!(screen_x <= self.rendered.width);
2418
2419 let scr = (
2420 screen_x as i16 - self.dark_offset.0 as i16,
2421 screen_y as i16 - self.dark_offset.1 as i16,
2422 );
2423 Some(scr)
2424 }
2425 }
2426 }
2427
2428 #[allow(clippy::needless_return)]
2430 pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
2431 let scr_pos = (
2432 scr_pos.0 + self.dark_offset.0 as i16,
2433 scr_pos.1 + self.dark_offset.1 as i16,
2434 );
2435
2436 match self.text_wrap {
2437 TextWrap::Shift => {
2438 let (ox, _, oy) = self.clean_offset();
2439
2440 if oy >= self.len_lines() {
2441 return None;
2442 }
2443
2444 if scr_pos.1 < 0 {
2445 return Some(TextPosition::new(
2447 0,
2448 oy.saturating_add_signed(scr_pos.1 as ipos_type),
2449 ));
2450 } else if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
2451 return Some(TextPosition::new(0, self.len_lines().saturating_sub(1)));
2453 }
2454
2455 let pos_y = oy + scr_pos.1 as upos_type;
2456
2457 if scr_pos.0 < 0 {
2458 return Some(TextPosition::new(
2459 ox.saturating_add_signed(scr_pos.0 as ipos_type),
2460 pos_y,
2461 ));
2462 } else if scr_pos.0 as u16 >= self.rendered.width {
2463 return Some(TextPosition::new(
2464 min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
2465 pos_y,
2466 ));
2467 } else {
2468 let mut start_pos = TextPosition::new(0, pos_y);
2469 for g in self
2470 .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
2471 .expect("valid-position")
2472 {
2473 if g.contains_screen_x(scr_pos.0 as u16) {
2474 return Some(TextPosition::new(g.pos().x, pos_y));
2475 }
2476 start_pos = g.pos();
2477 }
2478 Some(start_pos)
2479 }
2480 }
2481 TextWrap::Hard | TextWrap::Word(_) => {
2482 let (_, sub_row_offset, oy) = self.clean_offset();
2483
2484 if oy >= self.len_lines() {
2485 return None;
2486 }
2487
2488 if scr_pos.1 < 0 {
2489 let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2498
2499 self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2500
2501 let n_start_pos = 'f: {
2502 let line_break = self.value.cache().line_break.borrow();
2503 let start_range = TextPosition::new(0, ry);
2504 let end_range = TextPosition::new(sub_row_offset, oy);
2505
2506 let mut nrows = scr_pos.1.unsigned_abs();
2507 for (_break_pos, cache) in line_break.range(start_range..end_range).rev() {
2508 if nrows == 0 {
2509 break 'f cache.start_pos;
2510 }
2511 nrows -= 1;
2512 }
2513 TextPosition::new(0, ry)
2514 };
2515
2516 if scr_pos.0 < 0 {
2518 return Some(n_start_pos);
2519 }
2520
2521 let min_row = n_start_pos.y;
2522 let max_row = min(n_start_pos.y + 1, self.len_lines());
2523 for g in self
2524 .glyphs2(0, n_start_pos.x, min_row..max_row)
2525 .expect("valid-rows")
2526 {
2527 if g.contains_screen_x(scr_pos.0 as u16) {
2528 return Some(g.pos());
2529 }
2530 }
2531
2532 return Some(n_start_pos);
2534 } else {
2535 let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2536
2537 let n_start_pos = if scr_pos.1 == 0 {
2539 TextPosition::new(sub_row_offset, oy)
2540 } else {
2541 self.fill_cache(
2543 0,
2544 sub_row_offset,
2545 oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2546 )
2547 .expect("valid-rows");
2548
2549 'f: {
2550 let text_range = self.value.cache().line_break.borrow();
2551 let start_range = TextPosition::new(sub_row_offset, oy);
2552 let end_range = TextPosition::new(0, self.len_lines());
2553
2554 let mut nrows = scr_pos.1 - 1;
2555 let mut start_pos = TextPosition::new(sub_row_offset, oy);
2556 for (_break_pos, cache) in text_range.range(start_range..end_range) {
2557 if nrows == 0 {
2558 break 'f cache.start_pos;
2559 }
2560 start_pos = cache.start_pos;
2561 nrows -= 1;
2562 }
2563 start_pos
2564 }
2565 };
2566
2567 let min_row = n_start_pos.y;
2568 let max_row = min(n_start_pos.y + 1, self.len_lines());
2569 for g in self
2570 .glyphs2(0, n_start_pos.x, min_row..max_row)
2571 .expect("valid-rows")
2572 {
2573 if g.contains_screen_x(scr_pos.0) {
2574 return Some(g.pos());
2575 }
2576 }
2577
2578 return Some(n_start_pos);
2580 }
2581 }
2582 }
2583 }
2584}
2585
2586impl TextAreaState {
2587 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2590 pub fn screen_to_row(&self, scy: i16) -> upos_type {
2591 let (_, oy) = self.offset();
2592 let oy = oy as upos_type + self.dark_offset.1 as upos_type;
2593
2594 if scy < 0 {
2595 oy.saturating_sub((scy as ipos_type).unsigned_abs())
2596 } else if scy as u16 >= (self.inner.height + self.dark_offset.1) {
2597 min(oy + scy as upos_type, self.len_lines().saturating_sub(1))
2598 } else {
2599 let scy = oy + scy as upos_type;
2600 let len = self.len_lines();
2601 if scy < len {
2602 scy
2603 } else {
2604 len.saturating_sub(1)
2605 }
2606 }
2607 }
2608
2609 #[allow(deprecated)]
2616 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2617 pub fn screen_to_col(&self, row: upos_type, scx: i16) -> upos_type {
2618 self.try_screen_to_col(row, scx).expect("valid_row")
2619 }
2620
2621 #[allow(deprecated)]
2628 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2629 pub fn try_screen_to_col(&self, row: upos_type, scx: i16) -> Result<upos_type, TextError> {
2630 let (ox, _) = self.offset();
2631
2632 let ox = ox as upos_type + self.dark_offset.0 as upos_type;
2633
2634 if scx < 0 {
2635 Ok(ox.saturating_sub((scx as ipos_type).unsigned_abs()))
2636 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
2637 Ok(min(ox + scx as upos_type, self.line_width(row)))
2638 } else {
2639 let scx = scx as u16;
2640
2641 let line = self.try_glyphs(
2642 row..row + 1,
2643 ox as u16,
2644 self.inner.width + self.dark_offset.0,
2645 )?;
2646
2647 let mut col = ox;
2648 for g in line {
2649 if scx < g.screen_pos().0 + g.screen_width() {
2650 break;
2651 }
2652 col = g.pos().x + 1;
2653 }
2654 Ok(col)
2655 }
2656 }
2657
2658 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2661 pub fn row_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
2662 let pos = pos.into();
2663 let (_, oy) = self.offset();
2664
2665 if pos.y < oy as upos_type {
2666 return None;
2667 }
2668
2669 let screen_y = pos.y - oy as upos_type;
2670
2671 if screen_y >= self.dark_offset.1 as upos_type {
2672 Some(screen_y as u16 - self.dark_offset.1)
2673 } else {
2674 None
2675 }
2676 }
2677
2678 #[allow(deprecated)]
2681 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2682 pub fn col_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
2683 self.try_col_to_screen(pos).expect("valid_pos")
2684 }
2685
2686 #[allow(deprecated)]
2689 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2690 pub fn try_col_to_screen(
2691 &self,
2692 pos: impl Into<TextPosition>,
2693 ) -> Result<Option<u16>, TextError> {
2694 let pos = pos.into();
2695 let (ox, _) = self.offset();
2696
2697 if pos.x < ox as upos_type {
2698 return Ok(None);
2699 }
2700
2701 let line = self.try_glyphs(
2702 pos.y..pos.y + 1,
2703 ox as u16,
2704 self.inner.width + self.dark_offset.0,
2705 )?;
2706 let mut screen_x = 0;
2707 for g in line {
2708 if g.pos().x == pos.x {
2709 break;
2710 }
2711 screen_x = g.screen_pos().0 + g.screen_width();
2712 }
2713
2714 if screen_x >= self.dark_offset.0 {
2715 Ok(Some(screen_x - self.dark_offset.0))
2716 } else {
2717 Ok(None)
2718 }
2719 }
2720
2721 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2727 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2728 return false;
2729 };
2730 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2731 self.set_move_col(Some(scr_cursor.0));
2732 }
2733 self.set_cursor(cursor, extend_selection)
2734 }
2735
2736 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2743 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2744 return false;
2745 };
2746
2747 let anchor = self.anchor();
2748 let cursor = if cursor < anchor {
2749 self.word_start(cursor)
2750 } else {
2751 self.word_end(cursor)
2752 };
2753
2754 if !self.is_word_boundary(anchor) {
2756 if cursor < anchor {
2757 self.set_cursor(self.word_end(anchor), false);
2758 } else {
2759 self.set_cursor(self.word_start(anchor), false);
2760 }
2761 }
2762
2763 self.set_cursor(cursor, extend_selection)
2764 }
2765}
2766
2767impl TextAreaState {
2768 pub fn vertical_max_offset(&self) -> usize {
2773 self.vscroll.max_offset()
2774 }
2775
2776 pub fn vertical_offset(&self) -> usize {
2778 self.vscroll.offset()
2779 }
2780
2781 pub fn vertical_page(&self) -> usize {
2783 self.vscroll.page_len()
2784 }
2785
2786 pub fn vertical_scroll(&self) -> usize {
2788 self.vscroll.scroll_by()
2789 }
2790
2791 pub fn horizontal_max_offset(&self) -> usize {
2800 self.hscroll.max_offset()
2801 }
2802
2803 pub fn horizontal_offset(&self) -> usize {
2805 self.hscroll.offset()
2806 }
2807
2808 pub fn horizontal_page(&self) -> usize {
2810 self.hscroll.page_len()
2811 }
2812
2813 pub fn horizontal_scroll(&self) -> usize {
2815 self.hscroll.scroll_by()
2816 }
2817
2818 pub fn set_vertical_offset(&mut self, row_offset: usize) -> bool {
2825 self.scroll_to_cursor = false;
2826 self.sub_row_offset = 0;
2827 self.vscroll.set_offset(row_offset)
2828 }
2829
2830 pub fn set_horizontal_offset(&mut self, col_offset: usize) -> bool {
2839 self.scroll_to_cursor = false;
2840 self.hscroll.set_offset(col_offset)
2841 }
2842
2843 pub fn scroll_to_row(&mut self, pos: usize) -> bool {
2852 self.scroll_to_cursor = false;
2853
2854 match self.text_wrap {
2855 TextWrap::Shift => self.vscroll.scroll_to_pos(pos),
2856 TextWrap::Hard | TextWrap::Word(_) => {
2857 self.vscroll.set_offset(self.vscroll.limited_offset(pos))
2858 }
2859 }
2860 }
2861
2862 pub fn scroll_to_col(&mut self, pos: usize) -> bool {
2870 self.scroll_to_cursor = false;
2871 self.hscroll.set_offset(pos)
2872 }
2873
2874 pub fn scroll_up(&mut self, delta: usize) -> bool {
2880 if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2881 self.sub_row_offset = pos.x;
2882 self.vscroll.set_offset(pos.y as usize);
2883 true
2884 } else {
2885 false
2886 }
2887 }
2888
2889 pub fn scroll_down(&mut self, delta: usize) -> bool {
2895 if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2896 self.sub_row_offset = pos.x;
2897 self.vscroll.set_offset(pos.y as usize);
2898 true
2899 } else {
2900 false
2901 }
2902 }
2903
2904 pub fn scroll_left(&mut self, delta: usize) -> bool {
2914 self.hscroll
2915 .set_offset(self.hscroll.offset.saturating_add(delta))
2916 }
2917
2918 pub fn scroll_right(&mut self, delta: usize) -> bool {
2928 self.hscroll
2929 .set_offset(self.hscroll.offset.saturating_sub(delta))
2930 }
2931
2932 #[deprecated(since = "1.3.0", note = "not useful as is")]
2933 pub fn scroll_sub_row_offset(&mut self, col: upos_type) -> bool {
2934 if let Ok(max_col) = self.try_line_width(self.offset().1 as upos_type) {
2935 self.sub_row_offset = min(col as upos_type, max_col);
2936 } else {
2937 self.sub_row_offset = 0;
2938 }
2939 true
2940 }
2941}
2942
2943impl TextAreaState {
2944 pub fn scroll_cursor_to_visible(&mut self) {
2950 self.scroll_to_cursor = true;
2951 }
2952}
2953
2954impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2955 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2956 fn tc(r: bool) -> TextOutcome {
2958 if r {
2959 TextOutcome::TextChanged
2960 } else {
2961 TextOutcome::Unchanged
2962 }
2963 }
2964
2965 let mut r = if self.is_focused() {
2966 match event {
2967 ct_event!(key press c)
2968 | ct_event!(key press SHIFT-c)
2969 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2970 ct_event!(keycode press Tab) => {
2971 tc(if !self.focus.gained() {
2973 self.insert_tab()
2974 } else {
2975 false
2976 })
2977 }
2978 ct_event!(keycode press SHIFT-BackTab) => {
2979 tc(if !self.focus.gained() {
2981 self.insert_backtab()
2982 } else {
2983 false
2984 })
2985 }
2986 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2987 ct_event!(keycode press Backspace) => tc(self.delete_prev_char()),
2988 ct_event!(keycode press Delete) => tc(self.delete_next_char()),
2989 ct_event!(keycode press CONTROL-Backspace)
2990 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2991 ct_event!(keycode press CONTROL-Delete) | ct_event!(keycode press ALT-Delete) => {
2992 tc(self.delete_next_word())
2993 }
2994 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2995 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2996 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2997 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2998 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2999 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
3000
3001 ct_event!(key release _)
3002 | ct_event!(key release SHIFT-_)
3003 | ct_event!(key release CONTROL_ALT-_)
3004 | ct_event!(keycode release Tab)
3005 | ct_event!(keycode release Enter)
3006 | ct_event!(keycode release Backspace)
3007 | ct_event!(keycode release Delete)
3008 | ct_event!(keycode release CONTROL-Backspace)
3009 | ct_event!(keycode release ALT-Backspace)
3010 | ct_event!(keycode release CONTROL-Delete)
3011 | ct_event!(key release CONTROL-'x')
3012 | ct_event!(key release CONTROL-'v')
3013 | ct_event!(key release CONTROL-'d')
3014 | ct_event!(key release CONTROL-'y')
3015 | ct_event!(key release CONTROL-'z')
3016 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
3017 _ => TextOutcome::Continue,
3018 }
3019 } else {
3020 TextOutcome::Continue
3021 };
3022 if r == TextOutcome::Continue {
3023 r = self.handle(event, ReadOnly);
3024 }
3025 r
3026 }
3027}
3028
3029impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
3030 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
3031 let mut r = if self.is_focused() {
3032 match event {
3033 ct_event!(keycode press Left) => self.move_left(1, false).into(),
3034 ct_event!(keycode press Right) => self.move_right(1, false).into(),
3035 ct_event!(keycode press Up) => self.move_up(1, false).into(),
3036 ct_event!(keycode press Down) => self.move_down(1, false).into(),
3037 ct_event!(keycode press PageUp) => {
3038 self.move_up(self.vertical_page() as u16, false).into()
3039 }
3040 ct_event!(keycode press PageDown) => {
3041 self.move_down(self.vertical_page() as u16, false).into()
3042 }
3043 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
3044 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
3045 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
3046 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
3047 ct_event!(keycode press CONTROL-Up) => false.into(),
3048 ct_event!(keycode press CONTROL-Down) => false.into(),
3049 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
3050 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
3051 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
3052 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
3053
3054 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
3055 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
3056 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
3057 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
3058 ct_event!(keycode press ALT-PageUp) => {
3059 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
3060 }
3061 ct_event!(keycode press ALT-PageDown) => {
3062 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
3063 }
3064 ct_event!(keycode press ALT_SHIFT-PageUp) => {
3065 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
3066 }
3067 ct_event!(keycode press ALT_SHIFT-PageDown) => {
3068 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
3069 }
3070
3071 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
3072 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
3073 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
3074 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
3075 ct_event!(keycode press SHIFT-PageUp) => {
3076 self.move_up(self.vertical_page() as u16, true).into()
3077 }
3078 ct_event!(keycode press SHIFT-PageDown) => {
3079 self.move_down(self.vertical_page() as u16, true).into()
3080 }
3081 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
3082 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
3083 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
3084 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
3085 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
3086 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
3087 ct_event!(key press CONTROL-'a') => self.select_all().into(),
3088 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
3089
3090 ct_event!(keycode release Left)
3091 | ct_event!(keycode release Right)
3092 | ct_event!(keycode release Up)
3093 | ct_event!(keycode release Down)
3094 | ct_event!(keycode release PageUp)
3095 | ct_event!(keycode release PageDown)
3096 | ct_event!(keycode release Home)
3097 | ct_event!(keycode release End)
3098 | ct_event!(keycode release CONTROL-Left)
3099 | ct_event!(keycode release CONTROL-Right)
3100 | ct_event!(keycode release CONTROL-Up)
3101 | ct_event!(keycode release CONTROL-Down)
3102 | ct_event!(keycode release CONTROL-PageUp)
3103 | ct_event!(keycode release CONTROL-PageDown)
3104 | ct_event!(keycode release CONTROL-Home)
3105 | ct_event!(keycode release CONTROL-End)
3106 | ct_event!(keycode release ALT-Left)
3107 | ct_event!(keycode release ALT-Right)
3108 | ct_event!(keycode release ALT-Up)
3109 | ct_event!(keycode release ALT-Down)
3110 | ct_event!(keycode release ALT-PageUp)
3111 | ct_event!(keycode release ALT-PageDown)
3112 | ct_event!(keycode release ALT_SHIFT-PageUp)
3113 | ct_event!(keycode release ALT_SHIFT-PageDown)
3114 | ct_event!(keycode release SHIFT-Left)
3115 | ct_event!(keycode release SHIFT-Right)
3116 | ct_event!(keycode release SHIFT-Up)
3117 | ct_event!(keycode release SHIFT-Down)
3118 | ct_event!(keycode release SHIFT-PageUp)
3119 | ct_event!(keycode release SHIFT-PageDown)
3120 | ct_event!(keycode release SHIFT-Home)
3121 | ct_event!(keycode release SHIFT-End)
3122 | ct_event!(keycode release CONTROL_SHIFT-Left)
3123 | ct_event!(keycode release CONTROL_SHIFT-Right)
3124 | ct_event!(keycode release CONTROL_SHIFT-Home)
3125 | ct_event!(keycode release CONTROL_SHIFT-End)
3126 | ct_event!(key release CONTROL-'a')
3127 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
3128 _ => TextOutcome::Continue,
3129 }
3130 } else {
3131 TextOutcome::Continue
3132 };
3133
3134 if r == TextOutcome::Continue {
3135 r = self.handle(event, MouseOnly);
3136 }
3137 r
3138 }
3139}
3140
3141impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
3142 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
3143 flow!(match event {
3144 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
3145 let cx = m.column as i16 - self.inner.x as i16;
3146 let cy = m.row as i16 - self.inner.y as i16;
3147 self.set_screen_cursor((cx, cy), true).into()
3148 }
3149 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
3150 let cx = m.column as i16 - self.inner.x as i16;
3151 let cy = m.row as i16 - self.inner.y as i16;
3152 self.set_screen_cursor_words((cx, cy), true).into()
3153 }
3154 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
3155 if let Some(test) = self.screen_to_pos((m.column, m.row)) {
3156 let start = self.word_start(test);
3157 let end = self.word_end(test);
3158 self.set_selection(start, end).into()
3159 } else {
3160 TextOutcome::Unchanged
3161 }
3162 }
3163 ct_event!(mouse down 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((cx, cy), false).into()
3168 } else {
3169 TextOutcome::Continue
3170 }
3171 }
3172 ct_event!(mouse down SHIFT-Left for column,row) => {
3173 if self.inner.contains((*column, *row).into()) {
3174 let cx = (column - self.inner.x) as i16;
3175 let cy = (row - self.inner.y) as i16;
3176 self.set_screen_cursor((cx, cy), true).into()
3177 } else {
3178 TextOutcome::Continue
3179 }
3180 }
3181 ct_event!(mouse down CONTROL-Left for column,row) => {
3182 if self.inner.contains((*column, *row).into()) {
3183 let cx = (column - self.inner.x) as i16;
3184 let cy = (row - self.inner.y) as i16;
3185 self.set_screen_cursor((cx, cy), true).into()
3186 } else {
3187 TextOutcome::Continue
3188 }
3189 }
3190 ct_event!(mouse down ALT-Left for column,row) => {
3191 if self.inner.contains((*column, *row).into()) {
3192 let cx = (column - self.inner.x) as i16;
3193 let cy = (row - self.inner.y) as i16;
3194 self.set_screen_cursor_words((cx, cy), true).into()
3195 } else {
3196 TextOutcome::Continue
3197 }
3198 }
3199 _ => TextOutcome::Continue,
3200 });
3201
3202 let mut sas = ScrollAreaState::new()
3203 .area(self.inner)
3204 .h_scroll(&mut self.hscroll)
3205 .v_scroll(&mut self.vscroll);
3206 let r = match sas.handle(event, MouseOnly) {
3207 ScrollOutcome::Up(v) => self.scroll_up(v),
3208 ScrollOutcome::Down(v) => self.scroll_down(v),
3209 ScrollOutcome::Left(v) => self.scroll_left(v),
3210 ScrollOutcome::Right(v) => self.scroll_right(v),
3211 ScrollOutcome::VPos(v) => self.scroll_to_row(v),
3212 ScrollOutcome::HPos(v) => self.scroll_to_col(v),
3213 _ => false,
3214 };
3215 if r {
3216 return TextOutcome::Changed;
3217 }
3218
3219 TextOutcome::Continue
3220 }
3221}
3222
3223pub fn handle_events(
3227 state: &mut TextAreaState,
3228 focus: bool,
3229 event: &crossterm::event::Event,
3230) -> TextOutcome {
3231 state.focus.set(focus);
3232 state.handle(event, Regular)
3233}
3234
3235pub fn handle_readonly_events(
3239 state: &mut TextAreaState,
3240 focus: bool,
3241 event: &crossterm::event::Event,
3242) -> TextOutcome {
3243 state.focus.set(focus);
3244 state.handle(event, ReadOnly)
3245}
3246
3247pub fn handle_mouse_events(
3249 state: &mut TextAreaState,
3250 event: &crossterm::event::Event,
3251) -> TextOutcome {
3252 state.handle(event, MouseOnly)
3253}