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
432 .vscroll
433 .set_max_offset(state.len_lines().saturating_sub(1) as usize);
434 } else {
435 state.vscroll.set_max_offset(
436 state
437 .len_lines()
438 .saturating_sub(state.inner.height as upos_type) as usize,
439 );
440 }
441 state.vscroll.set_page_len(state.inner.height as usize);
442
443 if state.scroll_to_cursor {
444 state.scroll_to_cursor();
445 }
446
447 ScrollArea::new()
449 .block(widget.block.as_ref())
450 .h_scroll(widget.hscroll.as_ref())
451 .v_scroll(widget.vscroll.as_ref())
452 .style(style)
453 .render(
454 area,
455 buf,
456 &mut ScrollAreaState::new()
457 .h_scroll(&mut state.hscroll)
458 .v_scroll(&mut state.vscroll),
459 );
460
461 if state.inner.width == 0 || state.inner.height == 0 {
462 return;
464 }
465 if state.vscroll.offset() > state.value.len_lines() as usize {
466 return;
468 }
469
470 let (shift_left, sub_row_offset, start_row) = state.clean_offset();
471 let page_rows = start_row
472 ..min(
473 start_row + state.inner.height as upos_type,
474 state.value.len_lines(),
475 );
476 let page_bytes = state
477 .try_bytes_at_range(TextRange::new(
478 (sub_row_offset, page_rows.start),
479 (0, page_rows.end),
480 ))
481 .expect("valid_rows");
482 let selection = state.selection();
484 let mut styles = Vec::new();
485
486 for g in state
487 .glyphs2(shift_left, sub_row_offset, page_rows)
488 .expect("valid_offset")
489 {
490 let screen_pos = g.screen_pos();
492
493 if screen_pos.1 >= state.inner.height {
494 break;
495 }
496
497 if g.screen_width() > 0 {
498 let mut style = style;
499 state
501 .value
502 .styles_at_page(g.text_bytes().start, page_bytes.clone(), &mut styles);
503 for style_nr in &styles {
504 if let Some(s) = widget.text_style.get(style_nr) {
505 style = style.patch(*s);
506 }
507 }
508 if selection.contains_pos(g.pos()) {
510 style = style.patch(select_style);
511 };
512
513 if let Some(cell) =
515 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
516 {
517 cell.set_symbol(g.glyph());
518 cell.set_style(style);
519 }
520 for d in 1..g.screen_width() {
522 if let Some(cell) = buf.cell_mut((
523 state.inner.x + screen_pos.0 + d,
524 state.inner.y + screen_pos.1,
525 )) {
526 cell.reset();
527 cell.set_style(style);
528 }
529 }
530 }
531 }
532
533 state.screen_cursor = state.pos_to_screen(state.cursor());
534 }
536
537impl Default for TextAreaState {
538 fn default() -> Self {
539 let mut s = Self {
540 area: Default::default(),
541 inner: Default::default(),
542 rendered: Default::default(),
543 screen_cursor: Default::default(),
544 hscroll: Default::default(),
545 vscroll: Default::default(),
546 sub_row_offset: 0,
547 dark_offset: Default::default(),
548 scroll_to_cursor: Default::default(),
549 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
550 move_col: Default::default(),
551 auto_indent: true,
552 auto_quote: true,
553 text_wrap: TextWrap::Shift,
554 focus: Default::default(),
555 mouse: Default::default(),
556 non_exhaustive: NonExhaustive,
557 };
558 s.hscroll.set_max_offset(255);
559 s.hscroll.set_overscroll_by(Some(16384));
560 s
561 }
562}
563
564impl HasFocus for TextAreaState {
565 fn build(&self, builder: &mut FocusBuilder) {
566 builder.leaf_widget(self);
567 }
568
569 fn focus(&self) -> FocusFlag {
570 self.focus.clone()
571 }
572
573 fn area(&self) -> Rect {
574 self.area
575 }
576
577 fn navigable(&self) -> Navigation {
578 Navigation::Reach
579 }
580}
581
582impl TextAreaState {
583 #[inline]
585 pub fn new() -> Self {
586 Self::default()
587 }
588
589 #[inline]
591 pub fn named(name: &str) -> Self {
592 Self {
593 focus: FocusFlag::named(name),
594 ..Default::default()
595 }
596 }
597
598 #[inline]
604 pub fn set_newline(&mut self, br: impl Into<String>) {
605 self.value.set_newline(br.into());
606 }
607
608 #[inline]
610 pub fn newline(&self) -> &str {
611 self.value.newline()
612 }
613
614 #[inline]
616 pub fn set_auto_indent(&mut self, indent: bool) {
617 self.auto_indent = indent;
618 }
619
620 #[inline]
622 pub fn set_auto_quote(&mut self, quote: bool) {
623 self.auto_quote = quote;
624 }
625
626 #[inline]
628 pub fn set_tab_width(&mut self, tabs: u16) {
629 self.value.set_tab_width(tabs);
630 }
631
632 #[inline]
634 pub fn tab_width(&self) -> u16 {
635 self.value.tab_width()
636 }
637
638 #[inline]
640 pub fn set_expand_tabs(&mut self, expand: bool) {
641 self.value.set_expand_tabs(expand);
642 }
643
644 #[inline]
646 pub fn expand_tabs(&self) -> bool {
647 self.value.expand_tabs()
648 }
649
650 #[inline]
652 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
653 self.value.set_glyph_ctrl(show_ctrl);
654 }
655
656 pub fn show_ctrl(&self) -> bool {
658 self.value.glyph_ctrl()
659 }
660
661 #[inline]
664 pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
665 self.value.set_wrap_ctrl(wrap_ctrl);
666 }
667
668 pub fn wrap_ctrl(&self) -> bool {
671 self.value.wrap_ctrl()
672 }
673
674 pub fn set_text_wrap(&mut self, text_wrap: TextWrap) {
687 self.text_wrap = text_wrap;
688 }
689
690 pub fn text_wrap(&self) -> TextWrap {
692 self.text_wrap
693 }
694
695 #[inline]
705 pub fn set_move_col(&mut self, col: Option<i16>) {
706 self.move_col = col;
707 }
708
709 #[inline]
711 pub fn move_col(&mut self) -> Option<i16> {
712 self.move_col
713 }
714}
715
716impl TextAreaState {
717 #[inline]
720 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
721 match clip {
722 None => self.value.set_clipboard(None),
723 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
724 }
725 }
726
727 #[inline]
730 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
731 self.value.clipboard()
732 }
733
734 #[inline]
736 pub fn copy_to_clip(&mut self) -> bool {
737 let Some(clip) = self.value.clipboard() else {
738 return false;
739 };
740
741 _ = clip.set_string(self.selected_text().as_ref());
742 false
743 }
744
745 #[inline]
747 pub fn cut_to_clip(&mut self) -> bool {
748 let Some(clip) = self.value.clipboard() else {
749 return false;
750 };
751
752 match clip.set_string(self.selected_text().as_ref()) {
753 Ok(_) => self.delete_range(self.selection()),
754 Err(_) => false,
755 }
756 }
757
758 #[inline]
760 pub fn paste_from_clip(&mut self) -> bool {
761 let Some(clip) = self.value.clipboard() else {
762 return false;
763 };
764
765 if let Ok(text) = clip.get_string() {
766 self.insert_str(text)
767 } else {
768 false
769 }
770 }
771}
772
773impl TextAreaState {
774 #[inline]
781 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
782 match undo {
783 None => self.value.set_undo_buffer(None),
784 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
785 }
786 }
787
788 #[inline]
790 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
791 self.value.undo_buffer()
792 }
793
794 #[inline]
796 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
797 self.value.undo_buffer_mut()
798 }
799
800 #[inline]
806 pub fn begin_undo_seq(&mut self) {
807 self.value.begin_undo_seq()
808 }
809
810 #[inline]
812 pub fn end_undo_seq(&mut self) {
813 self.value.end_undo_seq()
814 }
815
816 #[inline]
821 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
822 self.value.recent_replay_log()
823 }
824
825 #[inline]
829 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
830 self.value.replay_log(replay)
831 }
832
833 #[inline]
835 pub fn undo(&mut self) -> bool {
836 self.value.undo()
837 }
838
839 #[inline]
841 pub fn redo(&mut self) -> bool {
842 self.value.redo()
843 }
844}
845
846impl TextAreaState {
847 #[inline]
864 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
865 self.value.set_styles(styles);
866 }
867
868 #[inline]
879 pub fn set_range_styles(&mut self, styles: Vec<(TextRange, usize)>) -> Result<(), TextError> {
880 self.value.set_range_styles(styles)
881 }
882
883 #[inline]
888 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
889 self.value.add_style(range, style);
890 }
891
892 #[inline]
896 pub fn add_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
897 let r = self.value.bytes_at_range(range)?;
898 self.value.add_style(r, style);
899 Ok(())
900 }
901
902 #[inline]
904 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
905 self.value.remove_style(range, style);
906 }
907
908 #[inline]
910 pub fn remove_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
911 let r = self.value.bytes_at_range(range)?;
912 self.value.remove_style(r, style);
913 Ok(())
914 }
915
916 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
918 self.value.styles_in(range, buf)
919 }
920
921 #[inline]
923 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
924 self.value.styles_at(byte_pos, buf)
925 }
926
927 #[inline]
930 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
931 self.value.style_match(byte_pos, style)
932 }
933
934 #[inline]
936 pub fn styles(&self) -> impl Iterator<Item = (Range<usize>, usize)> + '_ {
937 self.value.styles().expect("styles")
938 }
939}
940
941impl TextAreaState {
942 #[inline]
944 pub fn offset(&self) -> (usize, usize) {
945 (self.hscroll.offset(), self.vscroll.offset())
946 }
947
948 #[inline]
953 pub fn set_offset(&mut self, offset: (usize, usize)) -> bool {
954 self.scroll_to_cursor = false;
955 let c = self.hscroll.set_offset(offset.0);
956 let r = self.vscroll.set_offset(offset.1);
957 r || c
958 }
959
960 pub fn set_sub_row_offset(&mut self, sub_row_offset: upos_type) -> bool {
972 self.scroll_to_cursor = false;
973 let old = self.sub_row_offset;
974 self.sub_row_offset = sub_row_offset;
975 sub_row_offset != old
976 }
977
978 pub fn sub_row_offset(&self) -> upos_type {
983 self.sub_row_offset
984 }
985
986 fn clean_offset(&self) -> (upos_type, upos_type, upos_type) {
991 let ox = self.hscroll.offset as upos_type;
992 let mut oy = self.vscroll.offset as upos_type;
993
994 if oy >= self.len_lines() {
996 oy = 0;
997 }
998
999 match self.text_wrap {
1000 TextWrap::Shift => (ox, 0, oy),
1001 TextWrap::Hard | TextWrap::Word(_) => {
1002 if let Ok(max_col) = self.try_line_width(oy) {
1004 (0, min(self.sub_row_offset, max_col), oy)
1005 } else {
1006 (0, 0, oy)
1007 }
1008 }
1009 }
1010 }
1011
1012 #[inline]
1014 pub fn cursor(&self) -> TextPosition {
1015 self.value.cursor()
1016 }
1017
1018 #[inline]
1020 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
1021 self.scroll_cursor_to_visible();
1022 self.value.set_cursor(cursor.into(), extend_selection)
1023 }
1024
1025 #[inline]
1027 pub fn anchor(&self) -> TextPosition {
1028 self.value.anchor()
1029 }
1030
1031 #[inline]
1033 pub fn has_selection(&self) -> bool {
1034 self.value.has_selection()
1035 }
1036
1037 #[inline]
1039 pub fn selection(&self) -> TextRange {
1040 self.value.selection()
1041 }
1042
1043 #[inline]
1046 pub fn set_selection(
1047 &mut self,
1048 anchor: impl Into<TextPosition>,
1049 cursor: impl Into<TextPosition>,
1050 ) -> bool {
1051 self.scroll_cursor_to_visible();
1052 self.value.set_selection(anchor.into(), cursor.into())
1053 }
1054
1055 #[inline]
1058 pub fn select_all(&mut self) -> bool {
1059 self.scroll_cursor_to_visible();
1060 self.value.select_all()
1061 }
1062
1063 #[inline]
1065 pub fn selected_text(&self) -> Cow<'_, str> {
1066 self.value
1067 .str_slice(self.value.selection())
1068 .expect("valid_selection")
1069 }
1070}
1071
1072impl TextAreaState {
1073 #[inline]
1075 pub fn is_empty(&self) -> bool {
1076 self.value.is_empty()
1077 }
1078
1079 #[inline]
1081 pub fn rope(&self) -> &Rope {
1082 self.value.text().rope()
1083 }
1084
1085 #[inline]
1087 pub fn text(&self) -> String {
1088 self.value.text().string()
1089 }
1090
1091 #[inline]
1095 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1096 self.value.str_slice_byte(range).expect("valid_range")
1097 }
1098
1099 #[inline]
1101 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1102 self.value.str_slice_byte(range)
1103 }
1104
1105 #[inline]
1109 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
1110 self.value.str_slice(range.into()).expect("valid_range")
1111 }
1112
1113 #[inline]
1115 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
1116 self.value.str_slice(range.into())
1117 }
1118
1119 #[inline]
1121 pub fn len_lines(&self) -> upos_type {
1122 self.value.len_lines()
1123 }
1124
1125 #[inline]
1129 pub fn line_width(&self, row: upos_type) -> upos_type {
1130 self.try_line_width(row).expect("valid_row")
1131 }
1132
1133 #[inline]
1135 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1136 self.value.line_width(row)
1137 }
1138
1139 #[inline]
1144 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
1145 self.value.line_at(row).expect("valid_row")
1146 }
1147
1148 #[inline]
1151 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1152 self.value.line_at(row)
1153 }
1154
1155 #[inline]
1159 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
1160 self.value.lines_at(row).expect("valid_row")
1161 }
1162
1163 #[inline]
1165 pub fn try_lines_at(
1166 &self,
1167 row: upos_type,
1168 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1169 self.value.lines_at(row)
1170 }
1171
1172 #[inline]
1175 #[allow(deprecated)]
1176 #[deprecated(since = "1.1.0", note = "discontinued api")]
1177 pub fn glyphs(
1178 &self,
1179 rows: Range<upos_type>,
1180 screen_offset: u16,
1181 screen_width: u16,
1182 ) -> impl Iterator<Item = Glyph<'_>> {
1183 self.value
1184 .glyphs(rows, screen_offset, screen_width)
1185 .expect("valid_rows")
1186 }
1187
1188 #[inline]
1191 #[allow(deprecated)]
1192 #[deprecated(since = "1.1.0", note = "discontinued api")]
1193 pub fn try_glyphs(
1194 &self,
1195 rows: Range<upos_type>,
1196 screen_offset: u16,
1197 screen_width: u16,
1198 ) -> Result<impl Iterator<Item = Glyph<'_>>, TextError> {
1199 self.value.glyphs(rows, screen_offset, screen_width)
1200 }
1201
1202 #[inline]
1207 pub fn line_graphemes(&self, row: upos_type) -> impl Iterator<Item = Grapheme<'_>> {
1208 self.value.line_graphemes(row).expect("valid_row")
1209 }
1210
1211 #[inline]
1214 pub fn try_line_graphemes(
1215 &self,
1216 row: upos_type,
1217 ) -> Result<impl Iterator<Item = Grapheme<'_>>, TextError> {
1218 self.value.line_graphemes(row)
1219 }
1220
1221 #[inline]
1225 pub fn text_graphemes(&self, pos: TextPosition) -> impl Cursor<Item = Grapheme<'_>> {
1226 self.value.text_graphemes(pos).expect("valid_pos")
1227 }
1228
1229 #[inline]
1231 pub fn try_text_graphemes(
1232 &self,
1233 pos: TextPosition,
1234 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1235 self.value.text_graphemes(pos)
1236 }
1237
1238 #[inline]
1242 pub fn graphemes(
1243 &self,
1244 range: TextRange,
1245 pos: TextPosition,
1246 ) -> impl Cursor<Item = Grapheme<'_>> {
1247 self.value.graphemes(range, pos).expect("valid_args")
1248 }
1249
1250 #[inline]
1252 pub fn try_graphemes(
1253 &self,
1254 range: TextRange,
1255 pos: TextPosition,
1256 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1257 self.value.graphemes(range, pos)
1258 }
1259
1260 #[inline]
1265 pub fn byte_at(&self, pos: TextPosition) -> Range<usize> {
1266 self.value.byte_at(pos).expect("valid_pos")
1267 }
1268
1269 #[inline]
1272 pub fn try_byte_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError> {
1273 self.value.byte_at(pos)
1274 }
1275
1276 #[inline]
1280 pub fn bytes_at_range(&self, range: TextRange) -> Range<usize> {
1281 self.value.bytes_at_range(range).expect("valid_range")
1282 }
1283
1284 #[inline]
1286 pub fn try_bytes_at_range(&self, range: TextRange) -> Result<Range<usize>, TextError> {
1287 self.value.bytes_at_range(range)
1288 }
1289
1290 #[inline]
1295 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1296 self.value.byte_pos(byte).expect("valid_pos")
1297 }
1298
1299 #[inline]
1302 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1303 self.value.byte_pos(byte)
1304 }
1305
1306 #[inline]
1310 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1311 self.value.byte_range(bytes).expect("valid_range")
1312 }
1313
1314 #[inline]
1316 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1317 self.value.byte_range(bytes)
1318 }
1319}
1320
1321impl TextAreaState {
1322 #[inline]
1324 pub fn clear(&mut self) -> bool {
1325 if !self.is_empty() {
1326 self.value.clear();
1327 true
1328 } else {
1329 false
1330 }
1331 }
1332
1333 #[inline]
1337 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1338 self.scroll_to_cursor = false;
1339 self.vscroll.set_offset(0);
1340 self.hscroll.set_offset(0);
1341 self.set_sub_row_offset(0);
1342 self.set_move_col(None);
1343
1344 self.value.set_text(TextRope::new_text(s.as_ref()));
1345 }
1346
1347 #[inline]
1350 pub fn set_rope(&mut self, r: Rope) {
1351 self.scroll_to_cursor = false;
1352 self.vscroll.set_offset(0);
1353 self.hscroll.set_offset(0);
1354 self.set_sub_row_offset(0);
1355 self.set_move_col(None);
1356
1357 self.value.set_text(TextRope::new_rope(r));
1358 }
1359
1360 pub fn insert_char(&mut self, c: char) -> bool {
1371 let mut insert = true;
1372 if self.has_selection() {
1373 if self.auto_quote
1374 && (c == '\''
1375 || c == '"'
1376 || c == '`'
1377 || c == '<'
1378 || c == '['
1379 || c == '('
1380 || c == '{')
1381 {
1382 self.value
1383 .insert_quotes(self.selection(), c)
1384 .expect("valid_selection");
1385 insert = false;
1386 } else {
1387 self.value
1388 .remove_str_range(self.selection())
1389 .expect("valid_selection");
1390 }
1391 }
1392
1393 if insert {
1394 if c == '\n' {
1395 self.value
1396 .insert_newline(self.cursor())
1397 .expect("valid_cursor");
1398 } else if c == '\t' {
1399 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1400 } else {
1401 self.value
1402 .insert_char(self.cursor(), c)
1403 .expect("valid_cursor");
1404 }
1405 }
1406
1407 self.scroll_cursor_to_visible();
1408
1409 true
1410 }
1411
1412 pub fn insert_tab(&mut self) -> bool {
1418 if self.has_selection() {
1419 if self.auto_indent {
1420 let sel = self.selection();
1421 let indent = " ".repeat(self.tab_width() as usize);
1422
1423 self.value.begin_undo_seq();
1424 for r in sel.start.y..=sel.end.y {
1425 self.value
1426 .insert_str(TextPosition::new(0, r), &indent)
1427 .expect("valid_row");
1428 }
1429 self.value.end_undo_seq();
1430
1431 true
1432 } else {
1433 false
1434 }
1435 } else {
1436 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1437 self.scroll_cursor_to_visible();
1438
1439 true
1440 }
1441 }
1442
1443 pub fn insert_backtab(&mut self) -> bool {
1448 if self.has_selection() {
1449 let sel = self.selection();
1450
1451 self.value.begin_undo_seq();
1452 for r in sel.start.y..=sel.end.y {
1453 let mut idx = 0;
1454 let g_it = self
1455 .value
1456 .graphemes(TextRange::new((0, r), (0, r + 1)), TextPosition::new(0, r))
1457 .expect("valid_range")
1458 .take(self.tab_width() as usize);
1459 for g in g_it {
1460 if g != " " && g != "\t" {
1461 break;
1462 }
1463 idx += 1;
1464 }
1465
1466 self.value
1467 .remove_str_range(TextRange::new((0, r), (idx, r)))
1468 .expect("valid_range");
1469 }
1470 self.value.end_undo_seq();
1471
1472 true
1473 } else {
1474 false
1475 }
1476 }
1477
1478 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1481 let t = t.as_ref();
1482 if self.has_selection() {
1483 self.value
1484 .remove_str_range(self.selection())
1485 .expect("valid_selection");
1486 }
1487 self.value
1488 .insert_str(self.cursor(), t)
1489 .expect("valid_cursor");
1490 self.scroll_cursor_to_visible();
1491 true
1492 }
1493
1494 pub fn insert_newline(&mut self) -> bool {
1499 if self.has_selection() {
1500 self.value
1501 .remove_str_range(self.selection())
1502 .expect("valid_selection");
1503 }
1504 self.value
1505 .insert_newline(self.cursor())
1506 .expect("valid_cursor");
1507
1508 if self.auto_indent {
1510 let cursor = self.cursor();
1511 if cursor.y > 0 {
1512 let mut blanks = String::new();
1513 for g in self.line_graphemes(cursor.y - 1) {
1514 if g == " " || g == "\t" {
1515 blanks.push_str(g.grapheme());
1516 } else {
1517 break;
1518 }
1519 }
1520 if !blanks.is_empty() {
1521 self.value
1522 .insert_str(cursor, &blanks)
1523 .expect("valid_cursor");
1524 }
1525 }
1526 }
1527
1528 self.scroll_cursor_to_visible();
1529 true
1530 }
1531
1532 #[inline]
1536 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1537 self.try_delete_range(range).expect("valid_range")
1538 }
1539
1540 #[inline]
1542 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1543 let range = range.into();
1544 if !range.is_empty() {
1545 self.value.remove_str_range(range)?;
1546 self.scroll_cursor_to_visible();
1547 Ok(true)
1548 } else {
1549 Ok(false)
1550 }
1551 }
1552}
1553
1554impl TextAreaState {
1555 pub fn duplicate_text(&mut self) -> bool {
1558 if self.has_selection() {
1559 let sel_range = self.selection();
1560 if !sel_range.is_empty() {
1561 let v = self.str_slice(sel_range).to_string();
1562 self.value
1563 .insert_str(sel_range.end, &v)
1564 .expect("valid_selection");
1565 true
1566 } else {
1567 false
1568 }
1569 } else {
1570 let pos = self.cursor();
1571 let row_range = TextRange::new((0, pos.y), (0, pos.y + 1));
1572 let v = self.str_slice(row_range).to_string();
1573 self.value
1574 .insert_str(row_range.start, &v)
1575 .expect("valid_cursor");
1576 true
1577 }
1578 }
1579
1580 pub fn delete_line(&mut self) -> bool {
1583 let pos = self.cursor();
1584 if pos.y + 1 < self.len_lines() {
1585 self.delete_range(TextRange::new((0, pos.y), (0, pos.y + 1)))
1586 } else {
1587 let width = self.line_width(pos.y);
1588 self.delete_range(TextRange::new((0, pos.y), (width, pos.y)))
1589 }
1590 }
1591
1592 pub fn delete_next_char(&mut self) -> bool {
1595 if self.has_selection() {
1596 self.delete_range(self.selection())
1597 } else {
1598 let r = self
1599 .value
1600 .remove_next_char(self.cursor())
1601 .expect("valid_cursor");
1602 self.scroll_cursor_to_visible();
1603 r
1604 }
1605 }
1606
1607 pub fn delete_prev_char(&mut self) -> bool {
1610 if self.has_selection() {
1611 self.delete_range(self.selection())
1612 } else {
1613 let r = self
1614 .value
1615 .remove_prev_char(self.cursor())
1616 .expect("valid_cursor");
1617 self.scroll_cursor_to_visible();
1618 r
1619 }
1620 }
1621
1622 #[inline]
1627 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1628 self.value.next_word_start(pos.into()).expect("valid_pos")
1629 }
1630
1631 #[inline]
1634 pub fn try_next_word_start(
1635 &self,
1636 pos: impl Into<TextPosition>,
1637 ) -> Result<TextPosition, TextError> {
1638 self.value.next_word_start(pos.into())
1639 }
1640
1641 #[inline]
1646 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1647 self.value.next_word_end(pos.into()).expect("valid_pos")
1648 }
1649
1650 #[inline]
1653 pub fn try_next_word_end(
1654 &self,
1655 pos: impl Into<TextPosition>,
1656 ) -> Result<TextPosition, TextError> {
1657 self.value.next_word_end(pos.into())
1658 }
1659
1660 #[inline]
1668 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1669 self.value.prev_word_start(pos.into()).expect("valid_pos")
1670 }
1671
1672 #[inline]
1678 pub fn try_prev_word_start(
1679 &self,
1680 pos: impl Into<TextPosition>,
1681 ) -> Result<TextPosition, TextError> {
1682 self.value.prev_word_start(pos.into())
1683 }
1684
1685 #[inline]
1691 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1692 self.value.prev_word_end(pos.into()).expect("valid_pos")
1693 }
1694
1695 #[inline]
1699 pub fn try_prev_word_end(
1700 &self,
1701 pos: impl Into<TextPosition>,
1702 ) -> Result<TextPosition, TextError> {
1703 self.value.prev_word_end(pos.into())
1704 }
1705
1706 #[inline]
1710 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1711 self.value.is_word_boundary(pos.into()).expect("valid_pos")
1712 }
1713
1714 #[inline]
1716 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1717 self.value.is_word_boundary(pos.into())
1718 }
1719
1720 #[inline]
1725 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1726 self.value.word_start(pos.into()).expect("valid_pos")
1727 }
1728
1729 #[inline]
1732 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1733 self.value.word_start(pos.into())
1734 }
1735
1736 #[inline]
1741 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1742 self.value.word_end(pos.into()).expect("valid_pos")
1743 }
1744
1745 #[inline]
1748 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1749 self.value.word_end(pos.into())
1750 }
1751
1752 pub fn delete_next_word(&mut self) -> bool {
1757 if self.has_selection() {
1758 self.delete_range(self.selection())
1759 } else {
1760 let cursor = self.cursor();
1761
1762 let start = self.next_word_start(cursor);
1763 if start != cursor {
1764 self.delete_range(cursor..start)
1765 } else {
1766 let end = self.next_word_end(cursor);
1767 self.delete_range(cursor..end)
1768 }
1769 }
1770 }
1771
1772 pub fn delete_prev_word(&mut self) -> bool {
1777 if self.has_selection() {
1778 self.delete_range(self.selection())
1779 } else {
1780 let cursor = self.cursor();
1781
1782 let till_line_start = if cursor.x != 0 {
1784 self.graphemes(TextRange::new((0, cursor.y), cursor), cursor)
1785 .rev_cursor()
1786 .all(|v| v.is_whitespace())
1787 } else {
1788 false
1789 };
1790
1791 if till_line_start {
1792 self.delete_range(TextRange::new((0, cursor.y), cursor))
1793 } else {
1794 let end = self.prev_word_end(cursor);
1795 if end != cursor {
1796 self.delete_range(end..cursor)
1797 } else {
1798 let start = self.prev_word_start(cursor);
1799 self.delete_range(start..cursor)
1800 }
1801 }
1802 }
1803 }
1804
1805 pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1808 let mut cursor = self.cursor();
1809 if cursor.x == 0 {
1810 if cursor.y > 0 {
1811 cursor.y = cursor.y.saturating_sub(1);
1812 cursor.x = self.line_width(cursor.y);
1813 }
1814 } else {
1815 cursor.x = cursor.x.saturating_sub(n as upos_type);
1816 }
1817
1818 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
1819 self.set_move_col(Some(scr_cursor.0));
1820 }
1821
1822 self.set_cursor(cursor, extend_selection)
1823 }
1824
1825 pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1828 let mut cursor = self.cursor();
1829 let c_line_width = self.line_width(cursor.y);
1830 if cursor.x == c_line_width {
1831 if cursor.y + 1 < self.len_lines() {
1832 cursor.y += 1;
1833 cursor.x = 0;
1834 }
1835 } else {
1836 cursor.x = min(cursor.x + n as upos_type, c_line_width)
1837 }
1838
1839 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
1840 self.set_move_col(Some(scr_cursor.0));
1841 }
1842 self.set_cursor(cursor, extend_selection)
1843 }
1844
1845 pub fn move_up(&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_down(&mut self, n: u16, extend_selection: bool) -> bool {
1870 let cursor = self.cursor();
1871 if let Some(mut scr_cursor) = self.pos_to_relative_screen(cursor) {
1872 if let Some(move_col) = self.move_col() {
1873 scr_cursor.0 = move_col;
1874 }
1875 scr_cursor.1 += n as i16;
1876
1877 if let Some(new_cursor) = self.relative_screen_to_pos(scr_cursor) {
1878 self.set_cursor(new_cursor, extend_selection)
1879 } else {
1880 self.scroll_cursor_to_visible();
1881 true
1882 }
1883 } else {
1884 self.scroll_cursor_to_visible();
1885 true
1886 }
1887 }
1888
1889 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1893 let cursor = self.cursor();
1894
1895 let mut line_start = self.pos_to_line_start(cursor);
1896 for g in self
1897 .glyphs2(
1898 0,
1899 line_start.x,
1900 line_start.y..min(line_start.y + 1, self.len_lines()),
1901 )
1902 .expect("valid-pos")
1903 {
1904 if g.glyph() != " " && g.glyph() != "\t" {
1905 if g.pos().x != cursor.x {
1906 line_start.x = g.pos().x;
1907 }
1908 break;
1909 }
1910 }
1911
1912 if let Some(scr_pos) = self.pos_to_relative_screen(line_start) {
1913 self.set_move_col(Some(scr_pos.0));
1914 }
1915 self.set_cursor(line_start, extend_selection)
1916 }
1917
1918 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1922 let cursor = self.cursor();
1923 let line_end = self.pos_to_line_end(cursor);
1924 if let Some(scr_pos) = self.pos_to_relative_screen(line_end) {
1925 self.set_move_col(Some(scr_pos.0));
1926 }
1927 self.set_cursor(line_end, extend_selection)
1928 }
1929
1930 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1932 let cursor = TextPosition::new(0, 0);
1933
1934 self.set_move_col(Some(0));
1935 self.set_cursor(cursor, extend_selection)
1936 }
1937
1938 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1940 let cursor = TextPosition::new(
1941 self.line_width(self.len_lines().saturating_sub(1)),
1942 self.len_lines().saturating_sub(1),
1943 );
1944
1945 let line_start = self.pos_to_line_start(cursor);
1946 self.set_move_col(Some(0));
1947 self.set_cursor(line_start, extend_selection)
1948 }
1949
1950 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1952 let (ox, oy) = self.offset();
1953
1954 let cursor = TextPosition::new(ox as upos_type, oy as upos_type);
1955
1956 self.set_move_col(Some(0));
1957 self.set_cursor(cursor, extend_selection)
1958 }
1959
1960 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1962 let scr_end = (0, (self.inner.height as i16).saturating_sub(1));
1963 if let Some(pos) = self.relative_screen_to_pos(scr_end) {
1964 self.set_move_col(Some(0));
1965 self.set_cursor(pos, extend_selection)
1966 } else {
1967 self.scroll_cursor_to_visible();
1968 true
1969 }
1970 }
1971
1972 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1974 let cursor = self.cursor();
1975
1976 let word = self.next_word_end(cursor);
1977
1978 if let Some(scr_pos) = self.pos_to_relative_screen(word) {
1979 self.set_move_col(Some(scr_pos.0));
1980 }
1981 self.set_cursor(word, extend_selection)
1982 }
1983
1984 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1986 let cursor = self.cursor();
1987
1988 let word = self.prev_word_start(cursor);
1989
1990 if let Some(scr_pos) = self.pos_to_relative_screen(word) {
1991 self.set_move_col(Some(scr_pos.0));
1992 }
1993 self.set_cursor(word, extend_selection)
1994 }
1995}
1996
1997impl HasScreenCursor for TextAreaState {
1998 #[allow(clippy::question_mark)]
2000 fn screen_cursor(&self) -> Option<(u16, u16)> {
2001 if self.is_focused() {
2002 if self.has_selection() {
2003 None
2004 } else {
2005 let Some(scr_cursor) = self.screen_cursor else {
2006 return None;
2007 };
2008
2009 if !(scr_cursor.0 >= self.inner.x
2010 && scr_cursor.0 <= self.inner.right()
2011 && scr_cursor.1 >= self.inner.y
2012 && scr_cursor.1 < self.inner.bottom())
2013 {
2014 return None;
2015 }
2016 Some(scr_cursor)
2017 }
2018 } else {
2019 None
2020 }
2021 }
2022}
2023
2024impl RelocatableState for TextAreaState {
2025 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
2026 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
2028 self.area = relocate_area(self.area, shift, clip);
2029 self.inner = relocate_area(self.inner, shift, clip);
2030 if let Some(screen_cursor) = self.screen_cursor {
2031 self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
2032 }
2033 }
2034}
2035
2036impl TextAreaState {
2037 fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
2038 match self.text_wrap {
2039 TextWrap::Shift => (
2040 TextWrap2::Shift,
2041 shift_left,
2042 shift_left + self.rendered.width as upos_type,
2043 shift_left + self.rendered.width as upos_type,
2044 ),
2045 TextWrap::Hard => (
2046 TextWrap2::Hard,
2047 0,
2048 self.rendered.width as upos_type,
2049 self.rendered.width as upos_type,
2050 ),
2051 TextWrap::Word(margin) => (
2052 TextWrap2::Word,
2053 0,
2054 self.rendered.width as upos_type,
2055 self.rendered.width.saturating_sub(margin) as upos_type,
2056 ),
2057 }
2058 }
2059
2060 fn fill_cache(
2063 &self,
2064 shift_left: upos_type,
2065 sub_row_offset: upos_type,
2066 rows: Range<upos_type>,
2067 ) -> Result<(), TextError> {
2068 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
2069 self.value.fill_cache(
2070 self.rendered,
2071 sub_row_offset,
2072 rows,
2073 text_wrap,
2074 self.wrap_ctrl() | self.show_ctrl(),
2075 left_margin,
2076 right_margin,
2077 word_margin,
2078 )
2079 }
2080
2081 fn glyphs2(
2082 &self,
2083 shift_left: upos_type,
2084 sub_row_offset: upos_type,
2085 rows: Range<upos_type>,
2086 ) -> Result<GlyphIter2<'_, <TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
2087 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
2088 self.value.glyphs2(
2089 self.rendered,
2090 sub_row_offset,
2091 rows,
2092 text_wrap,
2093 self.wrap_ctrl() | self.show_ctrl(),
2094 left_margin,
2095 right_margin,
2096 word_margin,
2097 )
2098 }
2099
2100 pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
2102 let scr_pos = (
2103 scr_pos.0 as i16 - self.inner.x as i16,
2104 scr_pos.1 as i16 - self.inner.y as i16,
2105 );
2106 self.relative_screen_to_pos(scr_pos)
2107 }
2108
2109 pub fn pos_to_screen(&self, pos: TextPosition) -> Option<(u16, u16)> {
2111 let scr_pos = self.pos_to_relative_screen(pos)?;
2112 if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
2113 Some((
2114 (scr_pos.0 + self.inner.x as i16) as u16,
2115 (scr_pos.1 + self.inner.y as i16) as u16,
2116 ))
2117 } else {
2118 None
2119 }
2120 }
2121
2122 pub fn pos_to_line_start(&self, pos: TextPosition) -> TextPosition {
2124 match self.text_wrap {
2125 TextWrap::Shift => {
2126 TextPosition::new(0, pos.y)
2128 }
2129 TextWrap::Hard | TextWrap::Word(_) => {
2130 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2131 .expect("valid-row");
2132
2133 let mut start_pos = TextPosition::new(0, pos.y);
2134 for (break_pos, _) in self.value.cache().line_break.borrow().range(
2135 TextPosition::new(0, pos.y)
2136 ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
2137 ) {
2138 if pos >= start_pos && &pos <= break_pos {
2139 break;
2140 }
2141 start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2142 }
2143
2144 start_pos
2145 }
2146 }
2147 }
2148
2149 pub fn pos_to_line_end(&self, pos: TextPosition) -> TextPosition {
2151 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2152 .expect("valid-row");
2153
2154 let mut end_pos = TextPosition::new(0, pos.y);
2155 for (break_pos, _) in self
2156 .value
2157 .cache()
2158 .line_break
2159 .borrow()
2160 .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2161 {
2162 if pos >= end_pos && &pos <= break_pos {
2163 end_pos = *break_pos;
2164 break;
2165 }
2166 end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2167 }
2168
2169 end_pos
2170 }
2171
2172 fn scroll_to_cursor(&mut self) {
2173 let pos = self.cursor();
2174
2175 match self.text_wrap {
2176 TextWrap::Shift => {
2177 let (ox, _, oy) = self.clean_offset();
2178
2179 let height = self.rendered.height as upos_type;
2180 let width = self.rendered.width as upos_type;
2181 let width = if self.show_ctrl() || self.wrap_ctrl() {
2182 width.saturating_sub(1)
2183 } else {
2184 width
2185 };
2186
2187 let noy = if pos.y < oy.saturating_sub(height) {
2188 pos.y.saturating_sub(height * 4 / 10)
2189 } else if pos.y < oy {
2190 pos.y
2191 } else if pos.y >= oy + 2 * height {
2192 pos.y.saturating_sub(height * 6 / 10)
2193 } else if pos.y >= oy + height {
2194 pos.y.saturating_sub(height.saturating_sub(1))
2195 } else {
2196 oy
2197 };
2198
2199 let nox = if pos.x < ox {
2200 pos.x
2201 } else if pos.x >= ox + width {
2202 pos.x.saturating_sub(width) + 1
2203 } else {
2204 ox
2205 };
2206
2207 self.set_offset((nox as usize, noy as usize));
2208 self.set_sub_row_offset(0);
2209 }
2210 TextWrap::Hard | TextWrap::Word(_) => {
2211 let (_ox, sub_row_offset, oy) = self.clean_offset();
2212 let page = self.rendered.height as upos_type;
2213
2214 let scr = (0, oy.saturating_sub(page), 3 * page);
2216 self.stc_fill_screen_cache(scr);
2217 if let Some(off_row) =
2218 self.stc_screen_row(scr, TextPosition::new(sub_row_offset, oy))
2219 {
2220 if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2221 if pos_row < off_row && pos_row >= off_row.saturating_sub(page) {
2222 let noff_row = pos_row;
2223 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2224 self.set_offset((0, noy as usize));
2225 self.set_sub_row_offset(nsub_row_offset);
2226 return;
2227 } else if pos_row >= off_row + page && pos_row < off_row + 2 * page {
2228 let noff_row = pos_row.saturating_sub(page.saturating_sub(1));
2229 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2230 self.set_offset((0, noy as usize));
2231 self.set_sub_row_offset(nsub_row_offset);
2232 return;
2233 } else if pos_row >= off_row && pos_row < off_row + page {
2234 return;
2235 }
2236 }
2237 }
2238
2239 let alt_scr = (0, pos.y.saturating_sub(page), 3 * page);
2241 self.stc_fill_screen_cache(alt_scr);
2242 if let Some(alt_scr_row) = self.stc_screen_row(alt_scr, pos) {
2243 let noff_row = alt_scr_row.saturating_sub(page * 4 / 10);
2244 let (nsub_row_offset, noy) = self.stc_sub_row_offset(alt_scr, noff_row);
2245 self.set_offset((0, noy as usize));
2246 self.set_sub_row_offset(nsub_row_offset);
2247 } else {
2248 self.set_offset((0, pos.y as usize));
2249 self.set_sub_row_offset(0);
2250 }
2251 }
2252 }
2253 }
2254
2255 fn stc_fill_screen_cache(&self, scr: (upos_type, upos_type, upos_type)) {
2257 let y2 = scr.1 + scr.2;
2258 self.fill_cache(0, scr.0, scr.1..min(y2, self.len_lines()))
2259 .expect("valid-rows");
2260 }
2261
2262 fn stc_screen_row(
2264 &self,
2265 scr: (upos_type, upos_type, upos_type),
2266 pos: TextPosition,
2267 ) -> Option<upos_type> {
2268 if pos < TextPosition::new(scr.0, scr.1) {
2269 return None;
2270 }
2271
2272 let line_breaks = self.value.cache().line_break.borrow();
2273 let range_start = TextPosition::new(scr.0, scr.1);
2274 let y2 = scr.1 + scr.2;
2275 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
2276
2277 let mut start_pos = TextPosition::new(scr.0, scr.1);
2278 let mut scr_row = 0;
2279 for (_key, cache) in line_breaks.range(range_start..range_end) {
2280 if pos < cache.start_pos {
2281 return Some(scr_row);
2282 }
2283 scr_row += 1;
2284 start_pos = cache.start_pos;
2285 }
2286
2287 if pos == start_pos {
2289 return Some(scr_row);
2290 }
2291
2292 None
2293 }
2294
2295 fn stc_sub_row_offset(
2298 &self,
2299 scr: (upos_type, upos_type, upos_type),
2300 mut scr_row: upos_type,
2301 ) -> (upos_type, upos_type) {
2302 let line_breaks = self.value.cache().line_break.borrow();
2303 let range_start = TextPosition::new(scr.0, scr.1);
2304 let y2 = scr.1 + scr.2;
2305 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
2306
2307 let mut start_pos = (scr.0, scr.1);
2308 for (_key, cache) in line_breaks.range(range_start..range_end) {
2309 if scr_row == 0 {
2310 return start_pos;
2311 }
2312 scr_row -= 1;
2313 start_pos = (cache.start_pos.x, cache.start_pos.y);
2314 }
2315
2316 start_pos
2318 }
2319
2320 #[allow(clippy::explicit_counter_loop)]
2330 pub fn pos_to_relative_screen(&self, pos: TextPosition) -> Option<(i16, i16)> {
2331 match self.text_wrap {
2332 TextWrap::Shift => {
2333 let (ox, _, oy) = self.clean_offset();
2334
2335 if oy > self.len_lines() {
2336 return None;
2337 }
2338 if pos.y < oy {
2339 return None;
2340 }
2341 if pos.y > self.len_lines() {
2342 return None;
2343 }
2344 if pos.y - oy >= self.rendered.height as u32 {
2345 return None;
2346 }
2347
2348 let screen_y = (pos.y - oy) as u16;
2349
2350 let screen_x = 'f: {
2351 for g in self
2352 .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2353 .expect("valid-row")
2354 {
2355 if g.pos().x == pos.x {
2356 break 'f g.screen_pos().0;
2357 } else if g.line_break() {
2358 break 'f g.screen_pos().0;
2359 }
2360 }
2361 0
2363 };
2364 assert!(screen_x <= self.rendered.width);
2365
2366 let scr = Some((
2367 screen_x as i16 - self.dark_offset.0 as i16,
2368 screen_y as i16 - self.dark_offset.1 as i16,
2369 ));
2370 scr
2371 }
2372 TextWrap::Hard | TextWrap::Word(_) => {
2373 let (_, sub_row_offset, oy) = self.clean_offset();
2374
2375 if oy > self.len_lines() {
2376 return None;
2377 }
2378 if pos.y < oy {
2379 return None;
2380 }
2381 if pos.y > self.len_lines() {
2382 return None;
2383 }
2384
2385 let page = self.rendered.height as upos_type;
2386 let scr = (sub_row_offset, oy, page);
2387 self.stc_fill_screen_cache(scr);
2388 let (screen_y, start_pos) = if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2389 if pos_row >= page {
2390 return None;
2392 }
2393 let start_pos = self.stc_sub_row_offset(scr, pos_row);
2394 (pos_row, start_pos)
2395 } else {
2396 return None;
2398 };
2399
2400 let screen_x = 'f: {
2401 for g in self
2402 .glyphs2(
2403 0,
2404 start_pos.0,
2405 start_pos.1..min(start_pos.1 + 1, self.len_lines()),
2406 )
2407 .expect("valid-row")
2408 {
2409 if g.pos().x == pos.x {
2410 break 'f g.screen_pos().0;
2411 } else if g.line_break() {
2412 break 'f g.screen_pos().0;
2413 }
2414 }
2415 0
2417 };
2418 assert!(screen_x <= self.rendered.width);
2419
2420 let scr = (
2421 screen_x as i16 - self.dark_offset.0 as i16,
2422 screen_y as i16 - self.dark_offset.1 as i16,
2423 );
2424 Some(scr)
2425 }
2426 }
2427 }
2428
2429 #[allow(clippy::needless_return)]
2431 pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
2432 let scr_pos = (
2433 scr_pos.0 + self.dark_offset.0 as i16,
2434 scr_pos.1 + self.dark_offset.1 as i16,
2435 );
2436
2437 match self.text_wrap {
2438 TextWrap::Shift => {
2439 let (ox, _, oy) = self.clean_offset();
2440
2441 if oy >= self.len_lines() {
2442 return None;
2443 }
2444
2445 if scr_pos.1 < 0 {
2446 return Some(TextPosition::new(
2448 0,
2449 oy.saturating_add_signed(scr_pos.1 as ipos_type),
2450 ));
2451 } else if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
2452 return Some(TextPosition::new(0, self.len_lines().saturating_sub(1)));
2454 }
2455
2456 let pos_y = oy + scr_pos.1 as upos_type;
2457
2458 if scr_pos.0 < 0 {
2459 return Some(TextPosition::new(
2460 ox.saturating_add_signed(scr_pos.0 as ipos_type),
2461 pos_y,
2462 ));
2463 } else if scr_pos.0 as u16 >= self.rendered.width {
2464 return Some(TextPosition::new(
2465 min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
2466 pos_y,
2467 ));
2468 } else {
2469 let mut start_pos = TextPosition::new(0, pos_y);
2470 for g in self
2471 .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
2472 .expect("valid-position")
2473 {
2474 if g.contains_screen_x(scr_pos.0 as u16) {
2475 return Some(TextPosition::new(g.pos().x, pos_y));
2476 }
2477 start_pos = g.pos();
2478 }
2479 Some(start_pos)
2480 }
2481 }
2482 TextWrap::Hard | TextWrap::Word(_) => {
2483 let (_, sub_row_offset, oy) = self.clean_offset();
2484
2485 if oy >= self.len_lines() {
2486 return None;
2487 }
2488
2489 if scr_pos.1 < 0 {
2490 let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2499
2500 self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2501
2502 let n_start_pos = 'f: {
2503 let line_break = self.value.cache().line_break.borrow();
2504 let start_range = TextPosition::new(0, ry);
2505 let end_range = TextPosition::new(sub_row_offset, oy);
2506
2507 let mut nrows = scr_pos.1.unsigned_abs();
2508 for (_break_pos, cache) in line_break.range(start_range..end_range).rev() {
2509 if nrows == 0 {
2510 break 'f cache.start_pos;
2511 }
2512 nrows -= 1;
2513 }
2514 TextPosition::new(0, ry)
2515 };
2516
2517 if scr_pos.0 < 0 {
2519 return Some(n_start_pos);
2520 }
2521
2522 let min_row = n_start_pos.y;
2523 let max_row = min(n_start_pos.y + 1, self.len_lines());
2524 for g in self
2525 .glyphs2(0, n_start_pos.x, min_row..max_row)
2526 .expect("valid-rows")
2527 {
2528 if g.contains_screen_x(scr_pos.0 as u16) {
2529 return Some(g.pos());
2530 }
2531 }
2532
2533 return Some(n_start_pos);
2535 } else {
2536 let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2537
2538 let n_start_pos = if scr_pos.1 == 0 {
2540 TextPosition::new(sub_row_offset, oy)
2541 } else {
2542 self.fill_cache(
2544 0,
2545 sub_row_offset,
2546 oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2547 )
2548 .expect("valid-rows");
2549
2550 'f: {
2551 let text_range = self.value.cache().line_break.borrow();
2552 let start_range = TextPosition::new(sub_row_offset, oy);
2553 let end_range = TextPosition::new(0, self.len_lines());
2554
2555 let mut nrows = scr_pos.1 - 1;
2556 let mut start_pos = TextPosition::new(sub_row_offset, oy);
2557 for (_break_pos, cache) in text_range.range(start_range..end_range) {
2558 if nrows == 0 {
2559 break 'f cache.start_pos;
2560 }
2561 start_pos = cache.start_pos;
2562 nrows -= 1;
2563 }
2564 start_pos
2565 }
2566 };
2567
2568 let min_row = n_start_pos.y;
2569 let max_row = min(n_start_pos.y + 1, self.len_lines());
2570 for g in self
2571 .glyphs2(0, n_start_pos.x, min_row..max_row)
2572 .expect("valid-rows")
2573 {
2574 if g.contains_screen_x(scr_pos.0) {
2575 return Some(g.pos());
2576 }
2577 }
2578
2579 return Some(n_start_pos);
2581 }
2582 }
2583 }
2584 }
2585}
2586
2587impl TextAreaState {
2588 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2591 pub fn screen_to_row(&self, scy: i16) -> upos_type {
2592 let (_, oy) = self.offset();
2593 let oy = oy as upos_type + self.dark_offset.1 as upos_type;
2594
2595 if scy < 0 {
2596 oy.saturating_sub((scy as ipos_type).unsigned_abs())
2597 } else if scy as u16 >= (self.inner.height + self.dark_offset.1) {
2598 min(oy + scy as upos_type, self.len_lines().saturating_sub(1))
2599 } else {
2600 let scy = oy + scy as upos_type;
2601 let len = self.len_lines();
2602 if scy < len {
2603 scy
2604 } else {
2605 len.saturating_sub(1)
2606 }
2607 }
2608 }
2609
2610 #[allow(deprecated)]
2617 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2618 pub fn screen_to_col(&self, row: upos_type, scx: i16) -> upos_type {
2619 self.try_screen_to_col(row, scx).expect("valid_row")
2620 }
2621
2622 #[allow(deprecated)]
2629 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2630 pub fn try_screen_to_col(&self, row: upos_type, scx: i16) -> Result<upos_type, TextError> {
2631 let (ox, _) = self.offset();
2632
2633 let ox = ox as upos_type + self.dark_offset.0 as upos_type;
2634
2635 if scx < 0 {
2636 Ok(ox.saturating_sub((scx as ipos_type).unsigned_abs()))
2637 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
2638 Ok(min(ox + scx as upos_type, self.line_width(row)))
2639 } else {
2640 let scx = scx as u16;
2641
2642 let line = self.try_glyphs(
2643 row..row + 1,
2644 ox as u16,
2645 self.inner.width + self.dark_offset.0,
2646 )?;
2647
2648 let mut col = ox;
2649 for g in line {
2650 if scx < g.screen_pos().0 + g.screen_width() {
2651 break;
2652 }
2653 col = g.pos().x + 1;
2654 }
2655 Ok(col)
2656 }
2657 }
2658
2659 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2662 pub fn row_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
2663 let pos = pos.into();
2664 let (_, oy) = self.offset();
2665
2666 if pos.y < oy as upos_type {
2667 return None;
2668 }
2669
2670 let screen_y = pos.y - oy as upos_type;
2671
2672 if screen_y >= self.dark_offset.1 as upos_type {
2673 Some(screen_y as u16 - self.dark_offset.1)
2674 } else {
2675 None
2676 }
2677 }
2678
2679 #[allow(deprecated)]
2682 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2683 pub fn col_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
2684 self.try_col_to_screen(pos).expect("valid_pos")
2685 }
2686
2687 #[allow(deprecated)]
2690 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2691 pub fn try_col_to_screen(
2692 &self,
2693 pos: impl Into<TextPosition>,
2694 ) -> Result<Option<u16>, TextError> {
2695 let pos = pos.into();
2696 let (ox, _) = self.offset();
2697
2698 if pos.x < ox as upos_type {
2699 return Ok(None);
2700 }
2701
2702 let line = self.try_glyphs(
2703 pos.y..pos.y + 1,
2704 ox as u16,
2705 self.inner.width + self.dark_offset.0,
2706 )?;
2707 let mut screen_x = 0;
2708 for g in line {
2709 if g.pos().x == pos.x {
2710 break;
2711 }
2712 screen_x = g.screen_pos().0 + g.screen_width();
2713 }
2714
2715 if screen_x >= self.dark_offset.0 {
2716 Ok(Some(screen_x - self.dark_offset.0))
2717 } else {
2718 Ok(None)
2719 }
2720 }
2721
2722 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2728 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2729 return false;
2730 };
2731 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2732 self.set_move_col(Some(scr_cursor.0));
2733 }
2734 self.set_cursor(cursor, extend_selection)
2735 }
2736
2737 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2744 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2745 return false;
2746 };
2747
2748 let anchor = self.anchor();
2749 let cursor = if cursor < anchor {
2750 self.word_start(cursor)
2751 } else {
2752 self.word_end(cursor)
2753 };
2754
2755 if !self.is_word_boundary(anchor) {
2757 if cursor < anchor {
2758 self.set_cursor(self.word_end(anchor), false);
2759 } else {
2760 self.set_cursor(self.word_start(anchor), false);
2761 }
2762 }
2763
2764 self.set_cursor(cursor, extend_selection)
2765 }
2766}
2767
2768impl TextAreaState {
2769 pub fn vertical_max_offset(&self) -> usize {
2774 self.vscroll.max_offset()
2775 }
2776
2777 pub fn vertical_offset(&self) -> usize {
2779 self.vscroll.offset()
2780 }
2781
2782 pub fn vertical_page(&self) -> usize {
2784 self.vscroll.page_len()
2785 }
2786
2787 pub fn vertical_scroll(&self) -> usize {
2789 self.vscroll.scroll_by()
2790 }
2791
2792 pub fn horizontal_max_offset(&self) -> usize {
2796 self.hscroll.max_offset()
2797 }
2798
2799 pub fn horizontal_offset(&self) -> usize {
2801 self.hscroll.offset()
2802 }
2803
2804 pub fn horizontal_page(&self) -> usize {
2806 self.hscroll.page_len()
2807 }
2808
2809 pub fn horizontal_scroll(&self) -> usize {
2811 self.hscroll.scroll_by()
2812 }
2813
2814 pub fn set_vertical_offset(&mut self, row_offset: usize) -> bool {
2821 self.scroll_to_cursor = false;
2822 self.sub_row_offset = 0;
2823 self.vscroll.set_offset(row_offset)
2824 }
2825
2826 pub fn set_horizontal_offset(&mut self, col_offset: usize) -> bool {
2835 self.scroll_to_cursor = false;
2836 self.hscroll.set_offset(col_offset)
2837 }
2838
2839 pub fn scroll_to_row(&mut self, pos: usize) -> bool {
2841 self.scroll_to_cursor = false;
2842 self.vscroll.set_offset(pos)
2843 }
2844
2845 pub fn scroll_to_col(&mut self, pos: usize) -> bool {
2849 self.scroll_to_cursor = false;
2850 self.hscroll.set_offset(pos)
2851 }
2852
2853 pub fn scroll_up(&mut self, delta: usize) -> bool {
2855 if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2856 self.sub_row_offset = pos.x;
2857 self.vscroll.set_offset(pos.y as usize);
2858 true
2859 } else {
2860 self.sub_row_offset = 0;
2861
2862 let old = self.vscroll.offset;
2865 self.vscroll.offset = min(
2866 self.vscroll.offset.saturating_sub(delta),
2867 self.vscroll
2868 .max_offset
2869 .saturating_add(self.vscroll.page_len())
2870 .saturating_add(self.vscroll.overscroll_by()),
2871 );
2872 old != self.vscroll.offset
2873 }
2874 }
2875
2876 pub fn scroll_down(&mut self, delta: usize) -> bool {
2878 if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2879 self.sub_row_offset = pos.x;
2880 self.vscroll.set_offset(pos.y as usize);
2881 true
2882 } else {
2883 self.sub_row_offset = 0;
2884
2885 let old = self.vscroll.offset;
2888 self.vscroll.offset = min(
2889 self.vscroll.offset.saturating_add(delta),
2890 self.vscroll
2891 .max_offset
2892 .saturating_add(self.vscroll.page_len())
2893 .saturating_add(self.vscroll.overscroll_by()),
2894 );
2895
2896 old != self.vscroll.offset
2897 }
2898 }
2899
2900 pub fn scroll_left(&mut self, delta: usize) -> bool {
2904 self.hscroll.scroll_left(delta)
2905 }
2906
2907 pub fn scroll_right(&mut self, delta: usize) -> bool {
2911 self.hscroll.scroll_right(delta)
2912 }
2913
2914 pub fn scroll_sub_row_offset(&mut self, col: upos_type) -> bool {
2920 if let Ok(max_col) = self.try_line_width(self.offset().1 as upos_type) {
2921 self.sub_row_offset = min(col as upos_type, max_col);
2922 } else {
2923 self.sub_row_offset = 0;
2924 }
2925 true
2926 }
2927}
2928
2929impl TextAreaState {
2930 pub fn scroll_cursor_to_visible(&mut self) {
2933 self.scroll_to_cursor = true;
2934 }
2935}
2936
2937impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2938 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2939 fn tc(r: bool) -> TextOutcome {
2941 if r {
2942 TextOutcome::TextChanged
2943 } else {
2944 TextOutcome::Unchanged
2945 }
2946 }
2947
2948 let mut r = if self.is_focused() {
2949 match event {
2950 ct_event!(key press c)
2951 | ct_event!(key press SHIFT-c)
2952 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2953 ct_event!(keycode press Tab) => {
2954 tc(if !self.focus.gained() {
2956 self.insert_tab()
2957 } else {
2958 false
2959 })
2960 }
2961 ct_event!(keycode press SHIFT-BackTab) => {
2962 tc(if !self.focus.gained() {
2964 self.insert_backtab()
2965 } else {
2966 false
2967 })
2968 }
2969 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2970 ct_event!(keycode press Backspace) => tc(self.delete_prev_char()),
2971 ct_event!(keycode press Delete) => tc(self.delete_next_char()),
2972 ct_event!(keycode press CONTROL-Backspace)
2973 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2974 ct_event!(keycode press CONTROL-Delete) | ct_event!(keycode press ALT-Delete) => {
2975 tc(self.delete_next_word())
2976 }
2977 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2978 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2979 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2980 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2981 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2982 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2983
2984 ct_event!(key release _)
2985 | ct_event!(key release SHIFT-_)
2986 | ct_event!(key release CONTROL_ALT-_)
2987 | ct_event!(keycode release Tab)
2988 | ct_event!(keycode release Enter)
2989 | ct_event!(keycode release Backspace)
2990 | ct_event!(keycode release Delete)
2991 | ct_event!(keycode release CONTROL-Backspace)
2992 | ct_event!(keycode release ALT-Backspace)
2993 | ct_event!(keycode release CONTROL-Delete)
2994 | ct_event!(key release CONTROL-'x')
2995 | ct_event!(key release CONTROL-'v')
2996 | ct_event!(key release CONTROL-'d')
2997 | ct_event!(key release CONTROL-'y')
2998 | ct_event!(key release CONTROL-'z')
2999 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
3000 _ => TextOutcome::Continue,
3001 }
3002 } else {
3003 TextOutcome::Continue
3004 };
3005 if r == TextOutcome::Continue {
3006 r = self.handle(event, ReadOnly);
3007 }
3008 r
3009 }
3010}
3011
3012impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
3013 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
3014 let mut r = if self.is_focused() {
3015 match event {
3016 ct_event!(keycode press Left) => self.move_left(1, false).into(),
3017 ct_event!(keycode press Right) => self.move_right(1, false).into(),
3018 ct_event!(keycode press Up) => self.move_up(1, false).into(),
3019 ct_event!(keycode press Down) => self.move_down(1, false).into(),
3020 ct_event!(keycode press PageUp) => {
3021 self.move_up(self.vertical_page() as u16, false).into()
3022 }
3023 ct_event!(keycode press PageDown) => {
3024 self.move_down(self.vertical_page() as u16, false).into()
3025 }
3026 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
3027 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
3028 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
3029 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
3030 ct_event!(keycode press CONTROL-Up) => false.into(),
3031 ct_event!(keycode press CONTROL-Down) => false.into(),
3032 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
3033 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
3034 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
3035 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
3036
3037 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
3038 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
3039 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
3040 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
3041 ct_event!(keycode press ALT-PageUp) => {
3042 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
3043 }
3044 ct_event!(keycode press ALT-PageDown) => {
3045 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
3046 }
3047 ct_event!(keycode press ALT_SHIFT-PageUp) => {
3048 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
3049 }
3050 ct_event!(keycode press ALT_SHIFT-PageDown) => {
3051 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
3052 }
3053
3054 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
3055 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
3056 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
3057 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
3058 ct_event!(keycode press SHIFT-PageUp) => {
3059 self.move_up(self.vertical_page() as u16, true).into()
3060 }
3061 ct_event!(keycode press SHIFT-PageDown) => {
3062 self.move_down(self.vertical_page() as u16, true).into()
3063 }
3064 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
3065 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
3066 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
3067 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
3068 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
3069 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
3070 ct_event!(key press CONTROL-'a') => self.select_all().into(),
3071 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
3072
3073 ct_event!(keycode release Left)
3074 | ct_event!(keycode release Right)
3075 | ct_event!(keycode release Up)
3076 | ct_event!(keycode release Down)
3077 | ct_event!(keycode release PageUp)
3078 | ct_event!(keycode release PageDown)
3079 | ct_event!(keycode release Home)
3080 | ct_event!(keycode release End)
3081 | ct_event!(keycode release CONTROL-Left)
3082 | ct_event!(keycode release CONTROL-Right)
3083 | ct_event!(keycode release CONTROL-Up)
3084 | ct_event!(keycode release CONTROL-Down)
3085 | ct_event!(keycode release CONTROL-PageUp)
3086 | ct_event!(keycode release CONTROL-PageDown)
3087 | ct_event!(keycode release CONTROL-Home)
3088 | ct_event!(keycode release CONTROL-End)
3089 | ct_event!(keycode release ALT-Left)
3090 | ct_event!(keycode release ALT-Right)
3091 | ct_event!(keycode release ALT-Up)
3092 | ct_event!(keycode release ALT-Down)
3093 | ct_event!(keycode release ALT-PageUp)
3094 | ct_event!(keycode release ALT-PageDown)
3095 | ct_event!(keycode release ALT_SHIFT-PageUp)
3096 | ct_event!(keycode release ALT_SHIFT-PageDown)
3097 | ct_event!(keycode release SHIFT-Left)
3098 | ct_event!(keycode release SHIFT-Right)
3099 | ct_event!(keycode release SHIFT-Up)
3100 | ct_event!(keycode release SHIFT-Down)
3101 | ct_event!(keycode release SHIFT-PageUp)
3102 | ct_event!(keycode release SHIFT-PageDown)
3103 | ct_event!(keycode release SHIFT-Home)
3104 | ct_event!(keycode release SHIFT-End)
3105 | ct_event!(keycode release CONTROL_SHIFT-Left)
3106 | ct_event!(keycode release CONTROL_SHIFT-Right)
3107 | ct_event!(keycode release CONTROL_SHIFT-Home)
3108 | ct_event!(keycode release CONTROL_SHIFT-End)
3109 | ct_event!(key release CONTROL-'a')
3110 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
3111 _ => TextOutcome::Continue,
3112 }
3113 } else {
3114 TextOutcome::Continue
3115 };
3116
3117 if r == TextOutcome::Continue {
3118 r = self.handle(event, MouseOnly);
3119 }
3120 r
3121 }
3122}
3123
3124impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
3125 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
3126 flow!(match event {
3127 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
3128 let cx = m.column as i16 - self.inner.x as i16;
3129 let cy = m.row as i16 - self.inner.y as i16;
3130 self.set_screen_cursor((cx, cy), true).into()
3131 }
3132 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
3133 let cx = m.column as i16 - self.inner.x as i16;
3134 let cy = m.row as i16 - self.inner.y as i16;
3135 self.set_screen_cursor_words((cx, cy), true).into()
3136 }
3137 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
3138 if let Some(test) = self.screen_to_pos((m.column, m.row)) {
3139 let start = self.word_start(test);
3140 let end = self.word_end(test);
3141 self.set_selection(start, end).into()
3142 } else {
3143 TextOutcome::Unchanged
3144 }
3145 }
3146 ct_event!(mouse down Left for column,row) => {
3147 if self.inner.contains((*column, *row).into()) {
3148 let cx = (column - self.inner.x) as i16;
3149 let cy = (row - self.inner.y) as i16;
3150 self.set_screen_cursor((cx, cy), false).into()
3151 } else {
3152 TextOutcome::Continue
3153 }
3154 }
3155 ct_event!(mouse down SHIFT-Left for column,row) => {
3156 if self.inner.contains((*column, *row).into()) {
3157 let cx = (column - self.inner.x) as i16;
3158 let cy = (row - self.inner.y) as i16;
3159 self.set_screen_cursor((cx, cy), true).into()
3160 } else {
3161 TextOutcome::Continue
3162 }
3163 }
3164 ct_event!(mouse down CONTROL-Left for column,row) => {
3165 if self.inner.contains((*column, *row).into()) {
3166 let cx = (column - self.inner.x) as i16;
3167 let cy = (row - self.inner.y) as i16;
3168 self.set_screen_cursor((cx, cy), true).into()
3169 } else {
3170 TextOutcome::Continue
3171 }
3172 }
3173 ct_event!(mouse down ALT-Left for column,row) => {
3174 if self.inner.contains((*column, *row).into()) {
3175 let cx = (column - self.inner.x) as i16;
3176 let cy = (row - self.inner.y) as i16;
3177 self.set_screen_cursor_words((cx, cy), true).into()
3178 } else {
3179 TextOutcome::Continue
3180 }
3181 }
3182 _ => TextOutcome::Continue,
3183 });
3184
3185 let mut sas = ScrollAreaState::new()
3186 .area(self.inner)
3187 .h_scroll(&mut self.hscroll)
3188 .v_scroll(&mut self.vscroll);
3189 let r = match sas.handle(event, MouseOnly) {
3190 ScrollOutcome::Up(v) => self.scroll_up(v),
3191 ScrollOutcome::Down(v) => self.scroll_down(v),
3192 ScrollOutcome::Left(v) => self.scroll_left(v),
3193 ScrollOutcome::Right(v) => self.scroll_right(v),
3194 ScrollOutcome::VPos(v) => self.set_vertical_offset(v),
3195 ScrollOutcome::HPos(v) => self.set_horizontal_offset(v),
3196 _ => false,
3197 };
3198 if r {
3199 return TextOutcome::Changed;
3200 }
3201
3202 TextOutcome::Continue
3203 }
3204}
3205
3206pub fn handle_events(
3210 state: &mut TextAreaState,
3211 focus: bool,
3212 event: &crossterm::event::Event,
3213) -> TextOutcome {
3214 state.focus.set(focus);
3215 state.handle(event, Regular)
3216}
3217
3218pub fn handle_readonly_events(
3222 state: &mut TextAreaState,
3223 focus: bool,
3224 event: &crossterm::event::Event,
3225) -> TextOutcome {
3226 state.focus.set(focus);
3227 state.handle(event, ReadOnly)
3228}
3229
3230pub fn handle_mouse_events(
3232 state: &mut TextAreaState,
3233 event: &crossterm::event::Event,
3234) -> TextOutcome {
3235 state.handle(event, MouseOnly)
3236}