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 Some(h_max_offset) = widget.h_max_offset {
417 state.hscroll.set_max_offset(h_max_offset);
418 }
419 if let Some(h_overscroll) = widget.h_overscroll {
420 state.hscroll.set_overscroll_by(Some(h_overscroll));
421 }
422 state.hscroll.set_page_len(state.inner.width as usize);
423 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
424 state
425 .vscroll
426 .set_max_offset(state.len_lines().saturating_sub(1) as usize);
427 } else {
428 state.vscroll.set_max_offset(
429 state
430 .len_lines()
431 .saturating_sub(state.inner.height as upos_type) as usize,
432 );
433 }
434 state.vscroll.set_page_len(state.inner.height as usize);
435
436 if state.scroll_to_cursor {
437 state.scroll_to_cursor();
438 }
439
440 ScrollArea::new()
442 .block(widget.block.as_ref())
443 .h_scroll(widget.hscroll.as_ref())
444 .v_scroll(widget.vscroll.as_ref())
445 .style(style)
446 .render(
447 area,
448 buf,
449 &mut ScrollAreaState::new()
450 .h_scroll(&mut state.hscroll)
451 .v_scroll(&mut state.vscroll),
452 );
453
454 if state.inner.width == 0 || state.inner.height == 0 {
455 return;
457 }
458 if state.vscroll.offset() > state.value.len_lines() as usize {
459 return;
461 }
462
463 let (shift_left, sub_row_offset, start_row) = state.clean_offset();
464 let page_rows = start_row
465 ..min(
466 start_row + state.inner.height as upos_type,
467 state.value.len_lines(),
468 );
469 let page_bytes = state
470 .try_bytes_at_range(TextRange::new(
471 (sub_row_offset, page_rows.start),
472 (0, page_rows.end),
473 ))
474 .expect("valid_rows");
475 let mut screen_cursor = None;
476 let selection = state.selection();
477 let mut styles = Vec::new();
478
479 for g in state
480 .glyphs2(shift_left, sub_row_offset, page_rows)
481 .expect("valid_offset")
482 {
483 let screen_pos = g.screen_pos();
485
486 if screen_pos.1 >= state.inner.height {
487 break;
488 }
489
490 if g.pos() == state.cursor() && !g.soft_break() {
492 screen_cursor = Some(g.screen_pos());
493 }
494
495 if g.screen_width() > 0 {
496 let mut style = style;
497 state
499 .value
500 .styles_at_page(g.text_bytes().start, page_bytes.clone(), &mut styles);
501 for style_nr in &styles {
502 if let Some(s) = widget.text_style.get(style_nr) {
503 style = style.patch(*s);
504 }
505 }
506 if selection.contains_pos(g.pos()) {
508 style = style.patch(select_style);
509 };
510
511 if let Some(cell) =
513 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
514 {
515 cell.set_symbol(g.glyph());
516 cell.set_style(style);
517 }
518 for d in 1..g.screen_width() {
520 if let Some(cell) = buf.cell_mut((
521 state.inner.x + screen_pos.0 + d,
522 state.inner.y + screen_pos.1,
523 )) {
524 cell.reset();
525 cell.set_style(style);
526 }
527 }
528 }
529 }
530
531 state.screen_cursor = screen_cursor.map(|v| (state.inner.x + v.0, state.inner.y + v.1));
532}
533
534impl Default for TextAreaState {
535 fn default() -> Self {
536 let mut s = Self {
537 area: Default::default(),
538 inner: Default::default(),
539 rendered: Default::default(),
540 screen_cursor: Default::default(),
541 hscroll: Default::default(),
542 vscroll: Default::default(),
543 sub_row_offset: 0,
544 dark_offset: Default::default(),
545 scroll_to_cursor: Default::default(),
546 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
547 move_col: Default::default(),
548 auto_indent: true,
549 auto_quote: true,
550 text_wrap: TextWrap::Shift,
551 focus: Default::default(),
552 mouse: Default::default(),
553 non_exhaustive: NonExhaustive,
554 };
555 s.hscroll.set_max_offset(255);
556 s.hscroll.set_overscroll_by(Some(16384));
557 s
558 }
559}
560
561impl HasFocus for TextAreaState {
562 fn build(&self, builder: &mut FocusBuilder) {
563 builder.leaf_widget(self);
564 }
565
566 fn focus(&self) -> FocusFlag {
567 self.focus.clone()
568 }
569
570 fn area(&self) -> Rect {
571 self.area
572 }
573
574 fn navigable(&self) -> Navigation {
575 Navigation::Reach
576 }
577}
578
579impl TextAreaState {
580 #[inline]
582 pub fn new() -> Self {
583 Self::default()
584 }
585
586 #[inline]
588 pub fn named(name: &str) -> Self {
589 Self {
590 focus: FocusFlag::named(name),
591 ..Default::default()
592 }
593 }
594
595 #[inline]
601 pub fn set_newline(&mut self, br: impl Into<String>) {
602 self.value.set_newline(br.into());
603 }
604
605 #[inline]
607 pub fn newline(&self) -> &str {
608 self.value.newline()
609 }
610
611 #[inline]
613 pub fn set_auto_indent(&mut self, indent: bool) {
614 self.auto_indent = indent;
615 }
616
617 #[inline]
619 pub fn set_auto_quote(&mut self, quote: bool) {
620 self.auto_quote = quote;
621 }
622
623 #[inline]
625 pub fn set_tab_width(&mut self, tabs: u16) {
626 self.value.set_tab_width(tabs);
627 }
628
629 #[inline]
631 pub fn tab_width(&self) -> u16 {
632 self.value.tab_width()
633 }
634
635 #[inline]
637 pub fn set_expand_tabs(&mut self, expand: bool) {
638 self.value.set_expand_tabs(expand);
639 }
640
641 #[inline]
643 pub fn expand_tabs(&self) -> bool {
644 self.value.expand_tabs()
645 }
646
647 #[inline]
649 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
650 self.value.set_glyph_ctrl(show_ctrl);
651 }
652
653 pub fn show_ctrl(&self) -> bool {
655 self.value.glyph_ctrl()
656 }
657
658 #[inline]
661 pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
662 self.value.set_wrap_ctrl(wrap_ctrl);
663 }
664
665 pub fn wrap_ctrl(&self) -> bool {
668 self.value.wrap_ctrl()
669 }
670
671 pub fn set_text_wrap(&mut self, text_wrap: TextWrap) {
684 self.text_wrap = text_wrap;
685 }
686
687 pub fn text_wrap(&self) -> TextWrap {
689 self.text_wrap
690 }
691
692 #[inline]
702 pub fn set_move_col(&mut self, col: Option<i16>) {
703 self.move_col = col;
704 }
705
706 #[inline]
708 pub fn move_col(&mut self) -> Option<i16> {
709 self.move_col
710 }
711}
712
713impl TextAreaState {
714 #[inline]
717 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
718 match clip {
719 None => self.value.set_clipboard(None),
720 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
721 }
722 }
723
724 #[inline]
727 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
728 self.value.clipboard()
729 }
730
731 #[inline]
733 pub fn copy_to_clip(&mut self) -> bool {
734 let Some(clip) = self.value.clipboard() else {
735 return false;
736 };
737
738 _ = clip.set_string(self.selected_text().as_ref());
739 false
740 }
741
742 #[inline]
744 pub fn cut_to_clip(&mut self) -> bool {
745 let Some(clip) = self.value.clipboard() else {
746 return false;
747 };
748
749 match clip.set_string(self.selected_text().as_ref()) {
750 Ok(_) => self.delete_range(self.selection()),
751 Err(_) => false,
752 }
753 }
754
755 #[inline]
757 pub fn paste_from_clip(&mut self) -> bool {
758 let Some(clip) = self.value.clipboard() else {
759 return false;
760 };
761
762 if let Ok(text) = clip.get_string() {
763 self.insert_str(text)
764 } else {
765 false
766 }
767 }
768}
769
770impl TextAreaState {
771 #[inline]
778 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
779 match undo {
780 None => self.value.set_undo_buffer(None),
781 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
782 }
783 }
784
785 #[inline]
787 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
788 self.value.undo_buffer()
789 }
790
791 #[inline]
793 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
794 self.value.undo_buffer_mut()
795 }
796
797 #[inline]
803 pub fn begin_undo_seq(&mut self) {
804 self.value.begin_undo_seq()
805 }
806
807 #[inline]
809 pub fn end_undo_seq(&mut self) {
810 self.value.end_undo_seq()
811 }
812
813 #[inline]
818 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
819 self.value.recent_replay_log()
820 }
821
822 #[inline]
826 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
827 self.value.replay_log(replay)
828 }
829
830 #[inline]
832 pub fn undo(&mut self) -> bool {
833 self.value.undo()
834 }
835
836 #[inline]
838 pub fn redo(&mut self) -> bool {
839 self.value.redo()
840 }
841}
842
843impl TextAreaState {
844 #[inline]
861 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
862 self.value.set_styles(styles);
863 }
864
865 #[inline]
876 pub fn set_range_styles(&mut self, styles: Vec<(TextRange, usize)>) -> Result<(), TextError> {
877 self.value.set_range_styles(styles)
878 }
879
880 #[inline]
885 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
886 self.value.add_style(range, style);
887 }
888
889 #[inline]
893 pub fn add_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
894 let r = self.value.bytes_at_range(range)?;
895 self.value.add_style(r, style);
896 Ok(())
897 }
898
899 #[inline]
901 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
902 self.value.remove_style(range, style);
903 }
904
905 #[inline]
907 pub fn remove_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
908 let r = self.value.bytes_at_range(range)?;
909 self.value.remove_style(r, style);
910 Ok(())
911 }
912
913 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
915 self.value.styles_in(range, buf)
916 }
917
918 #[inline]
920 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
921 self.value.styles_at(byte_pos, buf)
922 }
923
924 #[inline]
927 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
928 self.value.style_match(byte_pos, style)
929 }
930
931 #[inline]
933 pub fn styles(&self) -> impl Iterator<Item = (Range<usize>, usize)> + '_ {
934 self.value.styles().expect("styles")
935 }
936}
937
938impl TextAreaState {
939 #[inline]
941 pub fn offset(&self) -> (usize, usize) {
942 (self.hscroll.offset(), self.vscroll.offset())
943 }
944
945 #[inline]
950 pub fn set_offset(&mut self, offset: (usize, usize)) -> bool {
951 self.scroll_to_cursor = false;
952 let c = self.hscroll.set_offset(offset.0);
953 let r = self.vscroll.set_offset(offset.1);
954 r || c
955 }
956
957 pub fn set_sub_row_offset(&mut self, sub_row_offset: upos_type) -> bool {
969 self.scroll_to_cursor = false;
970 let old = self.sub_row_offset;
971 self.sub_row_offset = sub_row_offset;
972 sub_row_offset != old
973 }
974
975 pub fn sub_row_offset(&self) -> upos_type {
980 self.sub_row_offset
981 }
982
983 fn clean_offset(&self) -> (upos_type, upos_type, upos_type) {
988 let ox = self.hscroll.offset as upos_type;
989 let mut oy = self.vscroll.offset as upos_type;
990
991 if oy >= self.len_lines() {
993 oy = 0;
994 }
995
996 match self.text_wrap {
997 TextWrap::Shift => (ox, 0, oy),
998 TextWrap::Hard | TextWrap::Word(_) => {
999 if let Ok(max_col) = self.try_line_width(oy) {
1001 (0, min(self.sub_row_offset, max_col), oy)
1002 } else {
1003 (0, 0, oy)
1004 }
1005 }
1006 }
1007 }
1008
1009 #[inline]
1011 pub fn cursor(&self) -> TextPosition {
1012 self.value.cursor()
1013 }
1014
1015 #[inline]
1017 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
1018 self.scroll_cursor_to_visible();
1019 self.value.set_cursor(cursor.into(), extend_selection)
1020 }
1021
1022 #[inline]
1024 pub fn anchor(&self) -> TextPosition {
1025 self.value.anchor()
1026 }
1027
1028 #[inline]
1030 pub fn has_selection(&self) -> bool {
1031 self.value.has_selection()
1032 }
1033
1034 #[inline]
1036 pub fn selection(&self) -> TextRange {
1037 self.value.selection()
1038 }
1039
1040 #[inline]
1043 pub fn set_selection(
1044 &mut self,
1045 anchor: impl Into<TextPosition>,
1046 cursor: impl Into<TextPosition>,
1047 ) -> bool {
1048 self.scroll_cursor_to_visible();
1049 self.value.set_selection(anchor.into(), cursor.into())
1050 }
1051
1052 #[inline]
1055 pub fn select_all(&mut self) -> bool {
1056 self.scroll_cursor_to_visible();
1057 self.value.select_all()
1058 }
1059
1060 #[inline]
1062 pub fn selected_text(&self) -> Cow<'_, str> {
1063 self.value
1064 .str_slice(self.value.selection())
1065 .expect("valid_selection")
1066 }
1067}
1068
1069impl TextAreaState {
1070 #[inline]
1072 pub fn is_empty(&self) -> bool {
1073 self.value.is_empty()
1074 }
1075
1076 #[inline]
1078 pub fn rope(&self) -> &Rope {
1079 self.value.text().rope()
1080 }
1081
1082 #[inline]
1084 pub fn text(&self) -> String {
1085 self.value.text().string()
1086 }
1087
1088 #[inline]
1092 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1093 self.value.str_slice_byte(range).expect("valid_range")
1094 }
1095
1096 #[inline]
1098 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1099 self.value.str_slice_byte(range)
1100 }
1101
1102 #[inline]
1106 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
1107 self.value.str_slice(range.into()).expect("valid_range")
1108 }
1109
1110 #[inline]
1112 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
1113 self.value.str_slice(range.into())
1114 }
1115
1116 #[inline]
1118 pub fn len_lines(&self) -> upos_type {
1119 self.value.len_lines()
1120 }
1121
1122 #[inline]
1126 pub fn line_width(&self, row: upos_type) -> upos_type {
1127 self.try_line_width(row).expect("valid_row")
1128 }
1129
1130 #[inline]
1132 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1133 self.value.line_width(row)
1134 }
1135
1136 #[inline]
1141 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
1142 self.value.line_at(row).expect("valid_row")
1143 }
1144
1145 #[inline]
1148 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1149 self.value.line_at(row)
1150 }
1151
1152 #[inline]
1156 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
1157 self.value.lines_at(row).expect("valid_row")
1158 }
1159
1160 #[inline]
1162 pub fn try_lines_at(
1163 &self,
1164 row: upos_type,
1165 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1166 self.value.lines_at(row)
1167 }
1168
1169 #[inline]
1172 #[allow(deprecated)]
1173 #[deprecated(since = "1.1.0", note = "discontinued api")]
1174 pub fn glyphs(
1175 &self,
1176 rows: Range<upos_type>,
1177 screen_offset: u16,
1178 screen_width: u16,
1179 ) -> impl Iterator<Item = Glyph<'_>> {
1180 self.value
1181 .glyphs(rows, screen_offset, screen_width)
1182 .expect("valid_rows")
1183 }
1184
1185 #[inline]
1188 #[allow(deprecated)]
1189 #[deprecated(since = "1.1.0", note = "discontinued api")]
1190 pub fn try_glyphs(
1191 &self,
1192 rows: Range<upos_type>,
1193 screen_offset: u16,
1194 screen_width: u16,
1195 ) -> Result<impl Iterator<Item = Glyph<'_>>, TextError> {
1196 self.value.glyphs(rows, screen_offset, screen_width)
1197 }
1198
1199 #[inline]
1204 pub fn line_graphemes(&self, row: upos_type) -> impl Iterator<Item = Grapheme<'_>> {
1205 self.value.line_graphemes(row).expect("valid_row")
1206 }
1207
1208 #[inline]
1211 pub fn try_line_graphemes(
1212 &self,
1213 row: upos_type,
1214 ) -> Result<impl Iterator<Item = Grapheme<'_>>, TextError> {
1215 self.value.line_graphemes(row)
1216 }
1217
1218 #[inline]
1222 pub fn text_graphemes(&self, pos: TextPosition) -> impl Cursor<Item = Grapheme<'_>> {
1223 self.value.text_graphemes(pos).expect("valid_pos")
1224 }
1225
1226 #[inline]
1228 pub fn try_text_graphemes(
1229 &self,
1230 pos: TextPosition,
1231 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1232 self.value.text_graphemes(pos)
1233 }
1234
1235 #[inline]
1239 pub fn graphemes(
1240 &self,
1241 range: TextRange,
1242 pos: TextPosition,
1243 ) -> impl Cursor<Item = Grapheme<'_>> {
1244 self.value.graphemes(range, pos).expect("valid_args")
1245 }
1246
1247 #[inline]
1249 pub fn try_graphemes(
1250 &self,
1251 range: TextRange,
1252 pos: TextPosition,
1253 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1254 self.value.graphemes(range, pos)
1255 }
1256
1257 #[inline]
1262 pub fn byte_at(&self, pos: TextPosition) -> Range<usize> {
1263 self.value.byte_at(pos).expect("valid_pos")
1264 }
1265
1266 #[inline]
1269 pub fn try_byte_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError> {
1270 self.value.byte_at(pos)
1271 }
1272
1273 #[inline]
1277 pub fn bytes_at_range(&self, range: TextRange) -> Range<usize> {
1278 self.value.bytes_at_range(range).expect("valid_range")
1279 }
1280
1281 #[inline]
1283 pub fn try_bytes_at_range(&self, range: TextRange) -> Result<Range<usize>, TextError> {
1284 self.value.bytes_at_range(range)
1285 }
1286
1287 #[inline]
1292 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1293 self.value.byte_pos(byte).expect("valid_pos")
1294 }
1295
1296 #[inline]
1299 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1300 self.value.byte_pos(byte)
1301 }
1302
1303 #[inline]
1307 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1308 self.value.byte_range(bytes).expect("valid_range")
1309 }
1310
1311 #[inline]
1313 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1314 self.value.byte_range(bytes)
1315 }
1316}
1317
1318impl TextAreaState {
1319 #[inline]
1321 pub fn clear(&mut self) -> bool {
1322 if !self.is_empty() {
1323 self.value.clear();
1324 true
1325 } else {
1326 false
1327 }
1328 }
1329
1330 #[inline]
1334 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1335 self.scroll_to_cursor = false;
1336 self.vscroll.set_offset(0);
1337 self.hscroll.set_offset(0);
1338 self.set_sub_row_offset(0);
1339 self.set_move_col(None);
1340
1341 self.value.set_text(TextRope::new_text(s.as_ref()));
1342 }
1343
1344 #[inline]
1347 pub fn set_rope(&mut self, r: Rope) {
1348 self.scroll_to_cursor = false;
1349 self.vscroll.set_offset(0);
1350 self.hscroll.set_offset(0);
1351 self.set_sub_row_offset(0);
1352 self.set_move_col(None);
1353
1354 self.value.set_text(TextRope::new_rope(r));
1355 }
1356
1357 pub fn insert_char(&mut self, c: char) -> bool {
1368 let mut insert = true;
1369 if self.has_selection() {
1370 if self.auto_quote
1371 && (c == '\''
1372 || c == '"'
1373 || c == '`'
1374 || c == '<'
1375 || c == '['
1376 || c == '('
1377 || c == '{')
1378 {
1379 self.value
1380 .insert_quotes(self.selection(), c)
1381 .expect("valid_selection");
1382 insert = false;
1383 } else {
1384 self.value
1385 .remove_str_range(self.selection())
1386 .expect("valid_selection");
1387 }
1388 }
1389
1390 if insert {
1391 if c == '\n' {
1392 self.value
1393 .insert_newline(self.cursor())
1394 .expect("valid_cursor");
1395 } else if c == '\t' {
1396 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1397 } else {
1398 self.value
1399 .insert_char(self.cursor(), c)
1400 .expect("valid_cursor");
1401 }
1402 }
1403
1404 self.scroll_cursor_to_visible();
1405
1406 true
1407 }
1408
1409 pub fn insert_tab(&mut self) -> bool {
1415 if self.has_selection() {
1416 if self.auto_indent {
1417 let sel = self.selection();
1418 let indent = " ".repeat(self.tab_width() as usize);
1419
1420 self.value.begin_undo_seq();
1421 for r in sel.start.y..=sel.end.y {
1422 self.value
1423 .insert_str(TextPosition::new(0, r), &indent)
1424 .expect("valid_row");
1425 }
1426 self.value.end_undo_seq();
1427
1428 true
1429 } else {
1430 false
1431 }
1432 } else {
1433 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1434 self.scroll_cursor_to_visible();
1435
1436 true
1437 }
1438 }
1439
1440 pub fn insert_backtab(&mut self) -> bool {
1445 if self.has_selection() {
1446 let sel = self.selection();
1447
1448 self.value.begin_undo_seq();
1449 for r in sel.start.y..=sel.end.y {
1450 let mut idx = 0;
1451 let g_it = self
1452 .value
1453 .graphemes(TextRange::new((0, r), (0, r + 1)), TextPosition::new(0, r))
1454 .expect("valid_range")
1455 .take(self.tab_width() as usize);
1456 for g in g_it {
1457 if g != " " && g != "\t" {
1458 break;
1459 }
1460 idx += 1;
1461 }
1462
1463 self.value
1464 .remove_str_range(TextRange::new((0, r), (idx, r)))
1465 .expect("valid_range");
1466 }
1467 self.value.end_undo_seq();
1468
1469 true
1470 } else {
1471 false
1472 }
1473 }
1474
1475 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1478 let t = t.as_ref();
1479 if self.has_selection() {
1480 self.value
1481 .remove_str_range(self.selection())
1482 .expect("valid_selection");
1483 }
1484 self.value
1485 .insert_str(self.cursor(), t)
1486 .expect("valid_cursor");
1487 self.scroll_cursor_to_visible();
1488 true
1489 }
1490
1491 pub fn insert_newline(&mut self) -> bool {
1496 if self.has_selection() {
1497 self.value
1498 .remove_str_range(self.selection())
1499 .expect("valid_selection");
1500 }
1501 self.value
1502 .insert_newline(self.cursor())
1503 .expect("valid_cursor");
1504
1505 if self.auto_indent {
1507 let cursor = self.cursor();
1508 if cursor.y > 0 {
1509 let mut blanks = String::new();
1510 for g in self.line_graphemes(cursor.y - 1) {
1511 if g == " " || g == "\t" {
1512 blanks.push_str(g.grapheme());
1513 } else {
1514 break;
1515 }
1516 }
1517 if !blanks.is_empty() {
1518 self.value
1519 .insert_str(cursor, &blanks)
1520 .expect("valid_cursor");
1521 }
1522 }
1523 }
1524
1525 self.scroll_cursor_to_visible();
1526 true
1527 }
1528
1529 #[inline]
1533 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1534 self.try_delete_range(range).expect("valid_range")
1535 }
1536
1537 #[inline]
1539 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1540 let range = range.into();
1541 if !range.is_empty() {
1542 self.value.remove_str_range(range)?;
1543 self.scroll_cursor_to_visible();
1544 Ok(true)
1545 } else {
1546 Ok(false)
1547 }
1548 }
1549}
1550
1551impl TextAreaState {
1552 pub fn duplicate_text(&mut self) -> bool {
1555 if self.has_selection() {
1556 let sel_range = self.selection();
1557 if !sel_range.is_empty() {
1558 let v = self.str_slice(sel_range).to_string();
1559 self.value
1560 .insert_str(sel_range.end, &v)
1561 .expect("valid_selection");
1562 true
1563 } else {
1564 false
1565 }
1566 } else {
1567 let pos = self.cursor();
1568 let row_range = TextRange::new((0, pos.y), (0, pos.y + 1));
1569 let v = self.str_slice(row_range).to_string();
1570 self.value
1571 .insert_str(row_range.start, &v)
1572 .expect("valid_cursor");
1573 true
1574 }
1575 }
1576
1577 pub fn delete_line(&mut self) -> bool {
1580 let pos = self.cursor();
1581 if pos.y + 1 < self.len_lines() {
1582 self.delete_range(TextRange::new((0, pos.y), (0, pos.y + 1)))
1583 } else {
1584 let width = self.line_width(pos.y);
1585 self.delete_range(TextRange::new((0, pos.y), (width, pos.y)))
1586 }
1587 }
1588
1589 pub fn delete_next_char(&mut self) -> bool {
1592 if self.has_selection() {
1593 self.delete_range(self.selection())
1594 } else {
1595 let r = self
1596 .value
1597 .remove_next_char(self.cursor())
1598 .expect("valid_cursor");
1599 self.scroll_cursor_to_visible();
1600 r
1601 }
1602 }
1603
1604 pub fn delete_prev_char(&mut self) -> bool {
1607 if self.has_selection() {
1608 self.delete_range(self.selection())
1609 } else {
1610 let r = self
1611 .value
1612 .remove_prev_char(self.cursor())
1613 .expect("valid_cursor");
1614 self.scroll_cursor_to_visible();
1615 r
1616 }
1617 }
1618
1619 #[inline]
1624 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1625 self.value.next_word_start(pos.into()).expect("valid_pos")
1626 }
1627
1628 #[inline]
1631 pub fn try_next_word_start(
1632 &self,
1633 pos: impl Into<TextPosition>,
1634 ) -> Result<TextPosition, TextError> {
1635 self.value.next_word_start(pos.into())
1636 }
1637
1638 #[inline]
1643 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1644 self.value.next_word_end(pos.into()).expect("valid_pos")
1645 }
1646
1647 #[inline]
1650 pub fn try_next_word_end(
1651 &self,
1652 pos: impl Into<TextPosition>,
1653 ) -> Result<TextPosition, TextError> {
1654 self.value.next_word_end(pos.into())
1655 }
1656
1657 #[inline]
1665 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1666 self.value.prev_word_start(pos.into()).expect("valid_pos")
1667 }
1668
1669 #[inline]
1675 pub fn try_prev_word_start(
1676 &self,
1677 pos: impl Into<TextPosition>,
1678 ) -> Result<TextPosition, TextError> {
1679 self.value.prev_word_start(pos.into())
1680 }
1681
1682 #[inline]
1688 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1689 self.value.prev_word_end(pos.into()).expect("valid_pos")
1690 }
1691
1692 #[inline]
1696 pub fn try_prev_word_end(
1697 &self,
1698 pos: impl Into<TextPosition>,
1699 ) -> Result<TextPosition, TextError> {
1700 self.value.prev_word_end(pos.into())
1701 }
1702
1703 #[inline]
1707 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1708 self.value.is_word_boundary(pos.into()).expect("valid_pos")
1709 }
1710
1711 #[inline]
1713 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1714 self.value.is_word_boundary(pos.into())
1715 }
1716
1717 #[inline]
1722 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1723 self.value.word_start(pos.into()).expect("valid_pos")
1724 }
1725
1726 #[inline]
1729 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1730 self.value.word_start(pos.into())
1731 }
1732
1733 #[inline]
1738 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1739 self.value.word_end(pos.into()).expect("valid_pos")
1740 }
1741
1742 #[inline]
1745 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1746 self.value.word_end(pos.into())
1747 }
1748
1749 pub fn delete_next_word(&mut self) -> bool {
1754 if self.has_selection() {
1755 self.delete_range(self.selection())
1756 } else {
1757 let cursor = self.cursor();
1758
1759 let start = self.next_word_start(cursor);
1760 if start != cursor {
1761 self.delete_range(cursor..start)
1762 } else {
1763 let end = self.next_word_end(cursor);
1764 self.delete_range(cursor..end)
1765 }
1766 }
1767 }
1768
1769 pub fn delete_prev_word(&mut self) -> bool {
1774 if self.has_selection() {
1775 self.delete_range(self.selection())
1776 } else {
1777 let cursor = self.cursor();
1778
1779 let till_line_start = if cursor.x != 0 {
1781 self.graphemes(TextRange::new((0, cursor.y), cursor), cursor)
1782 .rev_cursor()
1783 .all(|v| v.is_whitespace())
1784 } else {
1785 false
1786 };
1787
1788 if till_line_start {
1789 self.delete_range(TextRange::new((0, cursor.y), cursor))
1790 } else {
1791 let end = self.prev_word_end(cursor);
1792 if end != cursor {
1793 self.delete_range(end..cursor)
1794 } else {
1795 let start = self.prev_word_start(cursor);
1796 self.delete_range(start..cursor)
1797 }
1798 }
1799 }
1800 }
1801
1802 pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1805 let mut cursor = self.cursor();
1806 if cursor.x == 0 {
1807 if cursor.y > 0 {
1808 cursor.y = cursor.y.saturating_sub(1);
1809 cursor.x = self.line_width(cursor.y);
1810 }
1811 } else {
1812 cursor.x = cursor.x.saturating_sub(n as upos_type);
1813 }
1814
1815 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
1816 self.set_move_col(Some(scr_cursor.0));
1817 }
1818
1819 self.set_cursor(cursor, extend_selection)
1820 }
1821
1822 pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1825 let mut cursor = self.cursor();
1826 let c_line_width = self.line_width(cursor.y);
1827 if cursor.x == c_line_width {
1828 if cursor.y + 1 < self.len_lines() {
1829 cursor.y += 1;
1830 cursor.x = 0;
1831 }
1832 } else {
1833 cursor.x = min(cursor.x + n as upos_type, c_line_width)
1834 }
1835
1836 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
1837 self.set_move_col(Some(scr_cursor.0));
1838 }
1839 self.set_cursor(cursor, extend_selection)
1840 }
1841
1842 pub fn move_up(&mut self, n: u16, extend_selection: bool) -> bool {
1845 let cursor = self.cursor();
1846 if let Some(mut scr_cursor) = self.pos_to_relative_screen(cursor) {
1847 if let Some(move_col) = self.move_col() {
1848 scr_cursor.0 = move_col;
1849 }
1850 scr_cursor.1 -= n as i16;
1851
1852 if let Some(new_cursor) = self.relative_screen_to_pos(scr_cursor) {
1853 self.set_cursor(new_cursor, extend_selection)
1854 } else {
1855 self.scroll_cursor_to_visible();
1856 true
1857 }
1858 } else {
1859 self.scroll_cursor_to_visible();
1860 true
1861 }
1862 }
1863
1864 pub fn move_down(&mut self, n: u16, extend_selection: bool) -> bool {
1867 let cursor = self.cursor();
1868 if let Some(mut scr_cursor) = self.pos_to_relative_screen(cursor) {
1869 if let Some(move_col) = self.move_col() {
1870 scr_cursor.0 = move_col;
1871 }
1872 scr_cursor.1 += n as i16;
1873
1874 if let Some(new_cursor) = self.relative_screen_to_pos(scr_cursor) {
1875 self.set_cursor(new_cursor, extend_selection)
1876 } else {
1877 self.scroll_cursor_to_visible();
1878 true
1879 }
1880 } else {
1881 self.scroll_cursor_to_visible();
1882 true
1883 }
1884 }
1885
1886 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1890 let cursor = self.cursor();
1891
1892 let mut line_start = self.pos_to_line_start(cursor);
1893 for g in self
1894 .glyphs2(
1895 0,
1896 line_start.x,
1897 line_start.y..min(line_start.y + 1, self.len_lines()),
1898 )
1899 .expect("valid-pos")
1900 {
1901 if g.glyph() != " " && g.glyph() != "\t" {
1902 if g.pos().x != cursor.x {
1903 line_start.x = g.pos().x;
1904 }
1905 break;
1906 }
1907 }
1908
1909 if let Some(scr_pos) = self.pos_to_relative_screen(line_start) {
1910 self.set_move_col(Some(scr_pos.0));
1911 }
1912 self.set_cursor(line_start, extend_selection)
1913 }
1914
1915 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1919 let cursor = self.cursor();
1920 let line_end = self.pos_to_line_end(cursor);
1921 if let Some(scr_pos) = self.pos_to_relative_screen(line_end) {
1922 self.set_move_col(Some(scr_pos.0));
1923 }
1924 self.set_cursor(line_end, extend_selection)
1925 }
1926
1927 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1929 let cursor = TextPosition::new(0, 0);
1930
1931 self.set_move_col(Some(0));
1932 self.set_cursor(cursor, extend_selection)
1933 }
1934
1935 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1937 let cursor = TextPosition::new(
1938 self.line_width(self.len_lines().saturating_sub(1)),
1939 self.len_lines().saturating_sub(1),
1940 );
1941
1942 let line_start = self.pos_to_line_start(cursor);
1943 self.set_move_col(Some(0));
1944 self.set_cursor(line_start, extend_selection)
1945 }
1946
1947 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1949 let (ox, oy) = self.offset();
1950
1951 let cursor = TextPosition::new(ox as upos_type, oy as upos_type);
1952
1953 self.set_move_col(Some(0));
1954 self.set_cursor(cursor, extend_selection)
1955 }
1956
1957 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1959 let scr_end = (0, (self.inner.height as i16).saturating_sub(1));
1960 if let Some(pos) = self.relative_screen_to_pos(scr_end) {
1961 self.set_move_col(Some(0));
1962 self.set_cursor(pos, extend_selection)
1963 } else {
1964 self.scroll_cursor_to_visible();
1965 true
1966 }
1967 }
1968
1969 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1971 let cursor = self.cursor();
1972
1973 let word = self.next_word_end(cursor);
1974
1975 if let Some(scr_pos) = self.pos_to_relative_screen(word) {
1976 self.set_move_col(Some(scr_pos.0));
1977 }
1978 self.set_cursor(word, extend_selection)
1979 }
1980
1981 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1983 let cursor = self.cursor();
1984
1985 let word = self.prev_word_start(cursor);
1986
1987 if let Some(scr_pos) = self.pos_to_relative_screen(word) {
1988 self.set_move_col(Some(scr_pos.0));
1989 }
1990 self.set_cursor(word, extend_selection)
1991 }
1992}
1993
1994impl HasScreenCursor for TextAreaState {
1995 #[allow(clippy::question_mark)]
1997 fn screen_cursor(&self) -> Option<(u16, u16)> {
1998 if self.is_focused() {
1999 if self.has_selection() {
2000 None
2001 } else {
2002 let Some(scr_cursor) = self.screen_cursor else {
2003 return None;
2004 };
2005
2006 if !(scr_cursor.0 >= self.inner.x
2007 && scr_cursor.0 <= self.inner.right()
2008 && scr_cursor.1 >= self.inner.y
2009 && scr_cursor.1 < self.inner.bottom())
2010 {
2011 return None;
2012 }
2013 Some(scr_cursor)
2014 }
2015 } else {
2016 None
2017 }
2018 }
2019}
2020
2021impl RelocatableState for TextAreaState {
2022 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
2023 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
2025 self.area = relocate_area(self.area, shift, clip);
2026 self.inner = relocate_area(self.inner, shift, clip);
2027 if let Some(screen_cursor) = self.screen_cursor {
2028 self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
2029 }
2030 }
2031}
2032
2033impl TextAreaState {
2034 fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
2035 match self.text_wrap {
2036 TextWrap::Shift => (
2037 TextWrap2::Shift,
2038 shift_left,
2039 shift_left + self.rendered.width as upos_type,
2040 shift_left + self.rendered.width as upos_type,
2041 ),
2042 TextWrap::Hard => (
2043 TextWrap2::Hard,
2044 0,
2045 self.rendered.width as upos_type,
2046 self.rendered.width as upos_type,
2047 ),
2048 TextWrap::Word(margin) => (
2049 TextWrap2::Word,
2050 0,
2051 self.rendered.width as upos_type,
2052 self.rendered.width.saturating_sub(margin) as upos_type,
2053 ),
2054 }
2055 }
2056
2057 fn fill_cache(
2060 &self,
2061 shift_left: upos_type,
2062 sub_row_offset: upos_type,
2063 rows: Range<upos_type>,
2064 ) -> Result<(), TextError> {
2065 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
2066 self.value.fill_cache(
2067 self.rendered,
2068 sub_row_offset,
2069 rows,
2070 text_wrap,
2071 self.wrap_ctrl() | self.show_ctrl(),
2072 left_margin,
2073 right_margin,
2074 word_margin,
2075 )
2076 }
2077
2078 fn glyphs2(
2079 &self,
2080 shift_left: upos_type,
2081 sub_row_offset: upos_type,
2082 rows: Range<upos_type>,
2083 ) -> Result<GlyphIter2<'_, <TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
2084 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
2085 self.value.glyphs2(
2086 self.rendered,
2087 sub_row_offset,
2088 rows,
2089 text_wrap,
2090 self.wrap_ctrl() | self.show_ctrl(),
2091 left_margin,
2092 right_margin,
2093 word_margin,
2094 )
2095 }
2096
2097 pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
2099 let scr_pos = (
2100 scr_pos.0 as i16 - self.inner.x as i16,
2101 scr_pos.1 as i16 - self.inner.y as i16,
2102 );
2103 self.relative_screen_to_pos(scr_pos)
2104 }
2105
2106 pub fn pos_to_screen(&self, pos: TextPosition) -> Option<(u16, u16)> {
2108 let scr_pos = self.pos_to_relative_screen(pos)?;
2109 if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
2110 Some((
2111 (scr_pos.0 + self.inner.x as i16) as u16,
2112 (scr_pos.1 + self.inner.y as i16) as u16,
2113 ))
2114 } else {
2115 None
2116 }
2117 }
2118
2119 pub fn pos_to_line_start(&self, pos: TextPosition) -> TextPosition {
2121 match self.text_wrap {
2122 TextWrap::Shift => {
2123 TextPosition::new(0, pos.y)
2125 }
2126 TextWrap::Hard | TextWrap::Word(_) => {
2127 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2128 .expect("valid-row");
2129
2130 let mut start_pos = TextPosition::new(0, pos.y);
2131 for (break_pos, _) in self.value.cache().line_break.borrow().range(
2132 TextPosition::new(0, pos.y)
2133 ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
2134 ) {
2135 if pos >= start_pos && &pos <= break_pos {
2136 break;
2137 }
2138 start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2139 }
2140
2141 start_pos
2142 }
2143 }
2144 }
2145
2146 pub fn pos_to_line_end(&self, pos: TextPosition) -> TextPosition {
2148 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2149 .expect("valid-row");
2150
2151 let mut end_pos = TextPosition::new(0, pos.y);
2152 for (break_pos, _) in self
2153 .value
2154 .cache()
2155 .line_break
2156 .borrow()
2157 .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2158 {
2159 if pos >= end_pos && &pos <= break_pos {
2160 end_pos = *break_pos;
2161 break;
2162 }
2163 end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2164 }
2165
2166 end_pos
2167 }
2168
2169 fn scroll_to_cursor(&mut self) {
2170 let cursor = self.cursor();
2171
2172 let nox;
2173 let noy;
2174 let mut nsub_row_offset;
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 noy = if cursor.y < oy {
2188 cursor.y
2189 } else if cursor.y >= oy + height {
2190 cursor.y.saturating_sub(height) + 1
2191 } else {
2192 oy
2193 };
2194
2195 nox = if cursor.x < ox {
2196 cursor.x
2197 } else if cursor.x >= ox + width {
2198 cursor.x.saturating_sub(width) + 1
2199 } else {
2200 ox
2201 };
2202
2203 nsub_row_offset = 0;
2204 }
2205 TextWrap::Hard | TextWrap::Word(_) => {
2206 let (ox, sub_row_offset, oy) = self.clean_offset();
2207
2208 'f: {
2211 let mut sub_row_offsets = Vec::new();
2212 for g in self
2213 .glyphs2(
2214 0,
2215 sub_row_offset,
2216 oy..min(oy + 2 * self.rendered.height as upos_type, self.len_lines()),
2217 )
2218 .expect("valid_offset")
2219 {
2220 if g.screen_pos().0 == 0 {
2221 sub_row_offsets.push((g.pos().x, g.pos().y));
2222 }
2223 if g.screen_pos().1 >= 2 * self.rendered.height {
2224 break;
2225 }
2226 if g.pos() == cursor {
2227 if g.screen_pos().1 >= self.rendered.height {
2228 (nsub_row_offset, noy) = sub_row_offsets
2229 [(g.screen_pos().1 - self.rendered.height + 1) as usize];
2230 nox = ox;
2231 } else {
2232 nsub_row_offset = sub_row_offset;
2233 nox = ox;
2234 noy = oy;
2235 }
2236
2237 break 'f;
2238 }
2239 }
2240
2241 nsub_row_offset = 0;
2247 for g in self
2248 .glyphs2(0, 0, cursor.y..min(cursor.y + 1, self.len_lines()))
2249 .expect("valid_offset")
2250 {
2251 if g.screen_pos().0 == 0 {
2252 nsub_row_offset = g.pos().x;
2253 }
2254 if g.pos() == cursor {
2255 break; }
2257 }
2258
2259 nox = 0;
2260 noy = cursor.y;
2261 }
2262 }
2263 }
2264
2265 self.set_offset((nox as usize, noy as usize));
2266 self.set_sub_row_offset(nsub_row_offset);
2267 }
2268
2269 #[allow(clippy::explicit_counter_loop)]
2279 pub fn pos_to_relative_screen(&self, pos: TextPosition) -> Option<(i16, i16)> {
2280 match self.text_wrap {
2281 TextWrap::Shift => {
2282 let (ox, _, oy) = self.clean_offset();
2283
2284 if oy > self.len_lines() {
2285 return None;
2286 }
2287
2288 if pos.y < oy {
2289 return None;
2290 }
2291 if pos.y > self.len_lines() {
2292 return None;
2293 }
2294 if pos.y - oy >= self.rendered.height as u32 {
2295 return None;
2296 }
2297
2298 let screen_y = (pos.y - oy) as u16;
2299
2300 let screen_x = 'f: {
2301 for g in self
2302 .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2303 .expect("valid-row")
2304 {
2305 if g.pos().x == pos.x {
2306 break 'f g.screen_pos().0;
2307 } else if g.line_break() {
2308 break 'f g.screen_pos().0;
2309 }
2310 }
2311 return None;
2312 };
2313 assert!(screen_x <= self.rendered.width);
2314
2315 Some((
2316 screen_x as i16 - self.dark_offset.0 as i16,
2317 screen_y as i16 - self.dark_offset.1 as i16,
2318 ))
2319 }
2320 TextWrap::Hard | TextWrap::Word(_) => {
2321 let (_, sub_row_offset, oy) = self.clean_offset();
2322
2323 if oy > self.len_lines() {
2324 return None;
2325 }
2326 if pos.y < oy {
2327 return None;
2328 }
2329 if pos.y > self.len_lines() {
2330 return None;
2331 }
2332
2333 self.fill_cache(
2334 0,
2335 sub_row_offset,
2336 oy..min(oy + self.rendered.height as upos_type, self.len_lines()),
2337 )
2338 .expect("valid-rows");
2339
2340 let (screen_y, start_pos) = 'f: {
2341 let mut prev_screen_y = 0;
2342 let mut prev_pos = TextPosition::new(sub_row_offset, oy);
2343 let mut screen_y = 0;
2344 let mut start_pos = TextPosition::new(sub_row_offset, oy);
2345
2346 for (_, cache) in self.value.cache().line_break.borrow().range(
2347 TextPosition::new(sub_row_offset, oy)
2348 ..TextPosition::new(
2349 0,
2350 min(oy + self.rendered.height as upos_type, self.len_lines()),
2351 ),
2352 ) {
2353 if screen_y >= self.rendered.height {
2354 return None;
2355 }
2356 if pos < cache.start_pos {
2357 break 'f (screen_y, start_pos);
2358 }
2359
2360 prev_pos = start_pos;
2361 prev_screen_y = screen_y;
2362
2363 screen_y += 1;
2364 start_pos = cache.start_pos;
2365 }
2366
2367 if pos == start_pos {
2369 break 'f (prev_screen_y, prev_pos);
2370 }
2371 return None;
2372 };
2373
2374 let screen_x = 'f: {
2375 for g in self
2376 .glyphs2(
2377 0,
2378 start_pos.x,
2379 start_pos.y..min(start_pos.y + 1, self.len_lines()),
2380 )
2381 .expect("valid-row")
2382 {
2383 if g.pos().x == pos.x {
2384 break 'f g.screen_pos().0;
2385 } else if g.line_break() {
2386 break 'f g.screen_pos().0;
2387 }
2388 }
2389 return None;
2390 };
2391 assert!(screen_x <= self.rendered.width);
2392
2393 Some((
2394 screen_x as i16 - self.dark_offset.0 as i16,
2395 screen_y as i16 - self.dark_offset.1 as i16,
2396 ))
2397 }
2398 }
2399 }
2400
2401 #[allow(clippy::needless_return)]
2403 pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
2404 let scr_pos = (
2405 scr_pos.0 + self.dark_offset.0 as i16,
2406 scr_pos.1 + self.dark_offset.1 as i16,
2407 );
2408
2409 match self.text_wrap {
2410 TextWrap::Shift => {
2411 let (ox, _, oy) = self.clean_offset();
2412
2413 if oy >= self.len_lines() {
2414 return None;
2415 }
2416 if scr_pos.1 < 0 {
2417 return Some(TextPosition::new(
2419 0,
2420 oy.saturating_add_signed(scr_pos.1 as ipos_type),
2421 ));
2422 }
2423 if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
2424 return Some(TextPosition::new(
2426 self.line_width(self.len_lines().saturating_sub(1)),
2427 self.len_lines().saturating_sub(1),
2428 ));
2429 }
2430
2431 let pos_y = oy + scr_pos.1 as upos_type;
2432
2433 if scr_pos.0 < 0 {
2434 return Some(TextPosition::new(
2435 ox.saturating_add_signed(scr_pos.0 as ipos_type),
2436 pos_y,
2437 ));
2438 } else if scr_pos.0 as u16 >= self.rendered.width {
2439 return Some(TextPosition::new(
2440 min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
2441 pos_y,
2442 ));
2443 } else {
2444 for g in self
2445 .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
2446 .expect("valid-position")
2447 {
2448 if g.contains_screen_x(scr_pos.0 as u16) {
2449 return Some(TextPosition::new(g.pos().x, pos_y));
2450 }
2451 }
2452 unreachable!();
2453 }
2454 }
2455 TextWrap::Hard | TextWrap::Word(_) => {
2456 let (_, sub_row_offset, oy) = self.clean_offset();
2457
2458 if oy >= self.len_lines() {
2459 return None;
2460 }
2461
2462 if scr_pos.1 < 0 {
2463 let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2472
2473 self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2475
2476 let n_start_pos = 'f: {
2477 let mut nrows = scr_pos.1.unsigned_abs();
2478 for (_break_pos, cache) in self
2479 .value
2480 .cache()
2481 .line_break
2482 .borrow()
2483 .range(TextPosition::new(0, ry)..TextPosition::new(sub_row_offset, oy))
2484 .rev()
2485 {
2486 if nrows == 0 {
2487 break 'f cache.start_pos;
2488 }
2489 nrows -= 1;
2490 }
2491 TextPosition::new(0, 0)
2492 };
2493
2494 if scr_pos.0 < 0 {
2496 return Some(n_start_pos);
2497 }
2498
2499 for g in self
2500 .glyphs2(
2501 0,
2502 n_start_pos.x,
2503 n_start_pos.y..min(n_start_pos.y + 1, self.len_lines()),
2504 )
2505 .expect("valid-rows")
2506 {
2507 if g.contains_screen_x(scr_pos.0 as u16) {
2508 return Some(g.pos());
2509 }
2510 }
2511
2512 unreachable!("impossible screen-pos");
2513 } else {
2514 let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2515
2516 let n_start_pos = if scr_pos.1 == 0 {
2518 TextPosition::new(sub_row_offset, oy)
2519 } else {
2520 self.fill_cache(
2523 0,
2524 sub_row_offset,
2525 oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2526 )
2527 .expect("valid-rows");
2528
2529 'f: {
2530 let mut nrows = scr_pos.1 - 1;
2531 let mut fallback = TextPosition::new(sub_row_offset, oy);
2532 for (_break_pos, cache) in self.value.cache().line_break.borrow().range(
2533 TextPosition::new(sub_row_offset, oy)
2534 ..TextPosition::new(0, self.len_lines()),
2535 ) {
2536 if nrows == 0 {
2537 break 'f cache.start_pos;
2538 }
2539 fallback = cache.start_pos;
2540 nrows -= 1;
2541 }
2542 fallback
2543 }
2544 };
2545
2546 for g in self
2547 .glyphs2(
2548 0,
2549 n_start_pos.x,
2550 n_start_pos.y..min(n_start_pos.y + 1, self.len_lines()),
2551 )
2552 .expect("valid-rows")
2553 {
2554 if g.contains_screen_x(scr_pos.0) {
2555 return Some(g.pos());
2556 }
2557 }
2558 unreachable!("impossible screen-pos");
2559 }
2560 }
2561 }
2562 }
2563}
2564
2565impl TextAreaState {
2566 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2569 pub fn screen_to_row(&self, scy: i16) -> upos_type {
2570 let (_, oy) = self.offset();
2571 let oy = oy as upos_type + self.dark_offset.1 as upos_type;
2572
2573 if scy < 0 {
2574 oy.saturating_sub((scy as ipos_type).unsigned_abs())
2575 } else if scy as u16 >= (self.inner.height + self.dark_offset.1) {
2576 min(oy + scy as upos_type, self.len_lines().saturating_sub(1))
2577 } else {
2578 let scy = oy + scy as upos_type;
2579 let len = self.len_lines();
2580 if scy < len {
2581 scy
2582 } else {
2583 len.saturating_sub(1)
2584 }
2585 }
2586 }
2587
2588 #[allow(deprecated)]
2595 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2596 pub fn screen_to_col(&self, row: upos_type, scx: i16) -> upos_type {
2597 self.try_screen_to_col(row, scx).expect("valid_row")
2598 }
2599
2600 #[allow(deprecated)]
2607 #[deprecated(since = "1.1.0", note = "replaced by relative_screen_to_pos()")]
2608 pub fn try_screen_to_col(&self, row: upos_type, scx: i16) -> Result<upos_type, TextError> {
2609 let (ox, _) = self.offset();
2610
2611 let ox = ox as upos_type + self.dark_offset.0 as upos_type;
2612
2613 if scx < 0 {
2614 Ok(ox.saturating_sub((scx as ipos_type).unsigned_abs()))
2615 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
2616 Ok(min(ox + scx as upos_type, self.line_width(row)))
2617 } else {
2618 let scx = scx as u16;
2619
2620 let line = self.try_glyphs(
2621 row..row + 1,
2622 ox as u16,
2623 self.inner.width + self.dark_offset.0,
2624 )?;
2625
2626 let mut col = ox;
2627 for g in line {
2628 if scx < g.screen_pos().0 + g.screen_width() {
2629 break;
2630 }
2631 col = g.pos().x + 1;
2632 }
2633 Ok(col)
2634 }
2635 }
2636
2637 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2640 pub fn row_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
2641 let pos = pos.into();
2642 let (_, oy) = self.offset();
2643
2644 if pos.y < oy as upos_type {
2645 return None;
2646 }
2647
2648 let screen_y = pos.y - oy as upos_type;
2649
2650 if screen_y >= self.dark_offset.1 as upos_type {
2651 Some(screen_y as u16 - self.dark_offset.1)
2652 } else {
2653 None
2654 }
2655 }
2656
2657 #[allow(deprecated)]
2660 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2661 pub fn col_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
2662 self.try_col_to_screen(pos).expect("valid_pos")
2663 }
2664
2665 #[allow(deprecated)]
2668 #[deprecated(since = "1.1.0", note = "replaced by pos_to_relative_screen()")]
2669 pub fn try_col_to_screen(
2670 &self,
2671 pos: impl Into<TextPosition>,
2672 ) -> Result<Option<u16>, TextError> {
2673 let pos = pos.into();
2674 let (ox, _) = self.offset();
2675
2676 if pos.x < ox as upos_type {
2677 return Ok(None);
2678 }
2679
2680 let line = self.try_glyphs(
2681 pos.y..pos.y + 1,
2682 ox as u16,
2683 self.inner.width + self.dark_offset.0,
2684 )?;
2685 let mut screen_x = 0;
2686 for g in line {
2687 if g.pos().x == pos.x {
2688 break;
2689 }
2690 screen_x = g.screen_pos().0 + g.screen_width();
2691 }
2692
2693 if screen_x >= self.dark_offset.0 {
2694 Ok(Some(screen_x - self.dark_offset.0))
2695 } else {
2696 Ok(None)
2697 }
2698 }
2699
2700 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2706 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2707 return false;
2708 };
2709 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2710 self.set_move_col(Some(scr_cursor.0));
2711 }
2712 self.set_cursor(cursor, extend_selection)
2713 }
2714
2715 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2722 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2723 return false;
2724 };
2725
2726 let anchor = self.anchor();
2727 let cursor = if cursor < anchor {
2728 self.word_start(cursor)
2729 } else {
2730 self.word_end(cursor)
2731 };
2732
2733 if !self.is_word_boundary(anchor) {
2735 if cursor < anchor {
2736 self.set_cursor(self.word_end(anchor), false);
2737 } else {
2738 self.set_cursor(self.word_start(anchor), false);
2739 }
2740 }
2741
2742 self.set_cursor(cursor, extend_selection)
2743 }
2744}
2745
2746impl TextAreaState {
2747 pub fn vertical_max_offset(&self) -> usize {
2752 self.vscroll.max_offset()
2753 }
2754
2755 pub fn vertical_offset(&self) -> usize {
2757 self.vscroll.offset()
2758 }
2759
2760 pub fn vertical_page(&self) -> usize {
2762 self.vscroll.page_len()
2763 }
2764
2765 pub fn vertical_scroll(&self) -> usize {
2767 self.vscroll.scroll_by()
2768 }
2769
2770 pub fn horizontal_max_offset(&self) -> usize {
2774 self.hscroll.max_offset()
2775 }
2776
2777 pub fn horizontal_offset(&self) -> usize {
2779 self.hscroll.offset()
2780 }
2781
2782 pub fn horizontal_page(&self) -> usize {
2784 self.hscroll.page_len()
2785 }
2786
2787 pub fn horizontal_scroll(&self) -> usize {
2789 self.hscroll.scroll_by()
2790 }
2791
2792 pub fn set_vertical_offset(&mut self, row_offset: usize) -> bool {
2799 self.scroll_to_cursor = false;
2800 self.sub_row_offset = 0;
2801 self.vscroll.set_offset(row_offset)
2802 }
2803
2804 pub fn set_horizontal_offset(&mut self, col_offset: usize) -> bool {
2813 self.scroll_to_cursor = false;
2814 self.hscroll.set_offset(col_offset)
2815 }
2816
2817 pub fn scroll_to_row(&mut self, pos: usize) -> bool {
2819 self.scroll_to_cursor = false;
2820 self.vscroll.set_offset(pos)
2821 }
2822
2823 pub fn scroll_to_col(&mut self, pos: usize) -> bool {
2827 self.scroll_to_cursor = false;
2828 self.hscroll.set_offset(pos)
2829 }
2830
2831 pub fn scroll_up(&mut self, delta: usize) -> bool {
2833 if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2834 self.sub_row_offset = pos.x;
2835 self.vscroll.set_offset(pos.y as usize);
2836 true
2837 } else {
2838 self.sub_row_offset = 0;
2839
2840 let old = self.vscroll.offset;
2843 self.vscroll.offset = min(
2844 self.vscroll.offset.saturating_sub(delta),
2845 self.vscroll
2846 .max_offset
2847 .saturating_add(self.vscroll.page_len())
2848 .saturating_add(self.vscroll.overscroll_by()),
2849 );
2850 old != self.vscroll.offset
2851 }
2852 }
2853
2854 pub fn scroll_down(&mut self, delta: usize) -> bool {
2856 if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2857 self.sub_row_offset = pos.x;
2858 self.vscroll.set_offset(pos.y as usize);
2859 true
2860 } else {
2861 self.sub_row_offset = 0;
2862
2863 let old = self.vscroll.offset;
2866 self.vscroll.offset = min(
2867 self.vscroll.offset.saturating_add(delta),
2868 self.vscroll
2869 .max_offset
2870 .saturating_add(self.vscroll.page_len())
2871 .saturating_add(self.vscroll.overscroll_by()),
2872 );
2873
2874 old != self.vscroll.offset
2875 }
2876 }
2877
2878 pub fn scroll_left(&mut self, delta: usize) -> bool {
2882 self.hscroll.scroll_left(delta)
2883 }
2884
2885 pub fn scroll_right(&mut self, delta: usize) -> bool {
2889 self.hscroll.scroll_right(delta)
2890 }
2891
2892 pub fn scroll_sub_row_offset(&mut self, col: upos_type) -> bool {
2898 if let Ok(max_col) = self.try_line_width(self.offset().1 as upos_type) {
2899 self.sub_row_offset = min(col as upos_type, max_col);
2900 } else {
2901 self.sub_row_offset = 0;
2902 }
2903 true
2904 }
2905}
2906
2907impl TextAreaState {
2908 pub fn scroll_cursor_to_visible(&mut self) {
2911 self.scroll_to_cursor = true;
2912 }
2913}
2914
2915impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2916 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2917 fn tc(r: bool) -> TextOutcome {
2919 if r {
2920 TextOutcome::TextChanged
2921 } else {
2922 TextOutcome::Unchanged
2923 }
2924 }
2925
2926 let mut r = if self.is_focused() {
2927 match event {
2928 ct_event!(key press c)
2929 | ct_event!(key press SHIFT-c)
2930 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2931 ct_event!(keycode press Tab) => {
2932 tc(if !self.focus.gained() {
2934 self.insert_tab()
2935 } else {
2936 false
2937 })
2938 }
2939 ct_event!(keycode press SHIFT-BackTab) => {
2940 tc(if !self.focus.gained() {
2942 self.insert_backtab()
2943 } else {
2944 false
2945 })
2946 }
2947 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2948 ct_event!(keycode press Backspace) => tc(self.delete_prev_char()),
2949 ct_event!(keycode press Delete) => tc(self.delete_next_char()),
2950 ct_event!(keycode press CONTROL-Backspace)
2951 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2952 ct_event!(keycode press CONTROL-Delete) | ct_event!(keycode press ALT-Delete) => {
2953 tc(self.delete_next_word())
2954 }
2955 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2956 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2957 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2958 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2959 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2960 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2961
2962 ct_event!(key release _)
2963 | ct_event!(key release SHIFT-_)
2964 | ct_event!(key release CONTROL_ALT-_)
2965 | ct_event!(keycode release Tab)
2966 | ct_event!(keycode release Enter)
2967 | ct_event!(keycode release Backspace)
2968 | ct_event!(keycode release Delete)
2969 | ct_event!(keycode release CONTROL-Backspace)
2970 | ct_event!(keycode release ALT-Backspace)
2971 | ct_event!(keycode release CONTROL-Delete)
2972 | ct_event!(key release CONTROL-'x')
2973 | ct_event!(key release CONTROL-'v')
2974 | ct_event!(key release CONTROL-'d')
2975 | ct_event!(key release CONTROL-'y')
2976 | ct_event!(key release CONTROL-'z')
2977 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2978 _ => TextOutcome::Continue,
2979 }
2980 } else {
2981 TextOutcome::Continue
2982 };
2983 if r == TextOutcome::Continue {
2984 r = self.handle(event, ReadOnly);
2985 }
2986 r
2987 }
2988}
2989
2990impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
2991 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2992 let mut r = if self.is_focused() {
2993 match event {
2994 ct_event!(keycode press Left) => self.move_left(1, false).into(),
2995 ct_event!(keycode press Right) => self.move_right(1, false).into(),
2996 ct_event!(keycode press Up) => self.move_up(1, false).into(),
2997 ct_event!(keycode press Down) => self.move_down(1, false).into(),
2998 ct_event!(keycode press PageUp) => {
2999 self.move_up(self.vertical_page() as u16, false).into()
3000 }
3001 ct_event!(keycode press PageDown) => {
3002 self.move_down(self.vertical_page() as u16, false).into()
3003 }
3004 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
3005 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
3006 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
3007 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
3008 ct_event!(keycode press CONTROL-Up) => false.into(),
3009 ct_event!(keycode press CONTROL-Down) => false.into(),
3010 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
3011 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
3012 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
3013 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
3014
3015 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
3016 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
3017 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
3018 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
3019 ct_event!(keycode press ALT-PageUp) => {
3020 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
3021 }
3022 ct_event!(keycode press ALT-PageDown) => {
3023 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
3024 }
3025 ct_event!(keycode press ALT_SHIFT-PageUp) => {
3026 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
3027 }
3028 ct_event!(keycode press ALT_SHIFT-PageDown) => {
3029 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
3030 }
3031
3032 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
3033 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
3034 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
3035 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
3036 ct_event!(keycode press SHIFT-PageUp) => {
3037 self.move_up(self.vertical_page() as u16, true).into()
3038 }
3039 ct_event!(keycode press SHIFT-PageDown) => {
3040 self.move_down(self.vertical_page() as u16, true).into()
3041 }
3042 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
3043 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
3044 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
3045 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
3046 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
3047 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
3048 ct_event!(key press CONTROL-'a') => self.select_all().into(),
3049 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
3050
3051 ct_event!(keycode release Left)
3052 | ct_event!(keycode release Right)
3053 | ct_event!(keycode release Up)
3054 | ct_event!(keycode release Down)
3055 | ct_event!(keycode release PageUp)
3056 | ct_event!(keycode release PageDown)
3057 | ct_event!(keycode release Home)
3058 | ct_event!(keycode release End)
3059 | ct_event!(keycode release CONTROL-Left)
3060 | ct_event!(keycode release CONTROL-Right)
3061 | ct_event!(keycode release CONTROL-Up)
3062 | ct_event!(keycode release CONTROL-Down)
3063 | ct_event!(keycode release CONTROL-PageUp)
3064 | ct_event!(keycode release CONTROL-PageDown)
3065 | ct_event!(keycode release CONTROL-Home)
3066 | ct_event!(keycode release CONTROL-End)
3067 | ct_event!(keycode release ALT-Left)
3068 | ct_event!(keycode release ALT-Right)
3069 | ct_event!(keycode release ALT-Up)
3070 | ct_event!(keycode release ALT-Down)
3071 | ct_event!(keycode release ALT-PageUp)
3072 | ct_event!(keycode release ALT-PageDown)
3073 | ct_event!(keycode release ALT_SHIFT-PageUp)
3074 | ct_event!(keycode release ALT_SHIFT-PageDown)
3075 | ct_event!(keycode release SHIFT-Left)
3076 | ct_event!(keycode release SHIFT-Right)
3077 | ct_event!(keycode release SHIFT-Up)
3078 | ct_event!(keycode release SHIFT-Down)
3079 | ct_event!(keycode release SHIFT-PageUp)
3080 | ct_event!(keycode release SHIFT-PageDown)
3081 | ct_event!(keycode release SHIFT-Home)
3082 | ct_event!(keycode release SHIFT-End)
3083 | ct_event!(keycode release CONTROL_SHIFT-Left)
3084 | ct_event!(keycode release CONTROL_SHIFT-Right)
3085 | ct_event!(keycode release CONTROL_SHIFT-Home)
3086 | ct_event!(keycode release CONTROL_SHIFT-End)
3087 | ct_event!(key release CONTROL-'a')
3088 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
3089 _ => TextOutcome::Continue,
3090 }
3091 } else {
3092 TextOutcome::Continue
3093 };
3094
3095 if r == TextOutcome::Continue {
3096 r = self.handle(event, MouseOnly);
3097 }
3098 r
3099 }
3100}
3101
3102impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
3103 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
3104 flow!(match event {
3105 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
3106 let cx = m.column as i16 - self.inner.x as i16;
3107 let cy = m.row as i16 - self.inner.y as i16;
3108 self.set_screen_cursor((cx, cy), true).into()
3109 }
3110 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
3111 let cx = m.column as i16 - self.inner.x as i16;
3112 let cy = m.row as i16 - self.inner.y as i16;
3113 self.set_screen_cursor_words((cx, cy), true).into()
3114 }
3115 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
3116 if let Some(test) = self.screen_to_pos((m.column, m.row)) {
3117 let start = self.word_start(test);
3118 let end = self.word_end(test);
3119 self.set_selection(start, end).into()
3120 } else {
3121 TextOutcome::Unchanged
3122 }
3123 }
3124 ct_event!(mouse down Left for column,row) => {
3125 if self.inner.contains((*column, *row).into()) {
3126 let cx = (column - self.inner.x) as i16;
3127 let cy = (row - self.inner.y) as i16;
3128 self.set_screen_cursor((cx, cy), false).into()
3129 } else {
3130 TextOutcome::Continue
3131 }
3132 }
3133 ct_event!(mouse down SHIFT-Left for column,row) => {
3134 if self.inner.contains((*column, *row).into()) {
3135 let cx = (column - self.inner.x) as i16;
3136 let cy = (row - self.inner.y) as i16;
3137 self.set_screen_cursor((cx, cy), true).into()
3138 } else {
3139 TextOutcome::Continue
3140 }
3141 }
3142 ct_event!(mouse down CONTROL-Left for column,row) => {
3143 if self.inner.contains((*column, *row).into()) {
3144 let cx = (column - self.inner.x) as i16;
3145 let cy = (row - self.inner.y) as i16;
3146 self.set_screen_cursor((cx, cy), true).into()
3147 } else {
3148 TextOutcome::Continue
3149 }
3150 }
3151 ct_event!(mouse down ALT-Left for column,row) => {
3152 if self.inner.contains((*column, *row).into()) {
3153 let cx = (column - self.inner.x) as i16;
3154 let cy = (row - self.inner.y) as i16;
3155 self.set_screen_cursor_words((cx, cy), true).into()
3156 } else {
3157 TextOutcome::Continue
3158 }
3159 }
3160 _ => TextOutcome::Continue,
3161 });
3162
3163 let mut sas = ScrollAreaState::new()
3164 .area(self.inner)
3165 .h_scroll(&mut self.hscroll)
3166 .v_scroll(&mut self.vscroll);
3167 let r = match sas.handle(event, MouseOnly) {
3168 ScrollOutcome::Up(v) => self.scroll_up(v),
3169 ScrollOutcome::Down(v) => self.scroll_down(v),
3170 ScrollOutcome::Left(v) => self.scroll_left(v),
3171 ScrollOutcome::Right(v) => self.scroll_right(v),
3172 ScrollOutcome::VPos(v) => self.set_vertical_offset(v),
3173 ScrollOutcome::HPos(v) => self.set_horizontal_offset(v),
3174 _ => false,
3175 };
3176 if r {
3177 return TextOutcome::Changed;
3178 }
3179
3180 TextOutcome::Continue
3181 }
3182}
3183
3184pub fn handle_events(
3188 state: &mut TextAreaState,
3189 focus: bool,
3190 event: &crossterm::event::Event,
3191) -> TextOutcome {
3192 state.focus.set(focus);
3193 state.handle(event, Regular)
3194}
3195
3196pub fn handle_readonly_events(
3200 state: &mut TextAreaState,
3201 focus: bool,
3202 event: &crossterm::event::Event,
3203) -> TextOutcome {
3204 state.focus.set(focus);
3205 state.handle(event, ReadOnly)
3206}
3207
3208pub fn handle_mouse_events(
3210 state: &mut TextAreaState,
3211 event: &crossterm::event::Event,
3212) -> TextOutcome {
3213 state.handle(event, MouseOnly)
3214}