1use crate::_private::NonExhaustive;
7use crate::clipboard::{Clipboard, global_clipboard};
8use crate::event::{ReadOnly, TextOutcome};
9use crate::glyph2::{GlyphIter2, TextWrap2};
10use crate::text_area::text_area_op::*;
11use crate::text_core::TextCore;
12use crate::text_core::core_op::*;
13use crate::text_store::TextStore;
14use crate::text_store::text_rope::TextRope;
15use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
16use crate::{HasScreenCursor, TextError, TextPosition, TextRange, TextStyle, ipos_type, upos_type};
17use crossterm::event::KeyModifiers;
18use rat_event::util::MouseFlags;
19use rat_event::{HandleEvent, MouseOnly, Regular, ct_event, flow};
20use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
21use rat_reloc::{RelocatableState, relocate_area, relocate_dark_offset, relocate_pos_tuple};
22use rat_scrolled::event::ScrollOutcome;
23use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState};
24use ratatui::buffer::Buffer;
25use ratatui::layout::{Rect, Size};
26use ratatui::style::{Style, Stylize};
27use ratatui::widgets::{Block, StatefulWidget};
28use ropey::Rope;
29use std::borrow::Cow;
30use std::cmp::{max, min};
31use std::collections::HashMap;
32use std::ops::Range;
33
34pub mod text_area_op;
35
36#[derive(Debug, Default, Clone)]
76pub struct TextArea<'a> {
77 block: Option<Block<'a>>,
78 hscroll: Option<Scroll<'a>>,
79 h_max_offset: Option<upos_type>,
80 h_overscroll: Option<upos_type>,
81 vscroll: Option<Scroll<'a>>,
82
83 text_wrap: Option<TextWrap>,
84
85 style: Style,
86 focus_style: Option<Style>,
87 select_style: Option<Style>,
88 text_style: HashMap<usize, Style>,
89}
90
91#[derive(Debug)]
93pub struct TextAreaState {
94 pub area: Rect,
97 pub inner: Rect,
100 pub rendered: Size,
104 pub screen_cursor: Option<(u16, u16)>,
106
107 pub hscroll: ScrollState,
111 pub vscroll: ScrollState,
114 pub sub_row_offset: upos_type,
119 pub dark_offset: (u16, u16),
122 pub scroll_to_cursor: bool,
128
129 pub value: TextCore<TextRope>,
131
132 pub move_col: Option<i16>,
140 pub auto_indent: bool,
142 pub auto_quote: bool,
144 pub text_wrap: TextWrap,
146 pub newline: String,
148 pub tab_width: u32,
150 pub expand_tabs: bool,
152
153 pub focus: FocusFlag,
155
156 pub mouse: MouseFlags,
159
160 pub non_exhaustive: NonExhaustive,
161}
162
163impl Clone for TextAreaState {
164 fn clone(&self) -> Self {
165 Self {
166 area: self.area,
167 inner: self.inner,
168 rendered: self.rendered,
169 screen_cursor: self.screen_cursor,
170 hscroll: self.hscroll.clone(),
171 vscroll: self.vscroll.clone(),
172 sub_row_offset: self.sub_row_offset,
173 dark_offset: self.dark_offset,
174 scroll_to_cursor: self.scroll_to_cursor,
175 value: self.value.clone(),
176 move_col: None,
177 auto_indent: self.auto_indent,
178 auto_quote: self.auto_quote,
179 text_wrap: self.text_wrap,
180 newline: self.newline.clone(),
181 tab_width: self.tab_width,
182 expand_tabs: self.expand_tabs,
183 focus: FocusFlag::named(self.focus.name()),
184 mouse: Default::default(),
185 non_exhaustive: NonExhaustive,
186 }
187 }
188}
189
190#[derive(Debug, Default, Clone, Copy)]
192#[non_exhaustive]
193pub enum TextWrap {
194 #[default]
196 Shift,
197 Hard,
199 Word(u16),
209}
210
211impl<'a> TextArea<'a> {
212 pub fn new() -> Self {
214 Self::default()
215 }
216
217 #[inline]
219 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
220 if let Some(styles) = styles {
221 self.styles(styles)
222 } else {
223 self
224 }
225 }
226
227 #[inline]
229 pub fn styles(mut self, styles: TextStyle) -> Self {
230 self.style = styles.style;
231 if styles.focus.is_some() {
232 self.focus_style = styles.focus;
233 }
234 if styles.select.is_some() {
235 self.select_style = styles.select;
236 }
237 if let Some(border_style) = styles.border_style {
238 self.block = self.block.map(|v| v.border_style(border_style));
239 }
240 self.block = self.block.map(|v| v.style(self.style));
241 if styles.block.is_some() {
242 self.block = styles.block;
243 }
244 if let Some(styles) = styles.scroll {
245 self.hscroll = self.hscroll.map(|v| v.styles(styles.clone()));
246 self.vscroll = self.vscroll.map(|v| v.styles(styles));
247 }
248 self
249 }
250
251 pub fn style(mut self, style: Style) -> Self {
253 self.style = style;
254 self
255 }
256
257 pub fn focus_style(mut self, style: Style) -> Self {
259 self.focus_style = Some(style);
260 self.block = self.block.map(|v| v.style(self.style));
261 self
262 }
263
264 pub fn select_style(mut self, style: Style) -> Self {
266 self.select_style = Some(style);
267 self
268 }
269
270 pub fn text_style_idx(mut self, idx: usize, style: Style) -> Self {
275 self.text_style.insert(idx, style);
276 self
277 }
278
279 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
284 for (i, s) in styles.into_iter().enumerate() {
285 self.text_style.insert(i, s);
286 }
287 self
288 }
289
290 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
295 for (i, s) in styles.into_iter() {
296 self.text_style.insert(i, s.into());
297 }
298 self
299 }
300
301 #[inline]
303 pub fn block(mut self, block: Block<'a>) -> Self {
304 self.block = Some(block);
305 self
306 }
307
308 pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
310 self.hscroll = Some(scroll.clone().override_horizontal());
311 self.vscroll = Some(scroll.override_vertical());
312 self
313 }
314
315 pub fn hscroll(mut self, scroll: Scroll<'a>) -> Self {
317 self.hscroll = Some(scroll.override_horizontal());
318 self
319 }
320
321 pub fn text_wrap(mut self, wrap: TextWrap) -> Self {
323 self.text_wrap = Some(wrap);
324 self
325 }
326
327 pub fn set_horizontal_max_offset(mut self, offset: u32) -> Self {
344 self.h_max_offset = Some(offset);
345 self
346 }
347
348 pub fn set_horizontal_overscroll(mut self, overscroll: u32) -> Self {
355 self.h_overscroll = Some(overscroll);
356 self
357 }
358
359 pub fn vscroll(mut self, scroll: Scroll<'a>) -> Self {
361 self.vscroll = Some(scroll.override_vertical());
362 self
363 }
364}
365
366impl<'a> StatefulWidget for &TextArea<'a> {
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
374impl StatefulWidget for TextArea<'_> {
375 type State = TextAreaState;
376
377 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
378 render_text_area(&self, area, buf, state);
379 }
380}
381
382fn render_text_area(
383 widget: &TextArea<'_>,
384 area: Rect,
385 buf: &mut Buffer,
386 state: &mut TextAreaState,
387) {
388 state.area = area;
389 state.screen_cursor = None;
390 if let Some(text_wrap) = widget.text_wrap {
391 state.text_wrap = text_wrap;
392 }
393
394 let style = widget.style;
395 let focus_style = if let Some(focus_style) = widget.focus_style {
396 focus_style
397 } else {
398 style
399 };
400 let select_style = if let Some(select_style) = widget.select_style {
401 select_style
402 } else {
403 Style::default().black().on_yellow()
404 };
405 let (style, select_style) = if state.is_focused() {
406 (
407 style.patch(focus_style),
408 style.patch(focus_style).patch(select_style),
409 )
410 } else {
411 (style, style.patch(select_style))
412 };
413
414 state.area = area;
416 state.screen_cursor = None;
417 state.inner = ScrollArea::new()
418 .block(widget.block.as_ref())
419 .h_scroll(widget.hscroll.as_ref())
420 .v_scroll(widget.vscroll.as_ref())
421 .inner(area, Some(&state.hscroll), Some(&state.vscroll));
422 state.rendered = state.inner.as_size();
423
424 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
425 state.hscroll.set_max_offset(0);
426 state.hscroll.set_overscroll_by(None);
427 } else {
428 if let Some(h_max_offset) = widget.h_max_offset {
429 state.hscroll.set_max_offset(h_max_offset as usize);
430 }
431 if let Some(h_overscroll) = widget.h_overscroll {
432 state.hscroll.set_overscroll_by(Some(h_overscroll as usize));
433 }
434 }
435 state.hscroll.set_page_len(state.inner.width as usize);
436
437 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
438 state
439 .vscroll
440 .set_max_offset(state.len_lines().saturating_sub(1) as usize);
441 } else {
442 state.vscroll.set_max_offset(
443 state
444 .len_lines()
445 .saturating_sub(state.inner.height as upos_type) as usize,
446 );
447 }
448 state.vscroll.set_page_len(state.inner.height as usize);
449
450 if state.scroll_to_cursor {
451 state.scroll_to_pos(state.cursor());
452 }
453
454 ScrollArea::new()
456 .block(widget.block.as_ref())
457 .h_scroll(widget.hscroll.as_ref())
458 .v_scroll(widget.vscroll.as_ref())
459 .style(style)
460 .render(
461 area,
462 buf,
463 &mut ScrollAreaState::new()
464 .h_scroll(&mut state.hscroll)
465 .v_scroll(&mut state.vscroll),
466 );
467
468 if state.inner.width == 0 || state.inner.height == 0 {
469 return;
471 }
472 if state.vscroll.offset() > state.value.len_lines() as usize {
473 return;
475 }
476
477 let (shift_left, sub_row_offset, start_row) = state.clean_offset();
478 let page_rows = start_row
479 ..min(
480 start_row + state.inner.height as upos_type,
481 state.value.len_lines(),
482 );
483 let page_bytes = state
484 .try_bytes_at_range(TextRange::new(
485 (sub_row_offset, page_rows.start),
486 (0, page_rows.end),
487 ))
488 .expect("valid_rows");
489 let selection = state.selection();
491 let mut styles = Vec::new();
492
493 for g in state
494 .glyphs2(shift_left, sub_row_offset, page_rows)
495 .expect("valid_offset")
496 {
497 let screen_pos = g.screen_pos();
499
500 if screen_pos.1 >= state.inner.height {
501 break;
502 }
503
504 if g.screen_width() > 0 {
505 let mut style = style;
506 state
508 .value
509 .styles_at_page(g.text_bytes().start, page_bytes.clone(), &mut styles);
510 for style_nr in &styles {
511 if let Some(s) = widget.text_style.get(style_nr) {
512 style = style.patch(*s);
513 }
514 }
515 if selection.contains_pos(g.pos()) {
517 style = style.patch(select_style);
518 };
519
520 if let Some(cell) =
522 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
523 {
524 cell.set_symbol(g.glyph());
525 cell.set_style(style);
526 }
527 for d in 1..g.screen_width() {
529 if let Some(cell) = buf.cell_mut((
530 state.inner.x + screen_pos.0 + d,
531 state.inner.y + screen_pos.1,
532 )) {
533 cell.reset();
534 cell.set_style(style);
535 }
536 }
537 }
538 }
539
540 state.screen_cursor = state.pos_to_screen(state.cursor());
541 }
543
544impl Default for TextAreaState {
545 fn default() -> Self {
546 #[cfg(windows)]
547 const LINE_ENDING: &str = "\r\n";
548
549 #[cfg(not(windows))]
550 const LINE_ENDING: &str = "\n";
551
552 let mut s = Self {
553 area: Default::default(),
554 inner: Default::default(),
555 rendered: Default::default(),
556 screen_cursor: Default::default(),
557 hscroll: Default::default(),
558 vscroll: Default::default(),
559 sub_row_offset: 0,
560 dark_offset: Default::default(),
561 scroll_to_cursor: Default::default(),
562 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
563 move_col: Default::default(),
564 auto_indent: true,
565 auto_quote: true,
566 text_wrap: TextWrap::Shift,
567 newline: LINE_ENDING.to_string(),
568 tab_width: 8,
569 expand_tabs: true,
570 focus: Default::default(),
571 mouse: Default::default(),
572 non_exhaustive: NonExhaustive,
573 };
574 s.hscroll.set_max_offset(255);
575 s.hscroll.set_overscroll_by(Some(16384));
576 s
577 }
578}
579
580impl HasFocus for TextAreaState {
581 fn build(&self, builder: &mut FocusBuilder) {
582 builder.leaf_widget(self);
583 }
584
585 fn focus(&self) -> FocusFlag {
586 self.focus.clone()
587 }
588
589 fn area(&self) -> Rect {
590 self.area
591 }
592
593 fn navigable(&self) -> Navigation {
594 Navigation::Reach
595 }
596}
597
598impl TextAreaState {
599 #[inline]
601 pub fn new() -> Self {
602 Self::default()
603 }
604
605 #[inline]
607 pub fn named(name: &str) -> Self {
608 Self {
609 focus: FocusFlag::named(name),
610 ..Default::default()
611 }
612 }
613
614 #[inline]
620 pub fn set_newline(&mut self, br: impl Into<String>) {
621 self.newline = br.into();
622 }
623
624 #[inline]
626 pub fn newline(&self) -> &str {
627 &self.newline
628 }
629
630 #[inline]
632 pub fn set_auto_indent(&mut self, indent: bool) {
633 self.auto_indent = indent;
634 }
635
636 #[inline]
638 pub fn set_auto_quote(&mut self, quote: bool) {
639 self.auto_quote = quote;
640 }
641
642 #[inline]
644 pub fn set_tab_width(&mut self, tabs: u32) {
645 self.tab_width = tabs;
646 }
647
648 #[inline]
650 pub fn tab_width(&self) -> u32 {
651 self.tab_width
652 }
653
654 #[inline]
656 pub fn set_expand_tabs(&mut self, expand: bool) {
657 self.expand_tabs = expand;
658 }
659
660 #[inline]
662 pub fn expand_tabs(&self) -> bool {
663 self.expand_tabs
664 }
665
666 #[inline]
668 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
669 self.value.set_glyph_ctrl(show_ctrl);
670 }
671
672 pub fn show_ctrl(&self) -> bool {
674 self.value.glyph_ctrl()
675 }
676
677 #[inline]
680 pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
681 self.value.set_wrap_ctrl(wrap_ctrl);
682 }
683
684 pub fn wrap_ctrl(&self) -> bool {
687 self.value.wrap_ctrl()
688 }
689
690 pub fn set_text_wrap(&mut self, text_wrap: TextWrap) {
703 self.text_wrap = text_wrap;
704 }
705
706 pub fn text_wrap(&self) -> TextWrap {
708 self.text_wrap
709 }
710
711 #[inline]
721 pub fn set_move_col(&mut self, col: Option<i16>) {
722 self.move_col = col;
723 }
724
725 #[inline]
727 pub fn move_col(&mut self) -> Option<i16> {
728 self.move_col
729 }
730}
731
732impl TextAreaState {
733 #[inline]
736 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
737 match clip {
738 None => self.value.set_clipboard(None),
739 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
740 }
741 }
742
743 #[inline]
746 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
747 self.value.clipboard()
748 }
749
750 #[inline]
752 pub fn copy_to_clip(&mut self) -> bool {
753 let Some(clip) = self.value.clipboard() else {
754 return false;
755 };
756
757 _ = clip.set_string(self.selected_text().as_ref());
758 false
759 }
760
761 #[inline]
763 pub fn cut_to_clip(&mut self) -> bool {
764 let Some(clip) = self.value.clipboard() else {
765 return false;
766 };
767
768 match clip.set_string(self.selected_text().as_ref()) {
769 Ok(_) => self.delete_range(self.selection()),
770 Err(_) => false,
771 }
772 }
773
774 #[inline]
776 pub fn paste_from_clip(&mut self) -> bool {
777 let Some(clip) = self.value.clipboard() else {
778 return false;
779 };
780
781 if let Ok(text) = clip.get_string() {
782 self.insert_str(text)
783 } else {
784 false
785 }
786 }
787}
788
789impl TextAreaState {
790 #[inline]
797 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
798 match undo {
799 None => self.value.set_undo_buffer(None),
800 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
801 }
802 }
803
804 #[inline]
806 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
807 self.value.undo_buffer()
808 }
809
810 #[inline]
812 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
813 self.value.undo_buffer_mut()
814 }
815
816 #[inline]
822 pub fn begin_undo_seq(&mut self) {
823 self.value.begin_undo_seq()
824 }
825
826 #[inline]
828 pub fn end_undo_seq(&mut self) {
829 self.value.end_undo_seq()
830 }
831
832 #[inline]
837 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
838 self.value.recent_replay_log()
839 }
840
841 #[inline]
845 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
846 self.value.replay_log(replay)
847 }
848
849 #[inline]
851 pub fn undo(&mut self) -> bool {
852 self.value.undo()
853 }
854
855 #[inline]
857 pub fn redo(&mut self) -> bool {
858 self.value.redo()
859 }
860}
861
862impl TextAreaState {
863 #[inline]
880 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
881 self.value.set_styles(styles);
882 }
883
884 #[inline]
895 pub fn set_range_styles(&mut self, styles: Vec<(TextRange, usize)>) -> Result<(), TextError> {
896 let mut mapped = Vec::with_capacity(styles.len());
897 for (r, s) in styles {
898 let rr = self.value.bytes_at_range(r)?;
899 mapped.push((rr, s));
900 }
901 self.value.set_styles(mapped);
902 Ok(())
903 }
904
905 #[inline]
910 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
911 self.value.add_style(range, style);
912 }
913
914 #[inline]
918 pub fn add_range_style(
919 &mut self,
920 range: impl Into<TextRange>,
921 style: usize,
922 ) -> Result<(), TextError> {
923 let r = self.value.bytes_at_range(range.into())?;
924 self.value.add_style(r, style);
925 Ok(())
926 }
927
928 #[inline]
930 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
931 self.value.remove_style(range, style);
932 }
933
934 #[inline]
936 pub fn remove_style_fully(&mut self, style: usize) {
937 self.value.remove_style_fully(style);
938 }
939
940 #[inline]
942 pub fn remove_range_style(
943 &mut self,
944 range: impl Into<TextRange>,
945 style: usize,
946 ) -> Result<(), TextError> {
947 let r = self.value.bytes_at_range(range.into())?;
948 self.value.remove_style(r, style);
949 Ok(())
950 }
951
952 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
954 self.value.styles_in(range, buf)
955 }
956
957 pub fn styles_in_match(
959 &self,
960 range: Range<usize>,
961 style: usize,
962 buf: &mut Vec<(Range<usize>, usize)>,
963 ) {
964 self.value.styles_in_match(range, style, buf);
965 }
966
967 #[inline]
969 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
970 self.value.styles_at(byte_pos, buf)
971 }
972
973 #[inline]
976 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
977 self.value.styles_at_match(byte_pos, style)
978 }
979
980 #[inline]
982 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
983 self.value.styles()
984 }
985}
986
987impl TextAreaState {
988 #[inline]
990 pub fn offset(&self) -> (upos_type, upos_type) {
991 (
992 self.hscroll.offset() as upos_type,
993 self.vscroll.offset() as upos_type,
994 )
995 }
996
997 #[inline]
1002 pub fn set_offset(&mut self, offset: (upos_type, upos_type)) -> bool {
1003 self.scroll_to_cursor = false;
1004 let c = self.hscroll.set_offset(offset.0 as usize);
1005 let r = self.vscroll.set_offset(offset.1 as usize);
1006 r || c
1007 }
1008
1009 pub fn set_sub_row_offset(&mut self, sub_row_offset: upos_type) -> bool {
1021 self.scroll_to_cursor = false;
1022 let old = self.sub_row_offset;
1023 self.sub_row_offset = sub_row_offset;
1024 sub_row_offset != old
1025 }
1026
1027 pub fn sub_row_offset(&self) -> upos_type {
1032 self.sub_row_offset
1033 }
1034
1035 fn clean_offset(&self) -> (upos_type, upos_type, upos_type) {
1040 let ox = self.hscroll.offset as upos_type;
1041 let mut oy = self.vscroll.offset as upos_type;
1042
1043 if oy >= self.len_lines() {
1045 oy = 0;
1046 }
1047
1048 match self.text_wrap {
1049 TextWrap::Shift => (ox, 0, oy),
1050 TextWrap::Hard | TextWrap::Word(_) => {
1051 if let Ok(max_col) = self.try_line_width(oy) {
1053 (0, min(self.sub_row_offset, max_col), oy)
1054 } else {
1055 (0, 0, oy)
1056 }
1057 }
1058 }
1059 }
1060
1061 #[inline]
1063 pub fn cursor(&self) -> TextPosition {
1064 self.value.cursor()
1065 }
1066
1067 #[inline]
1069 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
1070 self.scroll_cursor_to_visible();
1071 self.value.set_cursor(cursor.into(), extend_selection)
1072 }
1073
1074 #[inline]
1076 pub fn anchor(&self) -> TextPosition {
1077 self.value.anchor()
1078 }
1079
1080 #[inline]
1082 pub fn has_selection(&self) -> bool {
1083 self.value.has_selection()
1084 }
1085
1086 #[inline]
1088 pub fn selection(&self) -> TextRange {
1089 self.value.selection()
1090 }
1091
1092 #[inline]
1095 pub fn set_selection(
1096 &mut self,
1097 anchor: impl Into<TextPosition>,
1098 cursor: impl Into<TextPosition>,
1099 ) -> bool {
1100 self.scroll_cursor_to_visible();
1101 self.value.set_selection(anchor.into(), cursor.into())
1102 }
1103
1104 #[inline]
1107 pub fn select_all(&mut self) -> bool {
1108 self.scroll_cursor_to_visible();
1109 self.value.select_all()
1110 }
1111
1112 #[inline]
1114 pub fn selected_text(&self) -> Cow<'_, str> {
1115 self.value
1116 .str_slice(self.value.selection())
1117 .expect("valid_selection")
1118 }
1119}
1120
1121impl TextAreaState {
1122 #[inline]
1124 pub fn is_empty(&self) -> bool {
1125 self.value.is_empty()
1126 }
1127
1128 #[inline]
1130 pub fn rope(&self) -> &Rope {
1131 self.value.text().rope()
1132 }
1133
1134 #[inline]
1136 pub fn text(&self) -> String {
1137 self.value.text().string()
1138 }
1139
1140 #[inline]
1144 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1145 self.value.str_slice_byte(range).expect("valid_range")
1146 }
1147
1148 #[inline]
1150 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1151 self.value.str_slice_byte(range)
1152 }
1153
1154 #[inline]
1158 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
1159 self.value.str_slice(range.into()).expect("valid_range")
1160 }
1161
1162 #[inline]
1164 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
1165 self.value.str_slice(range.into())
1166 }
1167
1168 #[inline]
1170 pub fn len_lines(&self) -> upos_type {
1171 self.value.len_lines()
1172 }
1173
1174 #[inline]
1176 pub fn len_bytes(&self) -> usize {
1177 self.value.len_bytes()
1178 }
1179
1180 #[inline]
1184 pub fn line_width(&self, row: upos_type) -> upos_type {
1185 self.try_line_width(row).expect("valid_row")
1186 }
1187
1188 #[inline]
1190 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1191 self.value.line_width(row)
1192 }
1193
1194 #[inline]
1199 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
1200 self.value.line_at(row).expect("valid_row")
1201 }
1202
1203 #[inline]
1206 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1207 self.value.line_at(row)
1208 }
1209
1210 #[inline]
1214 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
1215 self.value.lines_at(row).expect("valid_row")
1216 }
1217
1218 #[inline]
1220 pub fn try_lines_at(
1221 &self,
1222 row: upos_type,
1223 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1224 self.value.lines_at(row)
1225 }
1226
1227 #[inline]
1232 pub fn line_graphemes(&self, row: upos_type) -> <TextRope as TextStore>::GraphemeIter<'_> {
1233 self.value.line_graphemes(row).expect("valid_row")
1234 }
1235
1236 #[inline]
1239 pub fn try_line_graphemes(
1240 &self,
1241 row: upos_type,
1242 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1243 self.value.line_graphemes(row)
1244 }
1245
1246 #[inline]
1250 pub fn text_graphemes(
1251 &self,
1252 pos: impl Into<TextPosition>,
1253 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1254 self.value.text_graphemes(pos.into()).expect("valid_pos")
1255 }
1256
1257 #[inline]
1259 pub fn try_text_graphemes(
1260 &self,
1261 pos: impl Into<TextPosition>,
1262 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1263 self.value.text_graphemes(pos.into())
1264 }
1265
1266 #[inline]
1270 pub fn graphemes(
1271 &self,
1272 range: impl Into<TextRange>,
1273 pos: impl Into<TextPosition>,
1274 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1275 self.value
1276 .graphemes(range.into(), pos.into())
1277 .expect("valid_args")
1278 }
1279
1280 #[inline]
1282 pub fn try_graphemes(
1283 &self,
1284 range: impl Into<TextRange>,
1285 pos: impl Into<TextPosition>,
1286 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1287 self.value.graphemes(range.into(), pos.into())
1288 }
1289
1290 #[inline]
1295 pub fn byte_at(&self, pos: impl Into<TextPosition>) -> Range<usize> {
1296 self.value.byte_at(pos.into()).expect("valid_pos")
1297 }
1298
1299 #[inline]
1302 pub fn try_byte_at(&self, pos: impl Into<TextPosition>) -> Result<Range<usize>, TextError> {
1303 self.value.byte_at(pos.into())
1304 }
1305
1306 #[inline]
1310 pub fn bytes_at_range(&self, range: impl Into<TextRange>) -> Range<usize> {
1311 self.value
1312 .bytes_at_range(range.into())
1313 .expect("valid_range")
1314 }
1315
1316 #[inline]
1318 pub fn try_bytes_at_range(
1319 &self,
1320 range: impl Into<TextRange>,
1321 ) -> Result<Range<usize>, TextError> {
1322 self.value.bytes_at_range(range.into())
1323 }
1324
1325 #[inline]
1330 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1331 self.value.byte_pos(byte).expect("valid_pos")
1332 }
1333
1334 #[inline]
1337 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1338 self.value.byte_pos(byte)
1339 }
1340
1341 #[inline]
1345 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1346 self.value.byte_range(bytes).expect("valid_range")
1347 }
1348
1349 #[inline]
1351 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1352 self.value.byte_range(bytes)
1353 }
1354}
1355
1356impl TextAreaState {
1357 #[inline]
1359 pub fn clear(&mut self) -> bool {
1360 if !self.is_empty() {
1361 self.value.clear();
1362 true
1363 } else {
1364 false
1365 }
1366 }
1367
1368 #[inline]
1372 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1373 self.scroll_to_cursor = false;
1374 self.vscroll.set_offset(0);
1375 self.hscroll.set_offset(0);
1376 self.set_sub_row_offset(0);
1377 self.set_move_col(None);
1378
1379 self.value.set_text(TextRope::new_text(s.as_ref()));
1380 }
1381
1382 #[inline]
1385 pub fn set_rope(&mut self, r: Rope) {
1386 self.scroll_to_cursor = false;
1387 self.vscroll.set_offset(0);
1388 self.hscroll.set_offset(0);
1389 self.set_sub_row_offset(0);
1390 self.set_move_col(None);
1391
1392 self.value.set_text(TextRope::new_rope(r));
1393 }
1394
1395 pub fn insert_char(&mut self, c: char) -> bool {
1406 if self.has_selection() {
1407 if self.auto_quote
1408 && (c == '\''
1409 || c == '"'
1410 || c == '`'
1411 || c == '<'
1412 || c == '['
1413 || c == '('
1414 || c == '{')
1415 {
1416 let sel = self.selection();
1417 insert_quotes(&mut self.value, sel, c).expect("valid_selection");
1418 self.scroll_cursor_to_visible();
1419 return true;
1420 }
1421 }
1422
1423 self.value
1424 .remove_str_range(self.selection())
1425 .expect("valid_selection");
1426
1427 let pos = self.cursor();
1428
1429 if pos.x == 0
1431 && pos.y != 0
1432 && (pos.y == self.len_lines() || pos.y == self.len_lines().saturating_sub(1))
1433 && !self.value.text().has_final_newline()
1434 {
1435 let anchor = self.value.anchor();
1436 let cursor = self.value.cursor();
1437 self.value
1438 .insert_str(pos, &self.newline)
1439 .expect("valid_cursor");
1440 self.value.set_selection(anchor, cursor);
1441 }
1442
1443 if c == '\n' {
1444 self.value
1445 .insert_str(pos, &self.newline)
1446 .expect("valid_cursor");
1447 } else if c == '\t' {
1448 insert_tab(&mut self.value, pos, self.expand_tabs, self.tab_width)
1449 .expect("valid_cursor");
1450 } else {
1451 self.value.insert_char(pos, c).expect("valid_cursor");
1452 }
1453
1454 self.scroll_cursor_to_visible();
1455
1456 true
1457 }
1458
1459 pub fn insert_tab(&mut self) -> bool {
1465 if self.has_selection() {
1466 if self.auto_indent {
1467 indent(self, self.tab_width);
1468 true
1469 } else {
1470 false
1471 }
1472 } else {
1473 let pos = self.cursor();
1474 insert_tab(&mut self.value, pos, self.expand_tabs, self.tab_width)
1475 .expect("valid_cursor");
1476 self.scroll_cursor_to_visible();
1477
1478 true
1479 }
1480 }
1481
1482 pub fn insert_backtab(&mut self) -> bool {
1487 if self.has_selection() {
1488 dedent(self, self.tab_width);
1489 true
1490 } else {
1491 false
1492 }
1493 }
1494
1495 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1498 let t = t.as_ref();
1499 if self.has_selection() {
1500 self.value
1501 .remove_str_range(self.selection())
1502 .expect("valid_selection");
1503 }
1504 self.value
1505 .insert_str(self.cursor(), t)
1506 .expect("valid_cursor");
1507 self.scroll_cursor_to_visible();
1508 true
1509 }
1510
1511 pub fn insert_newline(&mut self) -> bool {
1516 if self.has_selection() {
1517 self.value
1518 .remove_str_range(self.selection())
1519 .expect("valid_selection");
1520 }
1521
1522 self.value
1523 .insert_str(self.cursor(), &self.newline)
1524 .expect("valid_cursor");
1525
1526 if self.auto_indent {
1528 auto_indent(self);
1529 }
1530
1531 self.scroll_cursor_to_visible();
1532 true
1533 }
1534
1535 #[inline]
1539 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1540 self.try_delete_range(range).expect("valid_range")
1541 }
1542
1543 #[inline]
1545 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1546 let range = range.into();
1547 if !range.is_empty() {
1548 self.value.remove_str_range(range)?;
1549 self.scroll_cursor_to_visible();
1550 Ok(true)
1551 } else {
1552 Ok(false)
1553 }
1554 }
1555}
1556
1557impl TextAreaState {
1558 #[inline]
1561 pub fn duplicate_text(&mut self) -> bool {
1562 duplicate_text(self)
1563 }
1564
1565 #[inline]
1568 pub fn delete_line(&mut self) -> bool {
1569 delete_line(self)
1570 }
1571
1572 #[inline]
1575 pub fn delete_next_char(&mut self) -> bool {
1576 self.scroll_cursor_to_visible();
1577 delete_next_char(self)
1578 }
1579
1580 #[inline]
1583 pub fn delete_prev_char(&mut self) -> bool {
1584 self.scroll_cursor_to_visible();
1585 delete_prev_char(self)
1586 }
1587
1588 #[inline]
1593 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1594 next_word_start(&self.value, pos.into()).expect("valid_pos")
1595 }
1596
1597 #[inline]
1600 pub fn try_next_word_start(
1601 &self,
1602 pos: impl Into<TextPosition>,
1603 ) -> Result<TextPosition, TextError> {
1604 next_word_start(&self.value, pos.into())
1605 }
1606
1607 #[inline]
1612 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1613 next_word_end(&self.value, pos.into()).expect("valid_pos")
1614 }
1615
1616 #[inline]
1619 pub fn try_next_word_end(
1620 &self,
1621 pos: impl Into<TextPosition>,
1622 ) -> Result<TextPosition, TextError> {
1623 next_word_end(&self.value, pos.into())
1624 }
1625
1626 #[inline]
1634 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1635 prev_word_start(&self.value, pos.into()).expect("valid_pos")
1636 }
1637
1638 #[inline]
1644 pub fn try_prev_word_start(
1645 &self,
1646 pos: impl Into<TextPosition>,
1647 ) -> Result<TextPosition, TextError> {
1648 prev_word_start(&self.value, pos.into())
1649 }
1650
1651 #[inline]
1657 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1658 prev_word_end(&self.value, pos.into()).expect("valid_pos")
1659 }
1660
1661 #[inline]
1665 pub fn try_prev_word_end(
1666 &self,
1667 pos: impl Into<TextPosition>,
1668 ) -> Result<TextPosition, TextError> {
1669 prev_word_end(&self.value, pos.into())
1670 }
1671
1672 #[inline]
1676 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1677 is_word_boundary(&self.value, pos.into()).expect("valid_pos")
1678 }
1679
1680 #[inline]
1682 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1683 is_word_boundary(&self.value, pos.into())
1684 }
1685
1686 #[inline]
1691 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1692 word_start(&self.value, pos.into()).expect("valid_pos")
1693 }
1694
1695 #[inline]
1698 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1699 word_start(&self.value, pos.into())
1700 }
1701
1702 #[inline]
1707 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1708 word_end(&self.value, pos.into()).expect("valid_pos")
1709 }
1710
1711 #[inline]
1714 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1715 word_end(&self.value, pos.into())
1716 }
1717
1718 #[inline]
1723 pub fn delete_next_word(&mut self) -> bool {
1724 delete_next_word(self)
1725 }
1726
1727 #[inline]
1732 pub fn delete_prev_word(&mut self) -> bool {
1733 delete_prev_word(self)
1734 }
1735
1736 pub fn search(&mut self, re: &str) -> Result<bool, TextError> {
1747 match search(self, re, MATCH_STYLE) {
1748 Ok(r) => Ok(r),
1749 Err(_) => Err(TextError::InvalidSearch),
1750 }
1751 }
1752
1753 pub fn move_to_next_match(&mut self) -> bool {
1755 move_to_next_match(self, MATCH_STYLE)
1756 }
1757
1758 pub fn move_to_prev_match(&mut self) -> bool {
1760 move_to_prev_match(self, MATCH_STYLE)
1761 }
1762
1763 pub fn clear_search(&mut self) {
1765 clear_search(self, MATCH_STYLE)
1766 }
1767
1768 #[inline]
1771 pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1772 move_left(self, n, extend_selection)
1773 }
1774
1775 #[inline]
1778 pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1779 move_right(self, n, extend_selection)
1780 }
1781
1782 #[inline]
1785 pub fn move_up(&mut self, n: u16, extend_selection: bool) -> bool {
1786 move_up(self, n, extend_selection)
1787 }
1788
1789 #[inline]
1792 pub fn move_down(&mut self, n: u16, extend_selection: bool) -> bool {
1793 move_down(self, n, extend_selection)
1794 }
1795
1796 #[inline]
1800 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1801 move_to_line_start(self, extend_selection)
1802 }
1803
1804 #[inline]
1808 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1809 move_to_line_end(self, extend_selection)
1810 }
1811
1812 #[inline]
1814 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1815 move_to_start(self, extend_selection)
1816 }
1817
1818 #[inline]
1820 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1821 move_to_end(self, extend_selection)
1822 }
1823
1824 #[inline]
1826 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1827 move_to_screen_start(self, extend_selection)
1828 }
1829
1830 #[inline]
1832 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1833 move_to_screen_end(self, extend_selection)
1834 }
1835
1836 #[inline]
1838 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1839 move_to_next_word(self, extend_selection)
1840 }
1841
1842 #[inline]
1844 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1845 move_to_prev_word(self, extend_selection)
1846 }
1847}
1848
1849impl HasScreenCursor for TextAreaState {
1850 #[allow(clippy::question_mark)]
1852 fn screen_cursor(&self) -> Option<(u16, u16)> {
1853 if self.is_focused() {
1854 if self.has_selection() {
1855 None
1856 } else {
1857 let Some(scr_cursor) = self.screen_cursor else {
1858 return None;
1859 };
1860
1861 if !(scr_cursor.0 >= self.inner.x
1862 && scr_cursor.0 <= self.inner.right()
1863 && scr_cursor.1 >= self.inner.y
1864 && scr_cursor.1 < self.inner.bottom())
1865 {
1866 return None;
1867 }
1868 Some(scr_cursor)
1869 }
1870 } else {
1871 None
1872 }
1873 }
1874}
1875
1876impl RelocatableState for TextAreaState {
1877 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1878 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1880 self.area = relocate_area(self.area, shift, clip);
1881 self.inner = relocate_area(self.inner, shift, clip);
1882 if let Some(screen_cursor) = self.screen_cursor {
1883 self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
1884 }
1885 }
1886}
1887
1888impl TextAreaState {
1889 fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
1890 match self.text_wrap {
1891 TextWrap::Shift => (
1892 TextWrap2::Shift,
1893 shift_left,
1894 shift_left + self.rendered.width as upos_type,
1895 shift_left + self.rendered.width as upos_type,
1896 ),
1897 TextWrap::Hard => (
1898 TextWrap2::Hard,
1899 0,
1900 self.rendered.width as upos_type,
1901 self.rendered.width as upos_type,
1902 ),
1903 TextWrap::Word(margin) => (
1904 TextWrap2::Word,
1905 0,
1906 self.rendered.width as upos_type,
1907 self.rendered.width.saturating_sub(margin) as upos_type,
1908 ),
1909 }
1910 }
1911
1912 fn fill_cache(
1915 &self,
1916 shift_left: upos_type,
1917 sub_row_offset: upos_type,
1918 rows: Range<upos_type>,
1919 ) -> Result<(), TextError> {
1920 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1921 self.value.fill_cache(
1922 self.rendered,
1923 sub_row_offset,
1924 rows,
1925 self.tab_width,
1926 text_wrap,
1927 self.wrap_ctrl() | self.show_ctrl(),
1928 left_margin,
1929 right_margin,
1930 word_margin,
1931 )
1932 }
1933
1934 fn glyphs2(
1935 &self,
1936 shift_left: upos_type,
1937 sub_row_offset: upos_type,
1938 rows: Range<upos_type>,
1939 ) -> Result<GlyphIter2<'_, <TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
1940 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1941 self.value.glyphs2(
1942 self.rendered,
1943 sub_row_offset,
1944 rows,
1945 self.tab_width,
1946 text_wrap,
1947 self.wrap_ctrl() | self.show_ctrl(),
1948 left_margin,
1949 right_margin,
1950 word_margin,
1951 )
1952 }
1953
1954 pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
1956 let scr_pos = (
1957 scr_pos.0 as i16 - self.inner.x as i16,
1958 scr_pos.1 as i16 - self.inner.y as i16,
1959 );
1960 self.relative_screen_to_pos(scr_pos)
1961 }
1962
1963 #[inline]
1965 pub fn pos_to_screen(&self, pos: impl Into<TextPosition>) -> Option<(u16, u16)> {
1966 let scr_pos = self.pos_to_relative_screen(pos.into())?;
1967 if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
1968 Some((
1969 (scr_pos.0 + self.inner.x as i16) as u16,
1970 (scr_pos.1 + self.inner.y as i16) as u16,
1971 ))
1972 } else {
1973 None
1974 }
1975 }
1976
1977 pub fn pos_to_line_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1979 let pos = pos.into();
1980 match self.text_wrap {
1981 TextWrap::Shift => {
1982 TextPosition::new(0, pos.y)
1984 }
1985 TextWrap::Hard | TextWrap::Word(_) => {
1986 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
1987 .expect("valid-row");
1988
1989 let mut start_pos = TextPosition::new(0, pos.y);
1990 for (break_pos, _) in self.value.cache().line_break.borrow().range(
1991 TextPosition::new(0, pos.y)
1992 ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
1993 ) {
1994 if pos >= start_pos && &pos <= break_pos {
1995 break;
1996 }
1997 start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
1998 }
1999
2000 start_pos
2001 }
2002 }
2003 }
2004
2005 pub fn pos_to_line_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
2007 let pos = pos.into();
2008
2009 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2010 .expect("valid-row");
2011
2012 let mut end_pos = TextPosition::new(0, pos.y);
2013 for (break_pos, _) in self
2014 .value
2015 .cache()
2016 .line_break
2017 .borrow()
2018 .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2019 {
2020 if pos >= end_pos && &pos <= break_pos {
2021 end_pos = *break_pos;
2022 break;
2023 }
2024 end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2025 }
2026
2027 end_pos
2028 }
2029
2030 fn stc_fill_screen_cache(&self, scr: (upos_type, upos_type, upos_type)) {
2032 let y2 = scr.1 + scr.2;
2033 self.fill_cache(0, scr.0, scr.1..min(y2, self.len_lines()))
2034 .expect("valid-rows");
2035 }
2036
2037 fn stc_screen_row(
2039 &self,
2040 scr: (upos_type, upos_type, upos_type),
2041 pos: TextPosition,
2042 ) -> Option<upos_type> {
2043 if pos < TextPosition::new(scr.0, scr.1) {
2044 return None;
2045 }
2046
2047 let line_breaks = self.value.cache().line_break.borrow();
2048 let range_start = TextPosition::new(scr.0, scr.1);
2049 let y2 = scr.1 + scr.2;
2050 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
2051
2052 let mut start_pos = TextPosition::new(scr.0, scr.1);
2053 let mut scr_row = 0;
2054 for (_key, cache) in line_breaks.range(range_start..range_end) {
2055 if pos < cache.start_pos {
2056 return Some(scr_row);
2057 }
2058 scr_row += 1;
2059 start_pos = cache.start_pos;
2060 }
2061
2062 if pos == start_pos {
2064 return Some(scr_row);
2065 }
2066
2067 None
2068 }
2069
2070 fn stc_sub_row_offset(
2073 &self,
2074 scr: (upos_type, upos_type, upos_type),
2075 mut scr_row: upos_type,
2076 ) -> (upos_type, upos_type) {
2077 let line_breaks = self.value.cache().line_break.borrow();
2078 let range_start = TextPosition::new(scr.0, scr.1);
2079 let y2 = scr.1 + scr.2;
2080 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
2081
2082 let mut start_pos = (scr.0, scr.1);
2083 for (_key, cache) in line_breaks.range(range_start..range_end) {
2084 if scr_row == 0 {
2085 return start_pos;
2086 }
2087 scr_row -= 1;
2088 start_pos = (cache.start_pos.x, cache.start_pos.y);
2089 }
2090
2091 start_pos
2093 }
2094
2095 #[allow(clippy::explicit_counter_loop)]
2105 pub fn pos_to_relative_screen(&self, pos: impl Into<TextPosition>) -> Option<(i16, i16)> {
2106 let pos = pos.into();
2107 match self.text_wrap {
2108 TextWrap::Shift => {
2109 let (ox, _, oy) = self.clean_offset();
2110
2111 if oy > self.len_lines() {
2112 return None;
2113 }
2114 if pos.y < oy {
2115 return None;
2116 }
2117 if pos.y > self.len_lines() {
2118 return None;
2119 }
2120 if pos.y - oy >= self.rendered.height as u32 {
2121 return None;
2122 }
2123
2124 let screen_y = (pos.y - oy) as u16;
2125
2126 let screen_x = 'f: {
2127 for g in self
2128 .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2129 .expect("valid-row")
2130 {
2131 if g.pos().x == pos.x {
2132 break 'f g.screen_pos().0;
2133 } else if g.line_break() {
2134 break 'f g.screen_pos().0;
2135 }
2136 }
2137 0
2139 };
2140 assert!(screen_x <= self.rendered.width);
2141
2142 Some((
2143 screen_x as i16 - self.dark_offset.0 as i16,
2144 screen_y as i16 - self.dark_offset.1 as i16,
2145 ))
2146 }
2147 TextWrap::Hard | TextWrap::Word(_) => {
2148 let (_, sub_row_offset, oy) = self.clean_offset();
2149
2150 if oy > self.len_lines() {
2151 return None;
2152 }
2153 if pos.y < oy {
2154 return None;
2155 }
2156 if pos.y > self.len_lines() {
2157 return None;
2158 }
2159
2160 let page = self.rendered.height as upos_type;
2161 let scr = (sub_row_offset, oy, page);
2162 self.stc_fill_screen_cache(scr);
2163 let (screen_y, start_pos) = if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2164 if pos_row >= page {
2165 return None;
2167 }
2168 let start_pos = self.stc_sub_row_offset(scr, pos_row);
2169 (pos_row, start_pos)
2170 } else {
2171 return None;
2173 };
2174
2175 let screen_x = 'f: {
2176 for g in self
2177 .glyphs2(
2178 0,
2179 start_pos.0,
2180 start_pos.1..min(start_pos.1 + 1, self.len_lines()),
2181 )
2182 .expect("valid-row")
2183 {
2184 if g.pos().x == pos.x {
2185 break 'f g.screen_pos().0;
2186 } else if g.line_break() {
2187 break 'f g.screen_pos().0;
2188 }
2189 }
2190 0
2192 };
2193 assert!(screen_x <= self.rendered.width);
2194
2195 let scr = (
2196 screen_x as i16 - self.dark_offset.0 as i16,
2197 screen_y as i16 - self.dark_offset.1 as i16,
2198 );
2199 Some(scr)
2200 }
2201 }
2202 }
2203
2204 #[allow(clippy::needless_return)]
2206 pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
2207 let scr_pos = (
2208 scr_pos.0 + self.dark_offset.0 as i16,
2209 scr_pos.1 + self.dark_offset.1 as i16,
2210 );
2211
2212 match self.text_wrap {
2213 TextWrap::Shift => {
2214 let (ox, _, oy) = self.clean_offset();
2215
2216 if oy >= self.len_lines() {
2217 return None;
2218 }
2219
2220 if scr_pos.1 < 0 {
2221 return Some(TextPosition::new(
2223 0,
2224 oy.saturating_add_signed(scr_pos.1 as ipos_type),
2225 ));
2226 } else if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
2227 return Some(TextPosition::new(0, self.len_lines().saturating_sub(1)));
2229 }
2230
2231 let pos_y = oy + scr_pos.1 as upos_type;
2232
2233 if scr_pos.0 < 0 {
2234 return Some(TextPosition::new(
2235 ox.saturating_add_signed(scr_pos.0 as ipos_type),
2236 pos_y,
2237 ));
2238 } else if scr_pos.0 as u16 >= self.rendered.width {
2239 return Some(TextPosition::new(
2240 min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
2241 pos_y,
2242 ));
2243 } else {
2244 let mut start_pos = TextPosition::new(0, pos_y);
2245 for g in self
2246 .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
2247 .expect("valid-position")
2248 {
2249 if g.contains_screen_x(scr_pos.0 as u16) {
2250 return Some(TextPosition::new(g.pos().x, pos_y));
2251 }
2252 start_pos = g.pos();
2253 }
2254 Some(start_pos)
2255 }
2256 }
2257 TextWrap::Hard | TextWrap::Word(_) => {
2258 let (_, sub_row_offset, oy) = self.clean_offset();
2259
2260 if oy >= self.len_lines() {
2261 return None;
2262 }
2263
2264 if scr_pos.1 < 0 {
2265 let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2274
2275 self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2276
2277 let n_start_pos = 'f: {
2278 let line_break = self.value.cache().line_break.borrow();
2279 let start_range = TextPosition::new(0, ry);
2280 let end_range = TextPosition::new(sub_row_offset, oy);
2281
2282 let mut nrows = scr_pos.1.unsigned_abs();
2283 for (_break_pos, cache) in line_break.range(start_range..end_range).rev() {
2284 if nrows == 0 {
2285 break 'f cache.start_pos;
2286 }
2287 nrows -= 1;
2288 }
2289 TextPosition::new(0, ry)
2290 };
2291
2292 if scr_pos.0 < 0 {
2294 return Some(n_start_pos);
2295 }
2296
2297 let min_row = n_start_pos.y;
2298 let max_row = min(n_start_pos.y + 1, self.len_lines());
2299 for g in self
2300 .glyphs2(0, n_start_pos.x, min_row..max_row)
2301 .expect("valid-rows")
2302 {
2303 if g.contains_screen_x(scr_pos.0 as u16) {
2304 return Some(g.pos());
2305 }
2306 }
2307
2308 return Some(n_start_pos);
2310 } else {
2311 let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2312
2313 let n_start_pos = if scr_pos.1 == 0 {
2315 TextPosition::new(sub_row_offset, oy)
2316 } else {
2317 self.fill_cache(
2319 0,
2320 sub_row_offset,
2321 oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2322 )
2323 .expect("valid-rows");
2324
2325 'f: {
2326 let text_range = self.value.cache().line_break.borrow();
2327 let start_range = TextPosition::new(sub_row_offset, oy);
2328 let end_range = TextPosition::new(0, self.len_lines());
2329
2330 let mut nrows = scr_pos.1 - 1;
2331 let mut start_pos = TextPosition::new(sub_row_offset, oy);
2332 for (_break_pos, cache) in text_range.range(start_range..end_range) {
2333 if nrows == 0 {
2334 break 'f cache.start_pos;
2335 }
2336 start_pos = cache.start_pos;
2337 nrows -= 1;
2338 }
2339 start_pos
2340 }
2341 };
2342
2343 let min_row = n_start_pos.y;
2344 let max_row = min(n_start_pos.y + 1, self.len_lines());
2345 for g in self
2346 .glyphs2(0, n_start_pos.x, min_row..max_row)
2347 .expect("valid-rows")
2348 {
2349 if g.contains_screen_x(scr_pos.0) {
2350 return Some(g.pos());
2351 }
2352 }
2353
2354 return Some(n_start_pos);
2356 }
2357 }
2358 }
2359 }
2360}
2361
2362impl TextAreaState {
2363 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2369 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2370 return false;
2371 };
2372 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2373 self.set_move_col(Some(scr_cursor.0));
2374 }
2375 self.set_cursor(cursor, extend_selection)
2376 }
2377
2378 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2385 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2386 return false;
2387 };
2388
2389 let anchor = self.anchor();
2390 let cursor = if cursor < anchor {
2391 self.word_start(cursor)
2392 } else {
2393 self.word_end(cursor)
2394 };
2395
2396 if !self.is_word_boundary(anchor) {
2398 if cursor < anchor {
2399 self.set_cursor(self.word_end(anchor), false);
2400 } else {
2401 self.set_cursor(self.word_start(anchor), false);
2402 }
2403 }
2404
2405 self.set_cursor(cursor, extend_selection)
2406 }
2407}
2408
2409impl TextAreaState {
2410 pub fn vertical_max_offset(&self) -> upos_type {
2415 self.vscroll.max_offset() as upos_type
2416 }
2417
2418 pub fn vertical_offset(&self) -> upos_type {
2420 self.vscroll.offset() as upos_type
2421 }
2422
2423 pub fn vertical_page(&self) -> upos_type {
2425 self.vscroll.page_len() as upos_type
2426 }
2427
2428 pub fn vertical_scroll(&self) -> upos_type {
2430 self.vscroll.scroll_by() as upos_type
2431 }
2432
2433 pub fn horizontal_max_offset(&self) -> upos_type {
2442 self.hscroll.max_offset() as upos_type
2443 }
2444
2445 pub fn horizontal_offset(&self) -> upos_type {
2447 self.hscroll.offset() as upos_type
2448 }
2449
2450 pub fn horizontal_page(&self) -> upos_type {
2452 self.hscroll.page_len() as upos_type
2453 }
2454
2455 pub fn horizontal_scroll(&self) -> upos_type {
2457 self.hscroll.scroll_by() as upos_type
2458 }
2459
2460 pub fn set_vertical_offset(&mut self, row_offset: upos_type) -> bool {
2467 self.scroll_to_cursor = false;
2468 self.sub_row_offset = 0;
2469 self.vscroll.set_offset(row_offset as usize)
2470 }
2471
2472 pub fn set_horizontal_offset(&mut self, col_offset: upos_type) -> bool {
2481 self.scroll_to_cursor = false;
2482 self.hscroll.set_offset(col_offset as usize)
2483 }
2484
2485 pub fn scroll_to_pos(&mut self, pos: impl Into<TextPosition>) -> bool {
2487 let old_offset = self.clean_offset();
2488
2489 let pos = pos.into();
2490
2491 'f: {
2492 match self.text_wrap {
2493 TextWrap::Shift => {
2494 let (ox, _, oy) = old_offset;
2495
2496 let height = self.rendered.height as upos_type;
2497 let width = self.rendered.width as upos_type;
2498 let width = if self.show_ctrl() || self.wrap_ctrl() {
2499 width.saturating_sub(1)
2500 } else {
2501 width
2502 };
2503
2504 let noy = if pos.y < oy.saturating_sub(height) {
2505 pos.y.saturating_sub(height * 4 / 10)
2506 } else if pos.y < oy {
2507 pos.y
2508 } else if pos.y >= oy + 2 * height {
2509 pos.y.saturating_sub(height * 6 / 10)
2510 } else if pos.y >= oy + height {
2511 pos.y.saturating_sub(height.saturating_sub(1))
2512 } else {
2513 oy
2514 };
2515
2516 let nox = if pos.x < ox {
2517 pos.x
2518 } else if pos.x >= ox + width {
2519 pos.x.saturating_sub(width) + 1
2520 } else {
2521 ox
2522 };
2523
2524 self.set_offset((nox, noy));
2525 self.set_sub_row_offset(0);
2526 }
2527 TextWrap::Hard | TextWrap::Word(_) => {
2528 let (_ox, sub_row_offset, oy) = old_offset;
2529 let page = self.rendered.height as upos_type;
2530
2531 let scr = (0, oy.saturating_sub(page), 3 * page);
2533 self.stc_fill_screen_cache(scr);
2534 if let Some(off_row) =
2535 self.stc_screen_row(scr, TextPosition::new(sub_row_offset, oy))
2536 {
2537 if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2538 if pos_row < off_row && pos_row >= off_row.saturating_sub(page) {
2539 let noff_row = pos_row;
2540 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2541 self.set_offset((0, noy));
2542 self.set_sub_row_offset(nsub_row_offset);
2543 break 'f;
2544 } else if pos_row >= off_row + page && pos_row < off_row + 2 * page {
2545 let noff_row = pos_row.saturating_sub(page.saturating_sub(1));
2546 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2547 self.set_offset((0, noy));
2548 self.set_sub_row_offset(nsub_row_offset);
2549 break 'f;
2550 } else if pos_row >= off_row && pos_row < off_row + page {
2551 break 'f;
2552 }
2553 }
2554 }
2555
2556 let alt_scr = (0, pos.y.saturating_sub(page), 3 * page);
2558 self.stc_fill_screen_cache(alt_scr);
2559 if let Some(alt_scr_row) = self.stc_screen_row(alt_scr, pos) {
2560 let noff_row = alt_scr_row.saturating_sub(page * 5 / 10);
2561 let (nsub_row_offset, noy) = self.stc_sub_row_offset(alt_scr, noff_row);
2562 self.set_offset((0, noy));
2563 self.set_sub_row_offset(nsub_row_offset);
2564 } else {
2565 self.set_offset((0, pos.y));
2566 self.set_sub_row_offset(0);
2567 }
2568 }
2569 }
2570 }
2571
2572 old_offset != self.clean_offset()
2573 }
2574
2575 pub fn scroll_to_row(&mut self, pos: upos_type) -> bool {
2584 self.scroll_to_cursor = false;
2585
2586 match self.text_wrap {
2587 TextWrap::Shift => self.vscroll.scroll_to_pos(pos as usize),
2588 TextWrap::Hard | TextWrap::Word(_) => self
2589 .vscroll
2590 .set_offset(self.vscroll.limited_offset(pos as usize)),
2591 }
2592 }
2593
2594 pub fn scroll_to_col(&mut self, pos: upos_type) -> bool {
2602 self.scroll_to_cursor = false;
2603 self.hscroll.set_offset(pos as usize)
2604 }
2605
2606 pub fn scroll_up(&mut self, delta: upos_type) -> bool {
2612 if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2613 self.sub_row_offset = pos.x;
2614 self.vscroll.set_offset(pos.y as usize);
2615 true
2616 } else {
2617 false
2618 }
2619 }
2620
2621 pub fn scroll_down(&mut self, delta: upos_type) -> bool {
2627 if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2628 self.sub_row_offset = pos.x;
2629 self.vscroll.set_offset(pos.y as usize);
2630 true
2631 } else {
2632 false
2633 }
2634 }
2635
2636 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
2646 self.hscroll
2647 .set_offset(self.hscroll.offset.saturating_add(delta as usize))
2648 }
2649
2650 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
2660 self.hscroll
2661 .set_offset(self.hscroll.offset.saturating_sub(delta as usize))
2662 }
2663}
2664
2665impl TextAreaState {
2666 pub fn scroll_cursor_to_visible(&mut self) {
2672 self.scroll_to_cursor = true;
2673 }
2674}
2675
2676impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2677 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2678 fn tc(r: bool) -> TextOutcome {
2680 if r {
2681 TextOutcome::TextChanged
2682 } else {
2683 TextOutcome::Unchanged
2684 }
2685 }
2686
2687 let mut r = if self.is_focused() {
2688 match event {
2689 ct_event!(key press c)
2690 | ct_event!(key press SHIFT-c)
2691 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2692 ct_event!(keycode press Tab) => {
2693 tc(if !self.focus.gained() {
2695 self.insert_tab()
2696 } else {
2697 false
2698 })
2699 }
2700 ct_event!(keycode press SHIFT-BackTab) => {
2701 tc(if !self.focus.gained() {
2703 self.insert_backtab()
2704 } else {
2705 false
2706 })
2707 }
2708 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2709 ct_event!(keycode press Backspace) => tc(self.delete_prev_char()),
2710 ct_event!(keycode press Delete) => tc(self.delete_next_char()),
2711 ct_event!(keycode press CONTROL-Backspace)
2712 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2713 ct_event!(keycode press CONTROL-Delete) | ct_event!(keycode press ALT-Delete) => {
2714 tc(self.delete_next_word())
2715 }
2716 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2717 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2718 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2719 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2720 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2721 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2722
2723 ct_event!(key release _)
2724 | ct_event!(key release SHIFT-_)
2725 | ct_event!(key release CONTROL_ALT-_)
2726 | ct_event!(keycode release Tab)
2727 | ct_event!(keycode release Enter)
2728 | ct_event!(keycode release Backspace)
2729 | ct_event!(keycode release Delete)
2730 | ct_event!(keycode release CONTROL-Backspace)
2731 | ct_event!(keycode release ALT-Backspace)
2732 | ct_event!(keycode release CONTROL-Delete)
2733 | ct_event!(key release CONTROL-'x')
2734 | ct_event!(key release CONTROL-'v')
2735 | ct_event!(key release CONTROL-'d')
2736 | ct_event!(key release CONTROL-'y')
2737 | ct_event!(key release CONTROL-'z')
2738 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2739 _ => TextOutcome::Continue,
2740 }
2741 } else {
2742 TextOutcome::Continue
2743 };
2744 if r == TextOutcome::Continue {
2745 r = self.handle(event, ReadOnly);
2746 }
2747 r
2748 }
2749}
2750
2751pub const MATCH_STYLE: usize = 100_001;
2753
2754impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
2755 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2756 let mut r = if self.is_focused() {
2757 match event {
2758 ct_event!(keycode press Left) => self.move_left(1, false).into(),
2759 ct_event!(keycode press Right) => self.move_right(1, false).into(),
2760 ct_event!(keycode press Up) => self.move_up(1, false).into(),
2761 ct_event!(keycode press Down) => self.move_down(1, false).into(),
2762 ct_event!(keycode press PageUp) => {
2763 self.move_up(self.vertical_page() as u16, false).into()
2764 }
2765 ct_event!(keycode press PageDown) => {
2766 self.move_down(self.vertical_page() as u16, false).into()
2767 }
2768 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
2769 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
2770 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
2771 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
2772 ct_event!(keycode press CONTROL-Up) => false.into(),
2773 ct_event!(keycode press CONTROL-Down) => false.into(),
2774 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
2775 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
2776 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
2777 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
2778
2779 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
2780 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
2781 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
2782 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
2783 ct_event!(keycode press ALT-PageUp) => {
2784 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
2785 }
2786 ct_event!(keycode press ALT-PageDown) => {
2787 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
2788 }
2789 ct_event!(keycode press ALT_SHIFT-PageUp) => {
2790 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
2791 }
2792 ct_event!(keycode press ALT_SHIFT-PageDown) => {
2793 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
2794 }
2795
2796 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
2797 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
2798 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
2799 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
2800 ct_event!(keycode press SHIFT-PageUp) => {
2801 self.move_up(self.vertical_page() as u16, true).into()
2802 }
2803 ct_event!(keycode press SHIFT-PageDown) => {
2804 self.move_down(self.vertical_page() as u16, true).into()
2805 }
2806 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
2807 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
2808 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
2809 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
2810 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
2811 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
2812 ct_event!(key press CONTROL-'a') => self.select_all().into(),
2813 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
2814
2815 ct_event!(keycode press F(3)) => self.move_to_next_match().into(),
2816 ct_event!(keycode press SHIFT-F(3)) => self.move_to_prev_match().into(),
2817
2818 ct_event!(keycode release Left)
2819 | ct_event!(keycode release Right)
2820 | ct_event!(keycode release Up)
2821 | ct_event!(keycode release Down)
2822 | ct_event!(keycode release PageUp)
2823 | ct_event!(keycode release PageDown)
2824 | ct_event!(keycode release Home)
2825 | ct_event!(keycode release End)
2826 | ct_event!(keycode release CONTROL-Left)
2827 | ct_event!(keycode release CONTROL-Right)
2828 | ct_event!(keycode release CONTROL-Up)
2829 | ct_event!(keycode release CONTROL-Down)
2830 | ct_event!(keycode release CONTROL-PageUp)
2831 | ct_event!(keycode release CONTROL-PageDown)
2832 | ct_event!(keycode release CONTROL-Home)
2833 | ct_event!(keycode release CONTROL-End)
2834 | ct_event!(keycode release ALT-Left)
2835 | ct_event!(keycode release ALT-Right)
2836 | ct_event!(keycode release ALT-Up)
2837 | ct_event!(keycode release ALT-Down)
2838 | ct_event!(keycode release ALT-PageUp)
2839 | ct_event!(keycode release ALT-PageDown)
2840 | ct_event!(keycode release ALT_SHIFT-PageUp)
2841 | ct_event!(keycode release ALT_SHIFT-PageDown)
2842 | ct_event!(keycode release SHIFT-Left)
2843 | ct_event!(keycode release SHIFT-Right)
2844 | ct_event!(keycode release SHIFT-Up)
2845 | ct_event!(keycode release SHIFT-Down)
2846 | ct_event!(keycode release SHIFT-PageUp)
2847 | ct_event!(keycode release SHIFT-PageDown)
2848 | ct_event!(keycode release SHIFT-Home)
2849 | ct_event!(keycode release SHIFT-End)
2850 | ct_event!(keycode release CONTROL_SHIFT-Left)
2851 | ct_event!(keycode release CONTROL_SHIFT-Right)
2852 | ct_event!(keycode release CONTROL_SHIFT-Home)
2853 | ct_event!(keycode release CONTROL_SHIFT-End)
2854 | ct_event!(key release CONTROL-'a')
2855 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2856 _ => TextOutcome::Continue,
2857 }
2858 } else {
2859 TextOutcome::Continue
2860 };
2861
2862 if r == TextOutcome::Continue {
2863 r = self.handle(event, MouseOnly);
2864 }
2865 r
2866 }
2867}
2868
2869impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
2870 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
2871 flow!(match event {
2872 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2873 let cx = m.column as i16 - self.inner.x as i16;
2874 let cy = m.row as i16 - self.inner.y as i16;
2875 self.set_screen_cursor((cx, cy), true).into()
2876 }
2877 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2878 let cx = m.column as i16 - self.inner.x as i16;
2879 let cy = m.row as i16 - self.inner.y as i16;
2880 self.set_screen_cursor_words((cx, cy), true).into()
2881 }
2882 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2883 if let Some(test) = self.screen_to_pos((m.column, m.row)) {
2884 let start = self.word_start(test);
2885 let end = self.word_end(test);
2886 self.set_selection(start, end).into()
2887 } else {
2888 TextOutcome::Unchanged
2889 }
2890 }
2891 ct_event!(mouse down Left for column,row) => {
2892 if self.inner.contains((*column, *row).into()) {
2893 let cx = (column - self.inner.x) as i16;
2894 let cy = (row - self.inner.y) as i16;
2895 self.set_screen_cursor((cx, cy), false).into()
2896 } else {
2897 TextOutcome::Continue
2898 }
2899 }
2900 ct_event!(mouse down SHIFT-Left for column,row) => {
2901 if self.inner.contains((*column, *row).into()) {
2902 let cx = (column - self.inner.x) as i16;
2903 let cy = (row - self.inner.y) as i16;
2904 self.set_screen_cursor((cx, cy), true).into()
2905 } else {
2906 TextOutcome::Continue
2907 }
2908 }
2909 ct_event!(mouse down CONTROL-Left for column,row) => {
2910 if self.inner.contains((*column, *row).into()) {
2911 let cx = (column - self.inner.x) as i16;
2912 let cy = (row - self.inner.y) as i16;
2913 self.set_screen_cursor((cx, cy), true).into()
2914 } else {
2915 TextOutcome::Continue
2916 }
2917 }
2918 ct_event!(mouse down ALT-Left for column,row) => {
2919 if self.inner.contains((*column, *row).into()) {
2920 let cx = (column - self.inner.x) as i16;
2921 let cy = (row - self.inner.y) as i16;
2922 self.set_screen_cursor_words((cx, cy), true).into()
2923 } else {
2924 TextOutcome::Continue
2925 }
2926 }
2927 _ => TextOutcome::Continue,
2928 });
2929
2930 let mut sas = ScrollAreaState::new()
2931 .area(self.inner)
2932 .h_scroll(&mut self.hscroll)
2933 .v_scroll(&mut self.vscroll);
2934 let r = match sas.handle(event, MouseOnly) {
2935 ScrollOutcome::Up(v) => self.scroll_up(v as upos_type),
2936 ScrollOutcome::Down(v) => self.scroll_down(v as upos_type),
2937 ScrollOutcome::Left(v) => self.scroll_left(v as upos_type),
2938 ScrollOutcome::Right(v) => self.scroll_right(v as upos_type),
2939 ScrollOutcome::VPos(v) => self.scroll_to_row(v as upos_type),
2940 ScrollOutcome::HPos(v) => self.scroll_to_col(v as upos_type),
2941 _ => false,
2942 };
2943 if r {
2944 return TextOutcome::Changed;
2945 }
2946
2947 TextOutcome::Continue
2948 }
2949}
2950
2951pub fn handle_events(
2955 state: &mut TextAreaState,
2956 focus: bool,
2957 event: &crossterm::event::Event,
2958) -> TextOutcome {
2959 state.focus.set(focus);
2960 state.handle(event, Regular)
2961}
2962
2963pub fn handle_readonly_events(
2967 state: &mut TextAreaState,
2968 focus: bool,
2969 event: &crossterm::event::Event,
2970) -> TextOutcome {
2971 state.focus.set(focus);
2972 state.handle(event, ReadOnly)
2973}
2974
2975pub fn handle_mouse_events(
2977 state: &mut TextAreaState,
2978 event: &crossterm::event::Event,
2979) -> TextOutcome {
2980 state.handle(event, MouseOnly)
2981}