1use crate::_private::NonExhaustive;
7use crate::clipboard::{Clipboard, global_clipboard};
8use crate::event::{ReadOnly, TextOutcome};
9use crate::glyph2::{GlyphIter2, TextWrap2};
10use crate::text_core::TextCore;
11use crate::text_core::core_op;
12use crate::text_store::TextStore;
13use crate::text_store::text_rope::TextRope;
14use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
15use crate::{HasScreenCursor, TextError, TextPosition, TextRange, TextStyle, ipos_type, upos_type};
16use crossterm::event::KeyModifiers;
17use rat_event::util::MouseFlags;
18use rat_event::{HandleEvent, MouseOnly, Regular, ct_event, flow};
19use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
20use rat_reloc::{RelocatableState, relocate_area, relocate_dark_offset, relocate_pos_tuple};
21use rat_scrolled::event::ScrollOutcome;
22use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState};
23use ratatui::buffer::Buffer;
24use ratatui::layout::{Rect, Size};
25use ratatui::style::{Style, Stylize};
26use ratatui::widgets::{Block, StatefulWidget};
27use ropey::Rope;
28use std::borrow::Cow;
29use std::cell::Cell;
30use std::cmp::{max, min};
31use std::collections::HashMap;
32use std::ops::Range;
33use std::rc::Rc;
34
35pub mod text_area_op;
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<upos_type>,
81 h_overscroll: Option<upos_type>,
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: Rc<Cell<bool>>,
129
130 pub value: TextCore<TextRope>,
132
133 pub move_col: Option<i16>,
142 pub auto_indent: bool,
145 pub auto_quote: bool,
148 pub text_wrap: TextWrap,
151 pub newline: String,
154 pub tab_width: u32,
157 pub expand_tabs: bool,
160
161 pub focus: FocusFlag,
164
165 pub mouse: MouseFlags,
168
169 pub non_exhaustive: NonExhaustive,
170}
171
172impl Clone for TextAreaState {
173 fn clone(&self) -> Self {
174 Self {
175 area: self.area,
176 inner: self.inner,
177 rendered: self.rendered,
178 screen_cursor: self.screen_cursor,
179 hscroll: self.hscroll.clone(),
180 vscroll: self.vscroll.clone(),
181 sub_row_offset: self.sub_row_offset,
182 dark_offset: self.dark_offset,
183 scroll_to_cursor: Rc::new(Cell::new(self.scroll_to_cursor.get())),
184 value: self.value.clone(),
185 move_col: None,
186 auto_indent: self.auto_indent,
187 auto_quote: self.auto_quote,
188 text_wrap: self.text_wrap,
189 newline: self.newline.clone(),
190 tab_width: self.tab_width,
191 expand_tabs: self.expand_tabs,
192 focus: FocusFlag::named(self.focus.name()),
193 mouse: Default::default(),
194 non_exhaustive: NonExhaustive,
195 }
196 }
197}
198
199#[derive(Debug, Default, Clone, Copy)]
201#[non_exhaustive]
202pub enum TextWrap {
203 #[default]
205 Shift,
206 Hard,
208 Word(u16),
218}
219
220impl<'a> TextArea<'a> {
221 pub fn new() -> Self {
223 Self::default()
224 }
225
226 #[inline]
228 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
229 if let Some(styles) = styles {
230 self.styles(styles)
231 } else {
232 self
233 }
234 }
235
236 #[inline]
238 pub fn styles(mut self, styles: TextStyle) -> Self {
239 self.style = styles.style;
240 if styles.focus.is_some() {
241 self.focus_style = styles.focus;
242 }
243 if styles.select.is_some() {
244 self.select_style = styles.select;
245 }
246 self.block = self.block.map(|v| v.style(self.style));
247 if let Some(border_style) = styles.border_style {
248 self.block = self.block.map(|v| v.border_style(border_style));
249 }
250 if styles.block.is_some() {
251 self.block = styles.block;
252 }
253 if let Some(styles) = styles.scroll {
254 self.hscroll = self.hscroll.map(|v| v.styles(styles.clone()));
255 self.vscroll = self.vscroll.map(|v| v.styles(styles));
256 }
257 self
258 }
259
260 pub fn style(mut self, style: Style) -> Self {
262 self.style = style;
263 self
264 }
265
266 pub fn focus_style(mut self, style: Style) -> Self {
268 self.focus_style = Some(style);
269 self.block = self.block.map(|v| v.style(self.style));
270 self
271 }
272
273 pub fn select_style(mut self, style: Style) -> Self {
275 self.select_style = Some(style);
276 self
277 }
278
279 pub fn text_style_idx(mut self, idx: usize, style: Style) -> Self {
284 self.text_style.insert(idx, style);
285 self
286 }
287
288 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
293 for (i, s) in styles.into_iter().enumerate() {
294 self.text_style.insert(i, s);
295 }
296 self
297 }
298
299 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
304 for (i, s) in styles.into_iter() {
305 self.text_style.insert(i, s.into());
306 }
307 self
308 }
309
310 #[inline]
312 pub fn block(mut self, block: Block<'a>) -> Self {
313 self.block = Some(block);
314 self
315 }
316
317 pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
319 self.hscroll = Some(scroll.clone().override_horizontal());
320 self.vscroll = Some(scroll.override_vertical());
321 self
322 }
323
324 pub fn hscroll(mut self, scroll: Scroll<'a>) -> Self {
326 self.hscroll = Some(scroll.override_horizontal());
327 self
328 }
329
330 pub fn text_wrap(mut self, wrap: TextWrap) -> Self {
332 self.text_wrap = Some(wrap);
333 self
334 }
335
336 pub fn set_horizontal_max_offset(mut self, offset: u32) -> Self {
353 self.h_max_offset = Some(offset);
354 self
355 }
356
357 pub fn set_horizontal_overscroll(mut self, overscroll: u32) -> Self {
364 self.h_overscroll = Some(overscroll);
365 self
366 }
367
368 pub fn vscroll(mut self, scroll: Scroll<'a>) -> Self {
370 self.vscroll = Some(scroll.override_vertical());
371 self
372 }
373}
374
375impl<'a> StatefulWidget for &TextArea<'a> {
376 type State = TextAreaState;
377
378 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
379 render_text_area(self, area, buf, state);
380 }
381}
382
383impl StatefulWidget for TextArea<'_> {
384 type State = TextAreaState;
385
386 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
387 render_text_area(&self, area, buf, state);
388 }
389}
390
391fn render_text_area(
392 widget: &TextArea<'_>,
393 area: Rect,
394 buf: &mut Buffer,
395 state: &mut TextAreaState,
396) {
397 state.area = area;
398 state.screen_cursor = None;
399 if let Some(text_wrap) = widget.text_wrap {
400 state.text_wrap = text_wrap;
401 }
402
403 let style = widget.style;
404 let focus_style = if let Some(focus_style) = widget.focus_style {
405 focus_style
406 } else {
407 style
408 };
409 let select_style = if let Some(select_style) = widget.select_style {
410 select_style
411 } else {
412 Style::default().black().on_yellow()
413 };
414 let (style, select_style) = if state.is_focused() {
415 (
416 style.patch(focus_style),
417 style.patch(focus_style).patch(select_style),
418 )
419 } else {
420 (style, style.patch(select_style))
421 };
422
423 state.area = area;
425 state.screen_cursor = None;
426 state.inner = ScrollArea::new()
427 .block(widget.block.as_ref())
428 .h_scroll(widget.hscroll.as_ref())
429 .v_scroll(widget.vscroll.as_ref())
430 .inner(area, Some(&state.hscroll), Some(&state.vscroll));
431 state.rendered = state.inner.as_size();
432
433 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
434 state.hscroll.set_max_offset(0);
435 state.hscroll.set_overscroll_by(None);
436 } else {
437 if let Some(h_max_offset) = widget.h_max_offset {
438 state.hscroll.set_max_offset(h_max_offset as usize);
439 }
440 if let Some(h_overscroll) = widget.h_overscroll {
441 state.hscroll.set_overscroll_by(Some(h_overscroll as usize));
442 }
443 }
444 state.hscroll.set_page_len(state.inner.width as usize);
445
446 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
447 state
448 .vscroll
449 .set_max_offset(state.len_lines().saturating_sub(1) as usize);
450 } else {
451 state.vscroll.set_max_offset(
452 state
453 .len_lines()
454 .saturating_sub(state.inner.height as upos_type) as usize,
455 );
456 }
457 state.vscroll.set_page_len(state.inner.height as usize);
458
459 if state.scroll_to_cursor.get() {
460 state.scroll_to_pos(state.cursor());
461 }
462
463 ScrollArea::new()
465 .block(widget.block.as_ref())
466 .h_scroll(widget.hscroll.as_ref())
467 .v_scroll(widget.vscroll.as_ref())
468 .style(style)
469 .render(
470 area,
471 buf,
472 &mut ScrollAreaState::new()
473 .h_scroll(&mut state.hscroll)
474 .v_scroll(&mut state.vscroll),
475 );
476
477 if state.inner.width == 0 || state.inner.height == 0 {
478 return;
480 }
481 if state.vscroll.offset() > state.value.len_lines() as usize {
482 return;
484 }
485
486 let (shift_left, sub_row_offset, start_row) = state.clean_offset();
487 let page_rows = start_row
488 ..min(
489 start_row + state.inner.height as upos_type,
490 state.value.len_lines(),
491 );
492 let page_bytes = state
493 .try_bytes_at_range(TextRange::new(
494 (sub_row_offset, page_rows.start),
495 (0, page_rows.end),
496 ))
497 .expect("valid_rows");
498 let selection = state.selection();
500 let mut styles = Vec::new();
501
502 for g in state
503 .glyphs2(shift_left, sub_row_offset, page_rows)
504 .expect("valid_offset")
505 {
506 let screen_pos = g.screen_pos();
508
509 if screen_pos.1 >= state.inner.height {
510 break;
511 }
512
513 if g.screen_width() > 0 {
514 let mut style = style;
515 state
517 .value
518 .styles_at_page(g.text_bytes().start, page_bytes.clone(), &mut styles);
519 for style_nr in &styles {
520 if let Some(s) = widget.text_style.get(style_nr) {
521 style = style.patch(*s);
522 }
523 }
524 if selection.contains_pos(g.pos()) {
526 style = style.patch(select_style);
527 };
528
529 if let Some(cell) =
531 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
532 {
533 cell.set_symbol(g.glyph());
534 cell.set_style(style);
535 }
536 for d in 1..g.screen_width() {
538 if let Some(cell) = buf.cell_mut((
539 state.inner.x + screen_pos.0 + d,
540 state.inner.y + screen_pos.1,
541 )) {
542 cell.reset();
543 cell.set_style(style);
544 }
545 }
546 }
547 }
548
549 state.screen_cursor = state.pos_to_screen(state.cursor());
550}
551
552impl Default for TextAreaState {
553 fn default() -> Self {
554 #[cfg(windows)]
555 const LINE_ENDING: &str = "\r\n";
556
557 #[cfg(not(windows))]
558 const LINE_ENDING: &str = "\n";
559
560 let mut s = Self {
561 area: Default::default(),
562 inner: Default::default(),
563 rendered: Default::default(),
564 screen_cursor: Default::default(),
565 hscroll: Default::default(),
566 vscroll: Default::default(),
567 sub_row_offset: 0,
568 dark_offset: Default::default(),
569 scroll_to_cursor: Default::default(),
570 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
571 move_col: Default::default(),
572 auto_indent: true,
573 auto_quote: true,
574 text_wrap: TextWrap::Shift,
575 newline: LINE_ENDING.to_string(),
576 tab_width: 8,
577 expand_tabs: true,
578 focus: Default::default(),
579 mouse: Default::default(),
580 non_exhaustive: NonExhaustive,
581 };
582 s.hscroll.set_max_offset(255);
583 s.hscroll.set_overscroll_by(Some(16384));
584 s
585 }
586}
587
588impl HasFocus for TextAreaState {
589 fn build(&self, builder: &mut FocusBuilder) {
590 builder.leaf_widget(self);
591 }
592
593 fn focus(&self) -> FocusFlag {
594 self.focus.clone()
595 }
596
597 fn area(&self) -> Rect {
598 self.area
599 }
600
601 fn navigable(&self) -> Navigation {
602 Navigation::Reach
603 }
604}
605
606impl TextAreaState {
607 #[inline]
609 pub fn new() -> Self {
610 Self::default()
611 }
612
613 #[inline]
615 pub fn named(name: &str) -> Self {
616 Self {
617 focus: FocusFlag::named(name),
618 ..Default::default()
619 }
620 }
621
622 #[inline]
628 pub fn set_newline(&mut self, br: impl Into<String>) {
629 self.newline = br.into();
630 }
631
632 #[inline]
634 pub fn newline(&self) -> &str {
635 &self.newline
636 }
637
638 #[inline]
640 pub fn set_auto_indent(&mut self, indent: bool) {
641 self.auto_indent = indent;
642 }
643
644 #[inline]
646 pub fn set_auto_quote(&mut self, quote: bool) {
647 self.auto_quote = quote;
648 }
649
650 #[inline]
652 pub fn set_tab_width(&mut self, tabs: u32) {
653 self.tab_width = tabs;
654 }
655
656 #[inline]
658 pub fn tab_width(&self) -> u32 {
659 self.tab_width
660 }
661
662 #[inline]
664 pub fn set_expand_tabs(&mut self, expand: bool) {
665 self.expand_tabs = expand;
666 }
667
668 #[inline]
670 pub fn expand_tabs(&self) -> bool {
671 self.expand_tabs
672 }
673
674 #[inline]
676 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
677 self.value.set_glyph_ctrl(show_ctrl);
678 }
679
680 pub fn show_ctrl(&self) -> bool {
682 self.value.glyph_ctrl()
683 }
684
685 #[inline]
688 pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
689 self.value.set_wrap_ctrl(wrap_ctrl);
690 }
691
692 pub fn wrap_ctrl(&self) -> bool {
695 self.value.wrap_ctrl()
696 }
697
698 pub fn set_text_wrap(&mut self, text_wrap: TextWrap) {
711 self.text_wrap = text_wrap;
712 }
713
714 pub fn text_wrap(&self) -> TextWrap {
716 self.text_wrap
717 }
718
719 #[inline]
729 pub fn set_move_col(&mut self, col: Option<i16>) {
730 self.move_col = col;
731 }
732
733 #[inline]
735 pub fn move_col(&mut self) -> Option<i16> {
736 self.move_col
737 }
738}
739
740impl TextAreaState {
741 #[inline]
744 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
745 match clip {
746 None => self.value.set_clipboard(None),
747 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
748 }
749 }
750
751 #[inline]
754 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
755 self.value.clipboard()
756 }
757
758 #[inline]
760 pub fn copy_to_clip(&mut self) -> bool {
761 let Some(clip) = self.value.clipboard() else {
762 return false;
763 };
764
765 _ = clip.set_string(self.selected_text().as_ref());
766 false
767 }
768
769 #[inline]
771 pub fn cut_to_clip(&mut self) -> bool {
772 let Some(clip) = self.value.clipboard() else {
773 return false;
774 };
775
776 match clip.set_string(self.selected_text().as_ref()) {
777 Ok(_) => self.delete_range(self.selection()),
778 Err(_) => false,
779 }
780 }
781
782 #[inline]
784 pub fn paste_from_clip(&mut self) -> bool {
785 let Some(clip) = self.value.clipboard() else {
786 return false;
787 };
788
789 if let Ok(text) = clip.get_string() {
790 self.insert_str(text)
791 } else {
792 false
793 }
794 }
795}
796
797impl TextAreaState {
798 #[inline]
805 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
806 match undo {
807 None => self.value.set_undo_buffer(None),
808 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
809 }
810 }
811
812 #[inline]
814 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
815 self.value.undo_buffer()
816 }
817
818 #[inline]
820 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
821 self.value.undo_buffer_mut()
822 }
823
824 #[inline]
830 pub fn begin_undo_seq(&mut self) {
831 self.value.begin_undo_seq()
832 }
833
834 #[inline]
836 pub fn end_undo_seq(&mut self) {
837 self.value.end_undo_seq()
838 }
839
840 #[inline]
845 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
846 self.value.recent_replay_log()
847 }
848
849 #[inline]
853 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
854 self.value.replay_log(replay)
855 }
856
857 #[inline]
859 pub fn undo(&mut self) -> bool {
860 self.value.undo()
861 }
862
863 #[inline]
865 pub fn redo(&mut self) -> bool {
866 self.value.redo()
867 }
868}
869
870impl TextAreaState {
871 #[inline]
888 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
889 self.value.set_styles(styles);
890 }
891
892 #[inline]
903 pub fn set_range_styles(&mut self, styles: Vec<(TextRange, usize)>) -> Result<(), TextError> {
904 let mut mapped = Vec::with_capacity(styles.len());
905 for (r, s) in styles {
906 let rr = self.value.bytes_at_range(r)?;
907 mapped.push((rr, s));
908 }
909 self.value.set_styles(mapped);
910 Ok(())
911 }
912
913 #[inline]
918 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
919 self.value.add_style(range, style);
920 }
921
922 #[inline]
926 pub fn add_range_style(
927 &mut self,
928 range: impl Into<TextRange>,
929 style: usize,
930 ) -> Result<(), TextError> {
931 let r = self.value.bytes_at_range(range.into())?;
932 self.value.add_style(r, style);
933 Ok(())
934 }
935
936 #[inline]
938 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
939 self.value.remove_style(range, style);
940 }
941
942 #[inline]
944 pub fn remove_style_fully(&mut self, style: usize) {
945 self.value.remove_style_fully(style);
946 }
947
948 #[inline]
950 pub fn remove_range_style(
951 &mut self,
952 range: impl Into<TextRange>,
953 style: usize,
954 ) -> Result<(), TextError> {
955 let r = self.value.bytes_at_range(range.into())?;
956 self.value.remove_style(r, style);
957 Ok(())
958 }
959
960 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
962 self.value.styles_in(range, buf)
963 }
964
965 pub fn styles_in_match(
967 &self,
968 range: Range<usize>,
969 style: usize,
970 buf: &mut Vec<(Range<usize>, usize)>,
971 ) {
972 self.value.styles_in_match(range, style, buf);
973 }
974
975 #[inline]
977 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
978 self.value.styles_at(byte_pos, buf)
979 }
980
981 #[inline]
984 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
985 self.value.styles_at_match(byte_pos, style)
986 }
987
988 #[inline]
990 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
991 self.value.styles()
992 }
993}
994
995impl TextAreaState {
996 #[inline]
998 pub fn offset(&self) -> (upos_type, upos_type) {
999 (
1000 self.hscroll.offset() as upos_type,
1001 self.vscroll.offset() as upos_type,
1002 )
1003 }
1004
1005 #[inline]
1010 pub fn set_offset(&mut self, offset: (upos_type, upos_type)) -> bool {
1011 self.scroll_to_cursor.set(false);
1012 let c = self.hscroll.set_offset(offset.0 as usize);
1013 let r = self.vscroll.set_offset(offset.1 as usize);
1014 r || c
1015 }
1016
1017 pub fn set_sub_row_offset(&mut self, sub_row_offset: upos_type) -> bool {
1029 self.scroll_to_cursor.set(false);
1030 let old = self.sub_row_offset;
1031 self.sub_row_offset = sub_row_offset;
1032 sub_row_offset != old
1033 }
1034
1035 pub fn sub_row_offset(&self) -> upos_type {
1040 self.sub_row_offset
1041 }
1042
1043 fn clean_offset(&self) -> (upos_type, upos_type, upos_type) {
1048 let ox = self.hscroll.offset as upos_type;
1049 let mut oy = self.vscroll.offset as upos_type;
1050
1051 if oy >= self.len_lines() {
1053 oy = 0;
1054 }
1055
1056 match self.text_wrap {
1057 TextWrap::Shift => (ox, 0, oy),
1058 TextWrap::Hard | TextWrap::Word(_) => {
1059 if let Ok(max_col) = self.try_line_width(oy) {
1061 (0, min(self.sub_row_offset, max_col), oy)
1062 } else {
1063 (0, 0, oy)
1064 }
1065 }
1066 }
1067 }
1068
1069 #[inline]
1071 pub fn cursor(&self) -> TextPosition {
1072 self.value.cursor()
1073 }
1074
1075 #[inline]
1077 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
1078 self.scroll_cursor_to_visible();
1079 self.value.set_cursor(cursor.into(), extend_selection)
1080 }
1081
1082 #[inline]
1084 pub fn anchor(&self) -> TextPosition {
1085 self.value.anchor()
1086 }
1087
1088 #[inline]
1090 pub fn has_selection(&self) -> bool {
1091 self.value.has_selection()
1092 }
1093
1094 #[inline]
1096 pub fn selection(&self) -> TextRange {
1097 self.value.selection()
1098 }
1099
1100 #[inline]
1103 pub fn set_selection(
1104 &mut self,
1105 anchor: impl Into<TextPosition>,
1106 cursor: impl Into<TextPosition>,
1107 ) -> bool {
1108 self.scroll_cursor_to_visible();
1109 self.value.set_selection(anchor.into(), cursor.into())
1110 }
1111
1112 #[inline]
1115 pub fn select_all(&mut self) -> bool {
1116 self.scroll_cursor_to_visible();
1117 self.value.select_all()
1118 }
1119
1120 #[inline]
1122 pub fn selected_text(&self) -> Cow<'_, str> {
1123 self.value
1124 .str_slice(self.value.selection())
1125 .expect("valid_selection")
1126 }
1127}
1128
1129impl TextAreaState {
1130 #[inline]
1132 pub fn clear(&mut self) -> bool {
1133 if !self.is_empty() {
1134 self.value.clear();
1135 true
1136 } else {
1137 false
1138 }
1139 }
1140
1141 #[inline]
1145 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1146 self.scroll_to_cursor.set(false);
1147 self.vscroll.set_offset(0);
1148 self.hscroll.set_offset(0);
1149 self.set_sub_row_offset(0);
1150 self.set_move_col(None);
1151
1152 self.value.set_text(TextRope::new_text(s.as_ref()));
1153 }
1154
1155 #[inline]
1157 pub fn text(&self) -> String {
1158 self.value.text().string()
1159 }
1160
1161 #[inline]
1164 pub fn set_rope(&mut self, r: Rope) {
1165 self.scroll_to_cursor.set(false);
1166 self.vscroll.set_offset(0);
1167 self.hscroll.set_offset(0);
1168 self.set_sub_row_offset(0);
1169 self.set_move_col(None);
1170
1171 self.value.set_text(TextRope::new_rope(r));
1172 }
1173
1174 #[inline]
1176 pub fn rope(&self) -> &Rope {
1177 self.value.text().rope()
1178 }
1179
1180 #[inline]
1184 pub fn set_value<S: AsRef<str>>(&mut self, s: S) {
1185 self.set_text(s);
1186 }
1187
1188 #[inline]
1190 pub fn value(&self) -> String {
1191 self.text()
1192 }
1193
1194 #[inline]
1196 pub fn is_empty(&self) -> bool {
1197 self.value.is_empty()
1198 }
1199
1200 #[inline]
1204 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1205 self.value.str_slice_byte(range).expect("valid_range")
1206 }
1207
1208 #[inline]
1210 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1211 self.value.str_slice_byte(range)
1212 }
1213
1214 #[inline]
1218 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
1219 self.value.str_slice(range.into()).expect("valid_range")
1220 }
1221
1222 #[inline]
1224 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
1225 self.value.str_slice(range.into())
1226 }
1227
1228 #[inline]
1230 pub fn len_lines(&self) -> upos_type {
1231 self.value.len_lines()
1232 }
1233
1234 #[inline]
1236 pub fn len_bytes(&self) -> usize {
1237 self.value.len_bytes()
1238 }
1239
1240 #[inline]
1244 pub fn line_width(&self, row: upos_type) -> upos_type {
1245 self.try_line_width(row).expect("valid_row")
1246 }
1247
1248 #[inline]
1250 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1251 self.value.line_width(row)
1252 }
1253
1254 #[inline]
1259 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
1260 self.value.line_at(row).expect("valid_row")
1261 }
1262
1263 #[inline]
1266 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1267 self.value.line_at(row)
1268 }
1269
1270 #[inline]
1274 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
1275 self.value.lines_at(row).expect("valid_row")
1276 }
1277
1278 #[inline]
1280 pub fn try_lines_at(
1281 &self,
1282 row: upos_type,
1283 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1284 self.value.lines_at(row)
1285 }
1286
1287 #[inline]
1292 pub fn line_graphemes(&self, row: upos_type) -> <TextRope as TextStore>::GraphemeIter<'_> {
1293 self.value.line_graphemes(row).expect("valid_row")
1294 }
1295
1296 #[inline]
1299 pub fn try_line_graphemes(
1300 &self,
1301 row: upos_type,
1302 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1303 self.value.line_graphemes(row)
1304 }
1305
1306 #[inline]
1310 pub fn text_graphemes(
1311 &self,
1312 pos: impl Into<TextPosition>,
1313 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1314 self.value.text_graphemes(pos.into()).expect("valid_pos")
1315 }
1316
1317 #[inline]
1319 pub fn try_text_graphemes(
1320 &self,
1321 pos: impl Into<TextPosition>,
1322 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1323 self.value.text_graphemes(pos.into())
1324 }
1325
1326 #[inline]
1330 pub fn graphemes(
1331 &self,
1332 range: impl Into<TextRange>,
1333 pos: impl Into<TextPosition>,
1334 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1335 self.value
1336 .graphemes(range.into(), pos.into())
1337 .expect("valid_args")
1338 }
1339
1340 #[inline]
1342 pub fn try_graphemes(
1343 &self,
1344 range: impl Into<TextRange>,
1345 pos: impl Into<TextPosition>,
1346 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1347 self.value.graphemes(range.into(), pos.into())
1348 }
1349
1350 #[inline]
1355 pub fn byte_at(&self, pos: impl Into<TextPosition>) -> Range<usize> {
1356 self.value.byte_at(pos.into()).expect("valid_pos")
1357 }
1358
1359 #[inline]
1362 pub fn try_byte_at(&self, pos: impl Into<TextPosition>) -> Result<Range<usize>, TextError> {
1363 self.value.byte_at(pos.into())
1364 }
1365
1366 #[inline]
1370 pub fn bytes_at_range(&self, range: impl Into<TextRange>) -> Range<usize> {
1371 self.value
1372 .bytes_at_range(range.into())
1373 .expect("valid_range")
1374 }
1375
1376 #[inline]
1378 pub fn try_bytes_at_range(
1379 &self,
1380 range: impl Into<TextRange>,
1381 ) -> Result<Range<usize>, TextError> {
1382 self.value.bytes_at_range(range.into())
1383 }
1384
1385 #[inline]
1390 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1391 self.value.byte_pos(byte).expect("valid_pos")
1392 }
1393
1394 #[inline]
1397 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1398 self.value.byte_pos(byte)
1399 }
1400
1401 #[inline]
1405 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1406 self.value.byte_range(bytes).expect("valid_range")
1407 }
1408
1409 #[inline]
1411 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1412 self.value.byte_range(bytes)
1413 }
1414}
1415
1416impl TextAreaState {
1417 #[inline]
1423 pub fn insert_char(&mut self, c: char) -> bool {
1424 match c {
1425 '\n' => text_area_op::insert_newline(self),
1426 '\t' => text_area_op::insert_tab(self),
1427 c => text_area_op::insert_char(self, c),
1428 }
1429 }
1430
1431 #[inline]
1437 pub fn insert_tab(&mut self) -> bool {
1438 text_area_op::insert_tab(self)
1439 }
1440
1441 #[inline]
1446 pub fn insert_backtab(&mut self) -> bool {
1447 text_area_op::insert_backtab(self)
1448 }
1449
1450 #[inline]
1453 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1454 text_area_op::insert_str(self, t.as_ref())
1455 }
1456
1457 #[inline]
1462 pub fn insert_newline(&mut self) -> bool {
1463 text_area_op::insert_newline(self)
1464 }
1465
1466 #[inline]
1470 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1471 text_area_op::delete_range(self, range.into()).expect("valid_range")
1472 }
1473
1474 #[inline]
1476 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1477 text_area_op::delete_range(self, range.into())
1478 }
1479
1480 #[inline]
1483 pub fn duplicate_text(&mut self) -> bool {
1484 text_area_op::duplicate_text(self)
1485 }
1486
1487 #[inline]
1490 pub fn delete_line(&mut self) -> bool {
1491 text_area_op::delete_line(self)
1492 }
1493
1494 #[inline]
1497 pub fn delete_next_char(&mut self) -> bool {
1498 text_area_op::delete_next_char(self)
1499 }
1500
1501 #[inline]
1504 pub fn delete_prev_char(&mut self) -> bool {
1505 text_area_op::delete_prev_char(self)
1506 }
1507
1508 #[inline]
1513 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1514 core_op::next_word_start(&self.value, pos.into()).expect("valid_pos")
1515 }
1516
1517 #[inline]
1520 pub fn try_next_word_start(
1521 &self,
1522 pos: impl Into<TextPosition>,
1523 ) -> Result<TextPosition, TextError> {
1524 core_op::next_word_start(&self.value, pos.into())
1525 }
1526
1527 #[inline]
1532 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1533 core_op::next_word_end(&self.value, pos.into()).expect("valid_pos")
1534 }
1535
1536 #[inline]
1539 pub fn try_next_word_end(
1540 &self,
1541 pos: impl Into<TextPosition>,
1542 ) -> Result<TextPosition, TextError> {
1543 core_op::next_word_end(&self.value, pos.into())
1544 }
1545
1546 #[inline]
1554 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1555 core_op::prev_word_start(&self.value, pos.into()).expect("valid_pos")
1556 }
1557
1558 #[inline]
1564 pub fn try_prev_word_start(
1565 &self,
1566 pos: impl Into<TextPosition>,
1567 ) -> Result<TextPosition, TextError> {
1568 core_op::prev_word_start(&self.value, pos.into())
1569 }
1570
1571 #[inline]
1577 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1578 core_op::prev_word_end(&self.value, pos.into()).expect("valid_pos")
1579 }
1580
1581 #[inline]
1585 pub fn try_prev_word_end(
1586 &self,
1587 pos: impl Into<TextPosition>,
1588 ) -> Result<TextPosition, TextError> {
1589 core_op::prev_word_end(&self.value, pos.into())
1590 }
1591
1592 #[inline]
1596 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1597 core_op::is_word_boundary(&self.value, pos.into()).expect("valid_pos")
1598 }
1599
1600 #[inline]
1602 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1603 core_op::is_word_boundary(&self.value, pos.into())
1604 }
1605
1606 #[inline]
1611 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1612 core_op::word_start(&self.value, pos.into()).expect("valid_pos")
1613 }
1614
1615 #[inline]
1618 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1619 core_op::word_start(&self.value, pos.into())
1620 }
1621
1622 #[inline]
1627 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1628 core_op::word_end(&self.value, pos.into()).expect("valid_pos")
1629 }
1630
1631 #[inline]
1634 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1635 core_op::word_end(&self.value, pos.into())
1636 }
1637
1638 #[inline]
1643 pub fn delete_next_word(&mut self) -> bool {
1644 text_area_op::delete_next_word(self)
1645 }
1646
1647 #[inline]
1652 pub fn delete_prev_word(&mut self) -> bool {
1653 text_area_op::delete_prev_word(self)
1654 }
1655
1656 pub fn search(&mut self, re: &str) -> Result<bool, TextError> {
1667 match text_area_op::search(self, re, MATCH_STYLE) {
1668 Ok(r) => Ok(r),
1669 Err(_) => Err(TextError::InvalidSearch),
1670 }
1671 }
1672
1673 pub fn move_to_next_match(&mut self) -> bool {
1675 text_area_op::move_to_next_match(self, MATCH_STYLE)
1676 }
1677
1678 pub fn move_to_prev_match(&mut self) -> bool {
1680 text_area_op::move_to_prev_match(self, MATCH_STYLE)
1681 }
1682
1683 pub fn clear_search(&mut self) {
1685 text_area_op::clear_search(self, MATCH_STYLE)
1686 }
1687
1688 #[inline]
1691 pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1692 text_area_op::move_left(self, n, extend_selection)
1693 }
1694
1695 #[inline]
1698 pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1699 text_area_op::move_right(self, n, extend_selection)
1700 }
1701
1702 #[inline]
1705 pub fn move_up(&mut self, n: u16, extend_selection: bool) -> bool {
1706 text_area_op::move_up(self, n, extend_selection)
1707 }
1708
1709 #[inline]
1712 pub fn move_down(&mut self, n: u16, extend_selection: bool) -> bool {
1713 text_area_op::move_down(self, n, extend_selection)
1714 }
1715
1716 #[inline]
1720 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1721 text_area_op::move_to_line_start(self, extend_selection)
1722 }
1723
1724 #[inline]
1728 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1729 text_area_op::move_to_line_end(self, extend_selection)
1730 }
1731
1732 #[inline]
1734 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1735 text_area_op::move_to_start(self, extend_selection)
1736 }
1737
1738 #[inline]
1740 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1741 text_area_op::move_to_end(self, extend_selection)
1742 }
1743
1744 #[inline]
1746 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1747 text_area_op::move_to_screen_start(self, extend_selection)
1748 }
1749
1750 #[inline]
1752 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1753 text_area_op::move_to_screen_end(self, extend_selection)
1754 }
1755
1756 #[inline]
1758 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1759 text_area_op::move_to_next_word(self, extend_selection)
1760 }
1761
1762 #[inline]
1764 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1765 text_area_op::move_to_prev_word(self, extend_selection)
1766 }
1767}
1768
1769impl HasScreenCursor for TextAreaState {
1770 #[allow(clippy::question_mark)]
1772 fn screen_cursor(&self) -> Option<(u16, u16)> {
1773 if self.is_focused() {
1774 if self.has_selection() {
1775 None
1776 } else {
1777 let Some(scr_cursor) = self.screen_cursor else {
1778 return None;
1779 };
1780
1781 if !(scr_cursor.0 >= self.inner.x
1782 && scr_cursor.0 <= self.inner.right()
1783 && scr_cursor.1 >= self.inner.y
1784 && scr_cursor.1 < self.inner.bottom())
1785 {
1786 return None;
1787 }
1788 Some(scr_cursor)
1789 }
1790 } else {
1791 None
1792 }
1793 }
1794}
1795
1796impl RelocatableState for TextAreaState {
1797 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1798 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1800 self.area = relocate_area(self.area, shift, clip);
1801 self.inner = relocate_area(self.inner, shift, clip);
1802 if let Some(screen_cursor) = self.screen_cursor {
1803 self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
1804 }
1805 }
1806}
1807
1808impl TextAreaState {
1809 fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
1811 match self.text_wrap {
1812 TextWrap::Shift => (
1813 TextWrap2::Shift,
1814 shift_left,
1815 shift_left + self.rendered.width as upos_type,
1816 shift_left + self.rendered.width as upos_type,
1817 ),
1818 TextWrap::Hard => (
1819 TextWrap2::Hard,
1820 0,
1821 self.rendered.width as upos_type,
1822 self.rendered.width as upos_type,
1823 ),
1824 TextWrap::Word(margin) => (
1825 TextWrap2::Word,
1826 0,
1827 self.rendered.width as upos_type,
1828 self.rendered.width.saturating_sub(margin) as upos_type,
1829 ),
1830 }
1831 }
1832
1833 fn fill_cache(
1836 &self,
1837 shift_left: upos_type,
1838 sub_row_offset: upos_type,
1839 rows: Range<upos_type>,
1840 ) -> Result<(), TextError> {
1841 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1842 self.value.fill_cache(
1843 self.rendered,
1844 sub_row_offset,
1845 rows,
1846 self.tab_width,
1847 text_wrap,
1848 self.wrap_ctrl() | self.show_ctrl(),
1849 left_margin,
1850 right_margin,
1851 word_margin,
1852 )
1853 }
1854
1855 fn glyphs2(
1857 &self,
1858 shift_left: upos_type,
1859 sub_row_offset: upos_type,
1860 rows: Range<upos_type>,
1861 ) -> Result<GlyphIter2<'_, <TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
1862 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1863 self.value.glyphs2(
1864 self.rendered,
1865 sub_row_offset,
1866 rows,
1867 self.tab_width,
1868 text_wrap,
1869 self.wrap_ctrl() | self.show_ctrl(),
1870 left_margin,
1871 right_margin,
1872 word_margin,
1873 )
1874 }
1875
1876 fn stc_fill_screen_cache(&self, scr: (upos_type, upos_type, upos_type)) {
1880 let y2 = scr.1 + scr.2;
1881 self.fill_cache(0, scr.0, scr.1..min(y2, self.len_lines()))
1882 .expect("valid-rows");
1883 }
1884
1885 fn stc_screen_row(
1892 &self,
1893 scr: (upos_type, upos_type, upos_type),
1894 pos: TextPosition,
1895 ) -> Option<upos_type> {
1896 if pos < TextPosition::new(scr.0, scr.1) {
1897 return None;
1898 }
1899
1900 let line_breaks = self.value.cache().line_break.borrow();
1901 let range_start = TextPosition::new(scr.0, scr.1);
1902 let y2 = scr.1 + scr.2;
1903 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
1904
1905 let mut start_pos = TextPosition::new(scr.0, scr.1);
1906 let mut scr_row = 0;
1907 for (_key, cache) in line_breaks.range(range_start..range_end) {
1908 if pos < cache.start_pos {
1909 return Some(scr_row);
1910 }
1911 scr_row += 1;
1912 start_pos = cache.start_pos;
1913 }
1914
1915 if pos == start_pos {
1917 return Some(scr_row);
1918 }
1919
1920 None
1921 }
1922
1923 fn stc_sub_row_offset(
1930 &self,
1931 scr: (upos_type, upos_type, upos_type),
1932 mut scr_row: upos_type,
1933 ) -> (upos_type, upos_type) {
1934 let line_breaks = self.value.cache().line_break.borrow();
1935 let range_start = TextPosition::new(scr.0, scr.1);
1936 let y2 = scr.1 + scr.2;
1937 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
1938
1939 let mut start_pos = (scr.0, scr.1);
1940 for (_key, cache) in line_breaks.range(range_start..range_end) {
1941 if scr_row == 0 {
1942 return start_pos;
1943 }
1944 scr_row -= 1;
1945 start_pos = (cache.start_pos.x, cache.start_pos.y);
1946 }
1947
1948 start_pos
1950 }
1951}
1952
1953impl TextAreaState {
1954 #[allow(clippy::needless_return)]
1956 pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
1957 let scr_pos = (
1958 scr_pos.0 + self.dark_offset.0 as i16,
1959 scr_pos.1 + self.dark_offset.1 as i16,
1960 );
1961
1962 match self.text_wrap {
1963 TextWrap::Shift => {
1964 let (ox, _, oy) = self.clean_offset();
1965
1966 if oy >= self.len_lines() {
1967 return None;
1968 }
1969
1970 if scr_pos.1 < 0 {
1971 return Some(TextPosition::new(
1973 0,
1974 oy.saturating_add_signed(scr_pos.1 as ipos_type),
1975 ));
1976 } else if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
1977 return Some(TextPosition::new(0, self.len_lines().saturating_sub(1)));
1979 }
1980
1981 let pos_y = oy + scr_pos.1 as upos_type;
1982
1983 if scr_pos.0 < 0 {
1984 return Some(TextPosition::new(
1985 ox.saturating_add_signed(scr_pos.0 as ipos_type),
1986 pos_y,
1987 ));
1988 } else if scr_pos.0 as u16 >= self.rendered.width {
1989 return Some(TextPosition::new(
1990 min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
1991 pos_y,
1992 ));
1993 } else {
1994 let mut start_pos = TextPosition::new(0, pos_y);
1995 for g in self
1996 .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
1997 .expect("valid-position")
1998 {
1999 if g.contains_screen_x(scr_pos.0 as u16) {
2000 return Some(TextPosition::new(g.pos().x, pos_y));
2001 }
2002 start_pos = g.pos();
2003 }
2004 Some(start_pos)
2005 }
2006 }
2007 TextWrap::Hard | TextWrap::Word(_) => {
2008 let (_, sub_row_offset, oy) = self.clean_offset();
2009
2010 if oy >= self.len_lines() {
2011 return None;
2012 }
2013
2014 if scr_pos.1 < 0 {
2015 let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2024
2025 self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2026
2027 let n_start_pos = 'f: {
2028 let line_break = self.value.cache().line_break.borrow();
2029 let start_range = TextPosition::new(0, ry);
2030 let end_range = TextPosition::new(sub_row_offset, oy);
2031
2032 let mut nrows = scr_pos.1.unsigned_abs();
2033 for (_break_pos, cache) in line_break.range(start_range..end_range).rev() {
2034 if nrows == 0 {
2035 break 'f cache.start_pos;
2036 }
2037 nrows -= 1;
2038 }
2039 TextPosition::new(0, ry)
2040 };
2041
2042 if scr_pos.0 < 0 {
2044 return Some(n_start_pos);
2045 }
2046
2047 let min_row = n_start_pos.y;
2048 let max_row = min(n_start_pos.y + 1, self.len_lines());
2049 for g in self
2050 .glyphs2(0, n_start_pos.x, min_row..max_row)
2051 .expect("valid-rows")
2052 {
2053 if g.contains_screen_x(scr_pos.0 as u16) {
2054 return Some(g.pos());
2055 }
2056 }
2057
2058 return Some(n_start_pos);
2060 } else {
2061 let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2062
2063 let n_start_pos = if scr_pos.1 == 0 {
2065 TextPosition::new(sub_row_offset, oy)
2066 } else {
2067 self.fill_cache(
2069 0,
2070 sub_row_offset,
2071 oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2072 )
2073 .expect("valid-rows");
2074
2075 'f: {
2076 let text_range = self.value.cache().line_break.borrow();
2077 let start_range = TextPosition::new(sub_row_offset, oy);
2078 let end_range = TextPosition::new(0, self.len_lines());
2079
2080 let mut nrows = scr_pos.1 - 1;
2081 let mut start_pos = TextPosition::new(sub_row_offset, oy);
2082 for (_break_pos, cache) in text_range.range(start_range..end_range) {
2083 if nrows == 0 {
2084 break 'f cache.start_pos;
2085 }
2086 start_pos = cache.start_pos;
2087 nrows -= 1;
2088 }
2089 start_pos
2090 }
2091 };
2092
2093 let min_row = n_start_pos.y;
2094 let max_row = min(n_start_pos.y + 1, self.len_lines());
2095 for g in self
2096 .glyphs2(0, n_start_pos.x, min_row..max_row)
2097 .expect("valid-rows")
2098 {
2099 if g.contains_screen_x(scr_pos.0) {
2100 return Some(g.pos());
2101 }
2102 }
2103
2104 return Some(n_start_pos);
2106 }
2107 }
2108 }
2109 }
2110
2111 pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
2113 let scr_pos = (
2114 scr_pos.0 as i16 - self.inner.x as i16,
2115 scr_pos.1 as i16 - self.inner.y as i16,
2116 );
2117 self.relative_screen_to_pos(scr_pos)
2118 }
2119
2120 #[allow(clippy::explicit_counter_loop)]
2130 pub fn pos_to_relative_screen(&self, pos: impl Into<TextPosition>) -> Option<(i16, i16)> {
2131 let pos = pos.into();
2132 match self.text_wrap {
2133 TextWrap::Shift => {
2134 let (ox, _, oy) = self.clean_offset();
2135
2136 if oy > self.len_lines() {
2137 return None;
2138 }
2139 if pos.y < oy {
2140 return None;
2141 }
2142 if pos.y > self.len_lines() {
2143 return None;
2144 }
2145 if pos.y - oy >= self.rendered.height as u32 {
2146 return None;
2147 }
2148
2149 let screen_y = (pos.y - oy) as u16;
2150
2151 let screen_x = 'f: {
2152 for g in self
2153 .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2154 .expect("valid-row")
2155 {
2156 if g.pos().x == pos.x {
2157 break 'f g.screen_pos().0;
2158 } else if g.line_break() {
2159 break 'f g.screen_pos().0;
2160 }
2161 }
2162 0
2164 };
2165 assert!(screen_x <= self.rendered.width);
2166
2167 Some((
2168 screen_x as i16 - self.dark_offset.0 as i16,
2169 screen_y as i16 - self.dark_offset.1 as i16,
2170 ))
2171 }
2172 TextWrap::Hard | TextWrap::Word(_) => {
2173 let (_, sub_row_offset, oy) = self.clean_offset();
2174
2175 if oy > self.len_lines() {
2176 return None;
2177 }
2178 if pos.y < oy {
2179 return None;
2180 }
2181 if pos.y > self.len_lines() {
2182 return None;
2183 }
2184
2185 let page = self.rendered.height as upos_type;
2186 let scr = (sub_row_offset, oy, page);
2187 self.stc_fill_screen_cache(scr);
2188 let (screen_y, start_pos) = if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2189 if pos_row >= page {
2190 return None;
2192 }
2193 let start_pos = self.stc_sub_row_offset(scr, pos_row);
2194 (pos_row, start_pos)
2195 } else {
2196 return None;
2198 };
2199
2200 let screen_x = 'f: {
2201 for g in self
2202 .glyphs2(
2203 0,
2204 start_pos.0,
2205 start_pos.1..min(start_pos.1 + 1, self.len_lines()),
2206 )
2207 .expect("valid-row")
2208 {
2209 if g.pos().x == pos.x {
2210 break 'f g.screen_pos().0;
2211 } else if g.line_break() {
2212 break 'f g.screen_pos().0;
2213 }
2214 }
2215 0
2217 };
2218 assert!(screen_x <= self.rendered.width);
2219
2220 let scr = (
2221 screen_x as i16 - self.dark_offset.0 as i16,
2222 screen_y as i16 - self.dark_offset.1 as i16,
2223 );
2224 Some(scr)
2225 }
2226 }
2227 }
2228
2229 #[inline]
2232 pub fn pos_to_screen(&self, pos: impl Into<TextPosition>) -> Option<(u16, u16)> {
2233 let scr_pos = self.pos_to_relative_screen(pos.into())?;
2234 if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
2235 Some((
2236 (scr_pos.0 + self.inner.x as i16) as u16,
2237 (scr_pos.1 + self.inner.y as i16) as u16,
2238 ))
2239 } else {
2240 None
2241 }
2242 }
2243
2244 pub fn pos_to_line_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
2246 let pos = pos.into();
2247 match self.text_wrap {
2248 TextWrap::Shift => {
2249 TextPosition::new(0, pos.y)
2251 }
2252 TextWrap::Hard | TextWrap::Word(_) => {
2253 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2254 .expect("valid-row");
2255
2256 let mut start_pos = TextPosition::new(0, pos.y);
2257 for (break_pos, _) in self.value.cache().line_break.borrow().range(
2258 TextPosition::new(0, pos.y)
2259 ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
2260 ) {
2261 if pos >= start_pos && &pos <= break_pos {
2262 break;
2263 }
2264 start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2265 }
2266
2267 start_pos
2268 }
2269 }
2270 }
2271
2272 pub fn pos_to_line_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
2274 let pos = pos.into();
2275
2276 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2277 .expect("valid-row");
2278
2279 let mut end_pos = TextPosition::new(0, pos.y);
2280 for (break_pos, _) in self
2281 .value
2282 .cache()
2283 .line_break
2284 .borrow()
2285 .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2286 {
2287 if pos >= end_pos && &pos <= break_pos {
2288 end_pos = *break_pos;
2289 break;
2290 }
2291 end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2292 }
2293
2294 end_pos
2295 }
2296
2297 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2303 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2304 return false;
2305 };
2306 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2307 self.set_move_col(Some(scr_cursor.0));
2308 }
2309 self.set_cursor(cursor, extend_selection)
2310 }
2311
2312 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2319 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2320 return false;
2321 };
2322
2323 let anchor = self.anchor();
2324 let cursor = if cursor < anchor {
2325 self.word_start(cursor)
2326 } else {
2327 self.word_end(cursor)
2328 };
2329
2330 if !self.is_word_boundary(anchor) {
2332 if cursor < anchor {
2333 self.set_cursor(self.word_end(anchor), false);
2334 } else {
2335 self.set_cursor(self.word_start(anchor), false);
2336 }
2337 }
2338
2339 self.set_cursor(cursor, extend_selection)
2340 }
2341}
2342
2343impl TextAreaState {
2344 pub fn vertical_max_offset(&self) -> upos_type {
2349 self.vscroll.max_offset() as upos_type
2350 }
2351
2352 pub fn vertical_offset(&self) -> upos_type {
2354 self.vscroll.offset() as upos_type
2355 }
2356
2357 pub fn vertical_page(&self) -> upos_type {
2359 self.vscroll.page_len() as upos_type
2360 }
2361
2362 pub fn vertical_scroll(&self) -> upos_type {
2364 self.vscroll.scroll_by() as upos_type
2365 }
2366
2367 pub fn horizontal_max_offset(&self) -> upos_type {
2376 self.hscroll.max_offset() as upos_type
2377 }
2378
2379 pub fn horizontal_offset(&self) -> upos_type {
2381 self.hscroll.offset() as upos_type
2382 }
2383
2384 pub fn horizontal_page(&self) -> upos_type {
2386 self.hscroll.page_len() as upos_type
2387 }
2388
2389 pub fn horizontal_scroll(&self) -> upos_type {
2391 self.hscroll.scroll_by() as upos_type
2392 }
2393
2394 pub fn set_vertical_offset(&mut self, row_offset: upos_type) -> bool {
2401 self.scroll_to_cursor.set(false);
2402 self.sub_row_offset = 0;
2403 self.vscroll.set_offset(row_offset as usize)
2404 }
2405
2406 pub fn set_horizontal_offset(&mut self, col_offset: upos_type) -> bool {
2415 self.scroll_to_cursor.set(false);
2416 self.hscroll.set_offset(col_offset as usize)
2417 }
2418
2419 pub fn scroll_cursor_to_visible(&mut self) {
2425 self.scroll_to_cursor.set(true);
2426 }
2427
2428 pub fn scroll_to_pos(&mut self, pos: impl Into<TextPosition>) -> bool {
2434 let old_offset = self.clean_offset();
2435
2436 let pos = pos.into();
2437
2438 'f: {
2439 match self.text_wrap {
2440 TextWrap::Shift => {
2441 let (ox, _, oy) = old_offset;
2442
2443 let height = self.rendered.height as upos_type;
2444 let width = self.rendered.width as upos_type;
2445 let width = if self.show_ctrl() || self.wrap_ctrl() {
2446 width.saturating_sub(1)
2447 } else {
2448 width
2449 };
2450
2451 let noy = if pos.y < oy.saturating_sub(height) {
2452 pos.y.saturating_sub(height * 4 / 10)
2453 } else if pos.y < oy {
2454 pos.y
2455 } else if pos.y >= oy + 2 * height {
2456 pos.y.saturating_sub(height * 6 / 10)
2457 } else if pos.y >= oy + height {
2458 pos.y.saturating_sub(height.saturating_sub(1))
2459 } else {
2460 oy
2461 };
2462
2463 let nox = if pos.x < ox {
2464 pos.x
2465 } else if pos.x >= ox + width {
2466 pos.x.saturating_sub(width) + 1
2467 } else {
2468 ox
2469 };
2470
2471 self.set_offset((nox, noy));
2472 self.set_sub_row_offset(0);
2473 }
2474 TextWrap::Hard | TextWrap::Word(_) => {
2475 let (_ox, sub_row_offset, oy) = old_offset;
2476 let page = self.rendered.height as upos_type;
2477
2478 let scr = (0, oy.saturating_sub(page), 3 * page);
2480 self.stc_fill_screen_cache(scr);
2481 if let Some(off_row) =
2482 self.stc_screen_row(scr, TextPosition::new(sub_row_offset, oy))
2483 {
2484 if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2485 if pos_row < off_row && pos_row >= off_row.saturating_sub(page) {
2486 let noff_row = pos_row;
2487 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2488 self.set_offset((0, noy));
2489 self.set_sub_row_offset(nsub_row_offset);
2490 break 'f;
2491 } else if pos_row >= off_row + page && pos_row < off_row + 2 * page {
2492 let noff_row = pos_row.saturating_sub(page.saturating_sub(1));
2493 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2494 self.set_offset((0, noy));
2495 self.set_sub_row_offset(nsub_row_offset);
2496 break 'f;
2497 } else if pos_row >= off_row && pos_row < off_row + page {
2498 break 'f;
2499 }
2500 }
2501 }
2502
2503 let alt_scr = (0, pos.y.saturating_sub(page), 3 * page);
2505 self.stc_fill_screen_cache(alt_scr);
2506 if let Some(alt_scr_row) = self.stc_screen_row(alt_scr, pos) {
2507 let noff_row = alt_scr_row.saturating_sub(page * 5 / 10);
2508 let (nsub_row_offset, noy) = self.stc_sub_row_offset(alt_scr, noff_row);
2509 self.set_offset((0, noy));
2510 self.set_sub_row_offset(nsub_row_offset);
2511 } else {
2512 self.set_offset((0, pos.y));
2513 self.set_sub_row_offset(0);
2514 }
2515 }
2516 }
2517 }
2518
2519 old_offset != self.clean_offset()
2520 }
2521
2522 pub fn scroll_to_row(&mut self, pos: upos_type) -> bool {
2531 self.scroll_to_cursor.set(false);
2532
2533 match self.text_wrap {
2534 TextWrap::Shift => self.vscroll.scroll_to_pos(pos as usize),
2535 TextWrap::Hard | TextWrap::Word(_) => self
2536 .vscroll
2537 .set_offset(self.vscroll.limited_offset(pos as usize)),
2538 }
2539 }
2540
2541 pub fn scroll_to_col(&mut self, pos: upos_type) -> bool {
2549 self.scroll_to_cursor.set(false);
2550 self.hscroll.set_offset(pos as usize)
2551 }
2552
2553 pub fn scroll_up(&mut self, delta: upos_type) -> bool {
2559 if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2560 self.sub_row_offset = pos.x;
2561 self.vscroll.set_offset(pos.y as usize);
2562 true
2563 } else {
2564 false
2565 }
2566 }
2567
2568 pub fn scroll_down(&mut self, delta: upos_type) -> bool {
2574 if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2575 self.sub_row_offset = pos.x;
2576 self.vscroll.set_offset(pos.y as usize);
2577 true
2578 } else {
2579 false
2580 }
2581 }
2582
2583 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
2595 self.hscroll
2596 .set_offset(self.hscroll.offset.saturating_add(delta as usize))
2597 }
2598
2599 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
2611 self.hscroll
2612 .set_offset(self.hscroll.offset.saturating_sub(delta as usize))
2613 }
2614}
2615
2616impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2617 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2618 fn tc(r: bool) -> TextOutcome {
2620 if r {
2621 TextOutcome::TextChanged
2622 } else {
2623 TextOutcome::Unchanged
2624 }
2625 }
2626
2627 let mut r = if self.is_focused() {
2628 match event {
2629 ct_event!(key press c)
2630 | ct_event!(key press SHIFT-c)
2631 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2632 ct_event!(keycode press Tab) => {
2633 tc(if !self.focus.gained() {
2635 self.insert_tab()
2636 } else {
2637 false
2638 })
2639 }
2640 ct_event!(keycode press SHIFT-BackTab) => {
2641 tc(if !self.focus.gained() {
2643 self.insert_backtab()
2644 } else {
2645 false
2646 })
2647 }
2648 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2649 ct_event!(keycode press Backspace) => tc(self.delete_prev_char()),
2650 ct_event!(keycode press Delete) => tc(self.delete_next_char()),
2651 ct_event!(keycode press CONTROL-Backspace)
2652 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2653 ct_event!(keycode press CONTROL-Delete) | ct_event!(keycode press ALT-Delete) => {
2654 tc(self.delete_next_word())
2655 }
2656 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2657 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2658 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2659 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2660 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2661 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2662
2663 ct_event!(key release _)
2664 | ct_event!(key release SHIFT-_)
2665 | ct_event!(key release CONTROL_ALT-_)
2666 | ct_event!(keycode release Tab)
2667 | ct_event!(keycode release Enter)
2668 | ct_event!(keycode release Backspace)
2669 | ct_event!(keycode release Delete)
2670 | ct_event!(keycode release CONTROL-Backspace)
2671 | ct_event!(keycode release ALT-Backspace)
2672 | ct_event!(keycode release CONTROL-Delete)
2673 | ct_event!(key release CONTROL-'x')
2674 | ct_event!(key release CONTROL-'v')
2675 | ct_event!(key release CONTROL-'d')
2676 | ct_event!(key release CONTROL-'y')
2677 | ct_event!(key release CONTROL-'z')
2678 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2679 _ => TextOutcome::Continue,
2680 }
2681 } else {
2682 TextOutcome::Continue
2683 };
2684 if r == TextOutcome::Continue {
2685 r = self.handle(event, ReadOnly);
2686 }
2687 r
2688 }
2689}
2690
2691pub const MATCH_STYLE: usize = 100_001;
2693
2694impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
2695 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2696 let mut r = if self.is_focused() {
2697 match event {
2698 ct_event!(keycode press Left) => self.move_left(1, false).into(),
2699 ct_event!(keycode press Right) => self.move_right(1, false).into(),
2700 ct_event!(keycode press Up) => self.move_up(1, false).into(),
2701 ct_event!(keycode press Down) => self.move_down(1, false).into(),
2702 ct_event!(keycode press PageUp) => {
2703 self.move_up(self.vertical_page() as u16, false).into()
2704 }
2705 ct_event!(keycode press PageDown) => {
2706 self.move_down(self.vertical_page() as u16, false).into()
2707 }
2708 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
2709 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
2710 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
2711 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
2712 ct_event!(keycode press CONTROL-Up) => false.into(),
2713 ct_event!(keycode press CONTROL-Down) => false.into(),
2714 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
2715 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
2716 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
2717 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
2718
2719 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
2720 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
2721 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
2722 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
2723 ct_event!(keycode press ALT-PageUp) => {
2724 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
2725 }
2726 ct_event!(keycode press ALT-PageDown) => {
2727 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
2728 }
2729 ct_event!(keycode press ALT_SHIFT-PageUp) => {
2730 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
2731 }
2732 ct_event!(keycode press ALT_SHIFT-PageDown) => {
2733 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
2734 }
2735
2736 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
2737 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
2738 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
2739 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
2740 ct_event!(keycode press SHIFT-PageUp) => {
2741 self.move_up(self.vertical_page() as u16, true).into()
2742 }
2743 ct_event!(keycode press SHIFT-PageDown) => {
2744 self.move_down(self.vertical_page() as u16, true).into()
2745 }
2746 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
2747 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
2748 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
2749 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
2750 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
2751 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
2752 ct_event!(key press CONTROL-'a') => self.select_all().into(),
2753 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
2754
2755 ct_event!(keycode press F(3)) => self.move_to_next_match().into(),
2756 ct_event!(keycode press SHIFT-F(3)) => self.move_to_prev_match().into(),
2757
2758 ct_event!(keycode release Left)
2759 | ct_event!(keycode release Right)
2760 | ct_event!(keycode release Up)
2761 | ct_event!(keycode release Down)
2762 | ct_event!(keycode release PageUp)
2763 | ct_event!(keycode release PageDown)
2764 | ct_event!(keycode release Home)
2765 | ct_event!(keycode release End)
2766 | ct_event!(keycode release CONTROL-Left)
2767 | ct_event!(keycode release CONTROL-Right)
2768 | ct_event!(keycode release CONTROL-Up)
2769 | ct_event!(keycode release CONTROL-Down)
2770 | ct_event!(keycode release CONTROL-PageUp)
2771 | ct_event!(keycode release CONTROL-PageDown)
2772 | ct_event!(keycode release CONTROL-Home)
2773 | ct_event!(keycode release CONTROL-End)
2774 | ct_event!(keycode release ALT-Left)
2775 | ct_event!(keycode release ALT-Right)
2776 | ct_event!(keycode release ALT-Up)
2777 | ct_event!(keycode release ALT-Down)
2778 | ct_event!(keycode release ALT-PageUp)
2779 | ct_event!(keycode release ALT-PageDown)
2780 | ct_event!(keycode release ALT_SHIFT-PageUp)
2781 | ct_event!(keycode release ALT_SHIFT-PageDown)
2782 | ct_event!(keycode release SHIFT-Left)
2783 | ct_event!(keycode release SHIFT-Right)
2784 | ct_event!(keycode release SHIFT-Up)
2785 | ct_event!(keycode release SHIFT-Down)
2786 | ct_event!(keycode release SHIFT-PageUp)
2787 | ct_event!(keycode release SHIFT-PageDown)
2788 | ct_event!(keycode release SHIFT-Home)
2789 | ct_event!(keycode release SHIFT-End)
2790 | ct_event!(keycode release CONTROL_SHIFT-Left)
2791 | ct_event!(keycode release CONTROL_SHIFT-Right)
2792 | ct_event!(keycode release CONTROL_SHIFT-Home)
2793 | ct_event!(keycode release CONTROL_SHIFT-End)
2794 | ct_event!(key release CONTROL-'a')
2795 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2796 _ => TextOutcome::Continue,
2797 }
2798 } else {
2799 TextOutcome::Continue
2800 };
2801
2802 if r == TextOutcome::Continue {
2803 r = self.handle(event, MouseOnly);
2804 }
2805 r
2806 }
2807}
2808
2809impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
2810 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
2811 flow!(match event {
2812 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2813 let cx = m.column as i16 - self.inner.x as i16;
2814 let cy = m.row as i16 - self.inner.y as i16;
2815 self.set_screen_cursor((cx, cy), true).into()
2816 }
2817 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2818 let cx = m.column as i16 - self.inner.x as i16;
2819 let cy = m.row as i16 - self.inner.y as i16;
2820 self.set_screen_cursor_words((cx, cy), true).into()
2821 }
2822 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2823 if let Some(test) = self.screen_to_pos((m.column, m.row)) {
2824 let start = self.word_start(test);
2825 let end = self.word_end(test);
2826 self.set_selection(start, end).into()
2827 } else {
2828 TextOutcome::Unchanged
2829 }
2830 }
2831 ct_event!(mouse down Left for column,row) => {
2832 if self.inner.contains((*column, *row).into()) {
2833 let cx = (column - self.inner.x) as i16;
2834 let cy = (row - self.inner.y) as i16;
2835 self.set_screen_cursor((cx, cy), false).into()
2836 } else {
2837 TextOutcome::Continue
2838 }
2839 }
2840 ct_event!(mouse down SHIFT-Left for column,row) => {
2841 if self.inner.contains((*column, *row).into()) {
2842 let cx = (column - self.inner.x) as i16;
2843 let cy = (row - self.inner.y) as i16;
2844 self.set_screen_cursor((cx, cy), true).into()
2845 } else {
2846 TextOutcome::Continue
2847 }
2848 }
2849 ct_event!(mouse down CONTROL-Left for column,row) => {
2850 if self.inner.contains((*column, *row).into()) {
2851 let cx = (column - self.inner.x) as i16;
2852 let cy = (row - self.inner.y) as i16;
2853 self.set_screen_cursor((cx, cy), true).into()
2854 } else {
2855 TextOutcome::Continue
2856 }
2857 }
2858 ct_event!(mouse down ALT-Left for column,row) => {
2859 if self.inner.contains((*column, *row).into()) {
2860 let cx = (column - self.inner.x) as i16;
2861 let cy = (row - self.inner.y) as i16;
2862 self.set_screen_cursor_words((cx, cy), true).into()
2863 } else {
2864 TextOutcome::Continue
2865 }
2866 }
2867 _ => TextOutcome::Continue,
2868 });
2869
2870 let mut sas = ScrollAreaState::new()
2871 .area(self.inner)
2872 .h_scroll(&mut self.hscroll)
2873 .v_scroll(&mut self.vscroll);
2874 let r = match sas.handle(event, MouseOnly) {
2875 ScrollOutcome::Up(v) => self.scroll_up(v as upos_type),
2876 ScrollOutcome::Down(v) => self.scroll_down(v as upos_type),
2877 ScrollOutcome::Left(v) => self.scroll_left(v as upos_type),
2878 ScrollOutcome::Right(v) => self.scroll_right(v as upos_type),
2879 ScrollOutcome::VPos(v) => self.scroll_to_row(v as upos_type),
2880 ScrollOutcome::HPos(v) => self.scroll_to_col(v as upos_type),
2881 _ => false,
2882 };
2883 if r {
2884 return TextOutcome::Changed;
2885 }
2886
2887 TextOutcome::Continue
2888 }
2889}
2890
2891pub fn handle_events(
2895 state: &mut TextAreaState,
2896 focus: bool,
2897 event: &crossterm::event::Event,
2898) -> TextOutcome {
2899 state.focus.set(focus);
2900 state.handle(event, Regular)
2901}
2902
2903pub fn handle_readonly_events(
2907 state: &mut TextAreaState,
2908 focus: bool,
2909 event: &crossterm::event::Event,
2910) -> TextOutcome {
2911 state.focus.set(focus);
2912 state.handle(event, ReadOnly)
2913}
2914
2915pub fn handle_mouse_events(
2917 state: &mut TextAreaState,
2918 event: &crossterm::event::Event,
2919) -> TextOutcome {
2920 state.handle(event, MouseOnly)
2921}