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_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: self.focus.new_instance(),
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 pub fn width(&self) -> u16 {
376 0
377 }
378
379 pub fn height(&self) -> u16 {
381 1
382 }
383}
384
385impl<'a> StatefulWidget for &TextArea<'a> {
386 type State = TextAreaState;
387
388 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
389 render_text_area(self, area, buf, state);
390 }
391}
392
393impl StatefulWidget for TextArea<'_> {
394 type State = TextAreaState;
395
396 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
397 render_text_area(&self, area, buf, state);
398 }
399}
400
401fn render_text_area(
402 widget: &TextArea<'_>,
403 area: Rect,
404 buf: &mut Buffer,
405 state: &mut TextAreaState,
406) {
407 state.area = area;
408 state.screen_cursor = None;
409 if let Some(text_wrap) = widget.text_wrap {
410 state.text_wrap = text_wrap;
411 }
412
413 let style = widget.style;
414 let focus_style = if let Some(focus_style) = widget.focus_style {
415 focus_style
416 } else {
417 style
418 };
419 let select_style = if let Some(select_style) = widget.select_style {
420 select_style
421 } else {
422 Style::default().black().on_yellow()
423 };
424 let (style, select_style) = if state.is_focused() {
425 (
426 style.patch(focus_style),
427 style.patch(focus_style).patch(select_style),
428 )
429 } else {
430 (style, style.patch(select_style))
431 };
432
433 state.area = area;
435 state.screen_cursor = None;
436 state.inner = ScrollArea::new()
437 .block(widget.block.as_ref())
438 .h_scroll(widget.hscroll.as_ref())
439 .v_scroll(widget.vscroll.as_ref())
440 .inner(area, Some(&state.hscroll), Some(&state.vscroll));
441 state.rendered = state.inner.as_size();
442
443 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
444 state.hscroll.set_max_offset(0);
445 state.hscroll.set_overscroll_by(None);
446 } else {
447 if let Some(h_max_offset) = widget.h_max_offset {
448 state.hscroll.set_max_offset(h_max_offset as usize);
449 }
450 if let Some(h_overscroll) = widget.h_overscroll {
451 state.hscroll.set_overscroll_by(Some(h_overscroll as usize));
452 }
453 }
454 state.hscroll.set_page_len(state.inner.width as usize);
455
456 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
457 state
458 .vscroll
459 .set_max_offset(state.len_lines().saturating_sub(1) as usize);
460 } else {
461 state.vscroll.set_max_offset(
462 state
463 .len_lines()
464 .saturating_sub(state.inner.height as upos_type) as usize,
465 );
466 }
467 state.vscroll.set_page_len(state.inner.height as usize);
468
469 if state.scroll_to_cursor.get() {
470 state.scroll_to_pos(state.cursor());
471 }
472
473 ScrollArea::new()
475 .block(widget.block.as_ref())
476 .h_scroll(widget.hscroll.as_ref())
477 .v_scroll(widget.vscroll.as_ref())
478 .style(style)
479 .render(
480 area,
481 buf,
482 &mut ScrollAreaState::new()
483 .h_scroll(&mut state.hscroll)
484 .v_scroll(&mut state.vscroll),
485 );
486
487 if state.inner.width == 0 || state.inner.height == 0 {
488 return;
490 }
491 if state.vscroll.offset() > state.value.len_lines() as usize {
492 return;
494 }
495
496 let (shift_left, sub_row_offset, start_row) = state.clean_offset();
497 let page_rows = start_row
498 ..min(
499 start_row + state.inner.height as upos_type,
500 state.value.len_lines(),
501 );
502 let page_bytes = state
503 .try_bytes_at_range(TextRange::new(
504 (sub_row_offset, page_rows.start),
505 (0, page_rows.end),
506 ))
507 .expect("valid_rows");
508 let selection = state.selection();
510 let mut styles = Vec::new();
511
512 for g in state
513 .glyphs2(shift_left, sub_row_offset, page_rows)
514 .expect("valid_offset")
515 {
516 let screen_pos = g.screen_pos();
518
519 if screen_pos.1 >= state.inner.height {
520 break;
521 }
522
523 if g.screen_width() > 0 {
524 let mut style = style;
525 state
527 .value
528 .styles_at_page(g.text_bytes().start, page_bytes.clone(), &mut styles);
529 for style_nr in &styles {
530 if let Some(s) = widget.text_style.get(style_nr) {
531 style = style.patch(*s);
532 }
533 }
534 if selection.contains_pos(g.pos()) {
536 style = style.patch(select_style);
537 };
538
539 if let Some(cell) =
541 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
542 {
543 cell.set_symbol(g.glyph());
544 cell.set_style(style);
545 }
546 for d in 1..g.screen_width() {
548 if let Some(cell) = buf.cell_mut((
549 state.inner.x + screen_pos.0 + d,
550 state.inner.y + screen_pos.1,
551 )) {
552 cell.reset();
553 cell.set_style(style);
554 }
555 }
556 }
557 }
558
559 state.screen_cursor = state.pos_to_screen(state.cursor());
560}
561
562impl Default for TextAreaState {
563 fn default() -> Self {
564 #[cfg(windows)]
565 const LINE_ENDING: &str = "\r\n";
566
567 #[cfg(not(windows))]
568 const LINE_ENDING: &str = "\n";
569
570 let mut s = Self {
571 area: Default::default(),
572 inner: Default::default(),
573 rendered: Default::default(),
574 screen_cursor: Default::default(),
575 hscroll: Default::default(),
576 vscroll: Default::default(),
577 sub_row_offset: 0,
578 dark_offset: Default::default(),
579 scroll_to_cursor: Default::default(),
580 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
581 move_col: Default::default(),
582 auto_indent: true,
583 auto_quote: true,
584 text_wrap: TextWrap::Shift,
585 newline: LINE_ENDING.to_string(),
586 tab_width: 8,
587 expand_tabs: true,
588 focus: Default::default(),
589 mouse: Default::default(),
590 non_exhaustive: NonExhaustive,
591 };
592 s.hscroll.set_max_offset(255);
593 s.hscroll.set_overscroll_by(Some(16384));
594 s
595 }
596}
597
598impl HasFocus for TextAreaState {
599 fn build(&self, builder: &mut FocusBuilder) {
600 builder.leaf_widget(self);
601 }
602
603 fn focus(&self) -> FocusFlag {
604 self.focus.clone()
605 }
606
607 fn area(&self) -> Rect {
608 self.area
609 }
610
611 fn navigable(&self) -> Navigation {
612 Navigation::Reach
613 }
614}
615
616impl TextAreaState {
617 #[inline]
619 pub fn new() -> Self {
620 Self::default()
621 }
622
623 #[inline]
625 pub fn named(name: &str) -> Self {
626 Self {
627 focus: FocusFlag::new().with_name(name),
628 ..Default::default()
629 }
630 }
631
632 #[inline]
638 pub fn set_newline(&mut self, br: impl Into<String>) {
639 self.newline = br.into();
640 }
641
642 #[inline]
644 pub fn newline(&self) -> &str {
645 &self.newline
646 }
647
648 #[inline]
650 pub fn set_auto_indent(&mut self, indent: bool) {
651 self.auto_indent = indent;
652 }
653
654 #[inline]
656 pub fn set_auto_quote(&mut self, quote: bool) {
657 self.auto_quote = quote;
658 }
659
660 #[inline]
662 pub fn set_tab_width(&mut self, tabs: u32) {
663 self.tab_width = tabs;
664 }
665
666 #[inline]
668 pub fn tab_width(&self) -> u32 {
669 self.tab_width
670 }
671
672 #[inline]
674 pub fn set_expand_tabs(&mut self, expand: bool) {
675 self.expand_tabs = expand;
676 }
677
678 #[inline]
680 pub fn expand_tabs(&self) -> bool {
681 self.expand_tabs
682 }
683
684 #[inline]
686 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
687 self.value.set_glyph_ctrl(show_ctrl);
688 }
689
690 pub fn show_ctrl(&self) -> bool {
692 self.value.glyph_ctrl()
693 }
694
695 #[inline]
698 pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
699 self.value.set_wrap_ctrl(wrap_ctrl);
700 }
701
702 pub fn wrap_ctrl(&self) -> bool {
705 self.value.wrap_ctrl()
706 }
707
708 pub fn set_text_wrap(&mut self, text_wrap: TextWrap) {
721 self.text_wrap = text_wrap;
722 }
723
724 pub fn text_wrap(&self) -> TextWrap {
726 self.text_wrap
727 }
728
729 #[inline]
739 pub fn set_move_col(&mut self, col: Option<i16>) {
740 self.move_col = col;
741 }
742
743 #[inline]
745 pub fn move_col(&mut self) -> Option<i16> {
746 self.move_col
747 }
748}
749
750impl TextAreaState {
751 #[inline]
754 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
755 match clip {
756 None => self.value.set_clipboard(None),
757 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
758 }
759 }
760
761 #[inline]
764 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
765 self.value.clipboard()
766 }
767
768 #[inline]
770 pub fn copy_to_clip(&mut self) -> bool {
771 let Some(clip) = self.value.clipboard() else {
772 return false;
773 };
774
775 _ = clip.set_string(self.selected_text().as_ref());
776 false
777 }
778
779 #[inline]
781 pub fn cut_to_clip(&mut self) -> bool {
782 let Some(clip) = self.value.clipboard() else {
783 return false;
784 };
785
786 match clip.set_string(self.selected_text().as_ref()) {
787 Ok(_) => self.delete_range(self.selection()),
788 Err(_) => false,
789 }
790 }
791
792 #[inline]
794 pub fn paste_from_clip(&mut self) -> bool {
795 let Some(clip) = self.value.clipboard() else {
796 return false;
797 };
798
799 if let Ok(text) = clip.get_string() {
800 self.insert_str(text)
801 } else {
802 false
803 }
804 }
805}
806
807impl TextAreaState {
808 #[inline]
815 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
816 match undo {
817 None => self.value.set_undo_buffer(None),
818 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
819 }
820 }
821
822 #[inline]
824 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
825 self.value.undo_buffer()
826 }
827
828 #[inline]
830 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
831 self.value.undo_buffer_mut()
832 }
833
834 #[inline]
840 pub fn begin_undo_seq(&mut self) {
841 self.value.begin_undo_seq()
842 }
843
844 #[inline]
846 pub fn end_undo_seq(&mut self) {
847 self.value.end_undo_seq()
848 }
849
850 #[inline]
855 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
856 self.value.recent_replay_log()
857 }
858
859 #[inline]
863 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
864 self.value.replay_log(replay)
865 }
866
867 #[inline]
869 pub fn undo(&mut self) -> bool {
870 self.value.undo()
871 }
872
873 #[inline]
875 pub fn redo(&mut self) -> bool {
876 self.value.redo()
877 }
878}
879
880impl TextAreaState {
881 #[inline]
898 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
899 self.value.set_styles(styles);
900 }
901
902 #[inline]
913 pub fn set_range_styles(&mut self, styles: Vec<(TextRange, usize)>) -> Result<(), TextError> {
914 let mut mapped = Vec::with_capacity(styles.len());
915 for (r, s) in styles {
916 let rr = self.value.bytes_at_range(r)?;
917 mapped.push((rr, s));
918 }
919 self.value.set_styles(mapped);
920 Ok(())
921 }
922
923 #[inline]
928 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
929 self.value.add_style(range, style);
930 }
931
932 #[inline]
936 pub fn add_range_style(
937 &mut self,
938 range: impl Into<TextRange>,
939 style: usize,
940 ) -> Result<(), TextError> {
941 let r = self.value.bytes_at_range(range.into())?;
942 self.value.add_style(r, style);
943 Ok(())
944 }
945
946 #[inline]
948 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
949 self.value.remove_style(range, style);
950 }
951
952 #[inline]
954 pub fn remove_style_fully(&mut self, style: usize) {
955 self.value.remove_style_fully(style);
956 }
957
958 #[inline]
960 pub fn remove_range_style(
961 &mut self,
962 range: impl Into<TextRange>,
963 style: usize,
964 ) -> Result<(), TextError> {
965 let r = self.value.bytes_at_range(range.into())?;
966 self.value.remove_style(r, style);
967 Ok(())
968 }
969
970 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
972 self.value.styles_in(range, buf)
973 }
974
975 pub fn styles_in_match(
977 &self,
978 range: Range<usize>,
979 style: usize,
980 buf: &mut Vec<(Range<usize>, usize)>,
981 ) {
982 self.value.styles_in_match(range, style, buf);
983 }
984
985 #[inline]
987 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
988 self.value.styles_at(byte_pos, buf)
989 }
990
991 #[inline]
994 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
995 self.value.styles_at_match(byte_pos, style)
996 }
997
998 #[inline]
1000 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
1001 self.value.styles()
1002 }
1003}
1004
1005impl TextAreaState {
1006 #[inline]
1008 pub fn offset(&self) -> (upos_type, upos_type) {
1009 (
1010 self.hscroll.offset() as upos_type,
1011 self.vscroll.offset() as upos_type,
1012 )
1013 }
1014
1015 #[inline]
1020 pub fn set_offset(&mut self, offset: (upos_type, upos_type)) -> bool {
1021 self.scroll_to_cursor.set(false);
1022 let c = self.hscroll.set_offset(offset.0 as usize);
1023 let r = self.vscroll.set_offset(offset.1 as usize);
1024 r || c
1025 }
1026
1027 pub fn set_sub_row_offset(&mut self, sub_row_offset: upos_type) -> bool {
1039 self.scroll_to_cursor.set(false);
1040 let old = self.sub_row_offset;
1041 self.sub_row_offset = sub_row_offset;
1042 sub_row_offset != old
1043 }
1044
1045 pub fn sub_row_offset(&self) -> upos_type {
1050 self.sub_row_offset
1051 }
1052
1053 fn clean_offset(&self) -> (upos_type, upos_type, upos_type) {
1058 let ox = self.hscroll.offset as upos_type;
1059 let mut oy = self.vscroll.offset as upos_type;
1060
1061 if oy >= self.len_lines() {
1063 oy = 0;
1064 }
1065
1066 match self.text_wrap {
1067 TextWrap::Shift => (ox, 0, oy),
1068 TextWrap::Hard | TextWrap::Word(_) => {
1069 if let Ok(max_col) = self.try_line_width(oy) {
1071 (0, min(self.sub_row_offset, max_col), oy)
1072 } else {
1073 (0, 0, oy)
1074 }
1075 }
1076 }
1077 }
1078
1079 #[inline]
1081 pub fn cursor(&self) -> TextPosition {
1082 self.value.cursor()
1083 }
1084
1085 #[inline]
1087 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
1088 self.scroll_cursor_to_visible();
1089 self.value.set_cursor(cursor.into(), extend_selection)
1090 }
1091
1092 #[inline]
1094 pub fn anchor(&self) -> TextPosition {
1095 self.value.anchor()
1096 }
1097
1098 #[inline]
1100 pub fn has_selection(&self) -> bool {
1101 self.value.has_selection()
1102 }
1103
1104 #[inline]
1106 pub fn selection(&self) -> TextRange {
1107 self.value.selection()
1108 }
1109
1110 #[inline]
1113 pub fn set_selection(
1114 &mut self,
1115 anchor: impl Into<TextPosition>,
1116 cursor: impl Into<TextPosition>,
1117 ) -> bool {
1118 self.scroll_cursor_to_visible();
1119 self.value.set_selection(anchor.into(), cursor.into())
1120 }
1121
1122 #[inline]
1125 pub fn select_all(&mut self) -> bool {
1126 self.scroll_cursor_to_visible();
1127 self.value.select_all()
1128 }
1129
1130 #[inline]
1132 pub fn selected_text(&self) -> Cow<'_, str> {
1133 self.value
1134 .str_slice(self.value.selection())
1135 .expect("valid_selection")
1136 }
1137}
1138
1139impl TextAreaState {
1140 #[inline]
1142 pub fn clear(&mut self) -> bool {
1143 if !self.is_empty() {
1144 self.value.clear();
1145 true
1146 } else {
1147 false
1148 }
1149 }
1150
1151 #[inline]
1155 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1156 self.scroll_to_cursor.set(false);
1157 self.vscroll.set_offset(0);
1158 self.hscroll.set_offset(0);
1159 self.set_sub_row_offset(0);
1160 self.set_move_col(None);
1161
1162 self.value.set_text(TextRope::new_text(s.as_ref()));
1163 }
1164
1165 #[inline]
1167 pub fn text(&self) -> String {
1168 self.value.text().string()
1169 }
1170
1171 #[inline]
1174 pub fn set_rope(&mut self, r: Rope) {
1175 self.scroll_to_cursor.set(false);
1176 self.vscroll.set_offset(0);
1177 self.hscroll.set_offset(0);
1178 self.set_sub_row_offset(0);
1179 self.set_move_col(None);
1180
1181 self.value.set_text(TextRope::new_rope(r));
1182 }
1183
1184 #[inline]
1186 pub fn rope(&self) -> &Rope {
1187 self.value.text().rope()
1188 }
1189
1190 #[inline]
1194 pub fn set_value<S: AsRef<str>>(&mut self, s: S) {
1195 self.set_text(s);
1196 }
1197
1198 #[inline]
1200 pub fn value(&self) -> String {
1201 self.text()
1202 }
1203
1204 #[inline]
1206 pub fn is_empty(&self) -> bool {
1207 self.value.is_empty()
1208 }
1209
1210 #[inline]
1214 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1215 self.value.str_slice_byte(range).expect("valid_range")
1216 }
1217
1218 #[inline]
1220 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1221 self.value.str_slice_byte(range)
1222 }
1223
1224 #[inline]
1228 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
1229 self.value.str_slice(range.into()).expect("valid_range")
1230 }
1231
1232 #[inline]
1234 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
1235 self.value.str_slice(range.into())
1236 }
1237
1238 #[inline]
1240 pub fn len_lines(&self) -> upos_type {
1241 self.value.len_lines()
1242 }
1243
1244 #[inline]
1246 pub fn len_bytes(&self) -> usize {
1247 self.value.len_bytes()
1248 }
1249
1250 #[inline]
1254 pub fn line_width(&self, row: upos_type) -> upos_type {
1255 self.try_line_width(row).expect("valid_row")
1256 }
1257
1258 #[inline]
1260 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1261 self.value.line_width(row)
1262 }
1263
1264 #[inline]
1269 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
1270 self.value.line_at(row).expect("valid_row")
1271 }
1272
1273 #[inline]
1276 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1277 self.value.line_at(row)
1278 }
1279
1280 #[inline]
1284 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
1285 self.value.lines_at(row).expect("valid_row")
1286 }
1287
1288 #[inline]
1290 pub fn try_lines_at(
1291 &self,
1292 row: upos_type,
1293 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1294 self.value.lines_at(row)
1295 }
1296
1297 #[inline]
1302 pub fn line_graphemes(&self, row: upos_type) -> <TextRope as TextStore>::GraphemeIter<'_> {
1303 self.value.line_graphemes(row).expect("valid_row")
1304 }
1305
1306 #[inline]
1309 pub fn try_line_graphemes(
1310 &self,
1311 row: upos_type,
1312 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1313 self.value.line_graphemes(row)
1314 }
1315
1316 #[inline]
1320 pub fn text_graphemes(
1321 &self,
1322 pos: impl Into<TextPosition>,
1323 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1324 self.value.text_graphemes(pos.into()).expect("valid_pos")
1325 }
1326
1327 #[inline]
1329 pub fn try_text_graphemes(
1330 &self,
1331 pos: impl Into<TextPosition>,
1332 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1333 self.value.text_graphemes(pos.into())
1334 }
1335
1336 #[inline]
1340 pub fn graphemes(
1341 &self,
1342 range: impl Into<TextRange>,
1343 pos: impl Into<TextPosition>,
1344 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1345 self.value
1346 .graphemes(range.into(), pos.into())
1347 .expect("valid_args")
1348 }
1349
1350 #[inline]
1352 pub fn try_graphemes(
1353 &self,
1354 range: impl Into<TextRange>,
1355 pos: impl Into<TextPosition>,
1356 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1357 self.value.graphemes(range.into(), pos.into())
1358 }
1359
1360 #[inline]
1365 pub fn byte_at(&self, pos: impl Into<TextPosition>) -> Range<usize> {
1366 self.value.byte_at(pos.into()).expect("valid_pos")
1367 }
1368
1369 #[inline]
1372 pub fn try_byte_at(&self, pos: impl Into<TextPosition>) -> Result<Range<usize>, TextError> {
1373 self.value.byte_at(pos.into())
1374 }
1375
1376 #[inline]
1380 pub fn bytes_at_range(&self, range: impl Into<TextRange>) -> Range<usize> {
1381 self.value
1382 .bytes_at_range(range.into())
1383 .expect("valid_range")
1384 }
1385
1386 #[inline]
1388 pub fn try_bytes_at_range(
1389 &self,
1390 range: impl Into<TextRange>,
1391 ) -> Result<Range<usize>, TextError> {
1392 self.value.bytes_at_range(range.into())
1393 }
1394
1395 #[inline]
1400 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1401 self.value.byte_pos(byte).expect("valid_pos")
1402 }
1403
1404 #[inline]
1407 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1408 self.value.byte_pos(byte)
1409 }
1410
1411 #[inline]
1415 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1416 self.value.byte_range(bytes).expect("valid_range")
1417 }
1418
1419 #[inline]
1421 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1422 self.value.byte_range(bytes)
1423 }
1424}
1425
1426impl TextAreaState {
1427 #[inline]
1433 pub fn insert_char(&mut self, c: char) -> bool {
1434 match c {
1435 '\n' => text_area_op::insert_newline(self),
1436 '\t' => text_area_op::insert_tab(self),
1437 c => text_area_op::insert_char(self, c),
1438 }
1439 }
1440
1441 #[inline]
1447 pub fn insert_tab(&mut self) -> bool {
1448 text_area_op::insert_tab(self)
1449 }
1450
1451 #[inline]
1456 pub fn insert_backtab(&mut self) -> bool {
1457 text_area_op::insert_backtab(self)
1458 }
1459
1460 #[inline]
1463 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1464 text_area_op::insert_str(self, t.as_ref())
1465 }
1466
1467 #[inline]
1472 pub fn insert_newline(&mut self) -> bool {
1473 text_area_op::insert_newline(self)
1474 }
1475
1476 #[inline]
1480 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1481 text_area_op::delete_range(self, range.into()).expect("valid_range")
1482 }
1483
1484 #[inline]
1486 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1487 text_area_op::delete_range(self, range.into())
1488 }
1489
1490 #[inline]
1493 pub fn duplicate_text(&mut self) -> bool {
1494 text_area_op::duplicate_text(self)
1495 }
1496
1497 #[inline]
1500 pub fn delete_line(&mut self) -> bool {
1501 text_area_op::delete_line(self)
1502 }
1503
1504 #[inline]
1507 pub fn delete_next_char(&mut self) -> bool {
1508 text_area_op::delete_next_char(self)
1509 }
1510
1511 #[inline]
1514 pub fn delete_prev_char(&mut self) -> bool {
1515 text_area_op::delete_prev_char(self)
1516 }
1517
1518 #[inline]
1523 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1524 core_op::next_word_start(&self.value, pos.into()).expect("valid_pos")
1525 }
1526
1527 #[inline]
1530 pub fn try_next_word_start(
1531 &self,
1532 pos: impl Into<TextPosition>,
1533 ) -> Result<TextPosition, TextError> {
1534 core_op::next_word_start(&self.value, pos.into())
1535 }
1536
1537 #[inline]
1542 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1543 core_op::next_word_end(&self.value, pos.into()).expect("valid_pos")
1544 }
1545
1546 #[inline]
1549 pub fn try_next_word_end(
1550 &self,
1551 pos: impl Into<TextPosition>,
1552 ) -> Result<TextPosition, TextError> {
1553 core_op::next_word_end(&self.value, pos.into())
1554 }
1555
1556 #[inline]
1564 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1565 core_op::prev_word_start(&self.value, pos.into()).expect("valid_pos")
1566 }
1567
1568 #[inline]
1574 pub fn try_prev_word_start(
1575 &self,
1576 pos: impl Into<TextPosition>,
1577 ) -> Result<TextPosition, TextError> {
1578 core_op::prev_word_start(&self.value, pos.into())
1579 }
1580
1581 #[inline]
1587 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1588 core_op::prev_word_end(&self.value, pos.into()).expect("valid_pos")
1589 }
1590
1591 #[inline]
1595 pub fn try_prev_word_end(
1596 &self,
1597 pos: impl Into<TextPosition>,
1598 ) -> Result<TextPosition, TextError> {
1599 core_op::prev_word_end(&self.value, pos.into())
1600 }
1601
1602 #[inline]
1606 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1607 core_op::is_word_boundary(&self.value, pos.into()).expect("valid_pos")
1608 }
1609
1610 #[inline]
1612 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1613 core_op::is_word_boundary(&self.value, pos.into())
1614 }
1615
1616 #[inline]
1621 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1622 core_op::word_start(&self.value, pos.into()).expect("valid_pos")
1623 }
1624
1625 #[inline]
1628 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1629 core_op::word_start(&self.value, pos.into())
1630 }
1631
1632 #[inline]
1637 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1638 core_op::word_end(&self.value, pos.into()).expect("valid_pos")
1639 }
1640
1641 #[inline]
1644 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1645 core_op::word_end(&self.value, pos.into())
1646 }
1647
1648 #[inline]
1653 pub fn delete_next_word(&mut self) -> bool {
1654 text_area_op::delete_next_word(self)
1655 }
1656
1657 #[inline]
1662 pub fn delete_prev_word(&mut self) -> bool {
1663 text_area_op::delete_prev_word(self)
1664 }
1665
1666 pub fn search(&mut self, re: &str) -> Result<bool, TextError> {
1677 match text_area_op::search(self, re, MATCH_STYLE) {
1678 Ok(r) => Ok(r),
1679 Err(_) => Err(TextError::InvalidSearch),
1680 }
1681 }
1682
1683 pub fn move_to_next_match(&mut self) -> bool {
1685 text_area_op::move_to_next_match(self, MATCH_STYLE)
1686 }
1687
1688 pub fn move_to_prev_match(&mut self) -> bool {
1690 text_area_op::move_to_prev_match(self, MATCH_STYLE)
1691 }
1692
1693 pub fn clear_search(&mut self) {
1695 text_area_op::clear_search(self, MATCH_STYLE)
1696 }
1697
1698 #[inline]
1701 pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1702 text_area_op::move_left(self, n, extend_selection)
1703 }
1704
1705 #[inline]
1708 pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1709 text_area_op::move_right(self, n, extend_selection)
1710 }
1711
1712 #[inline]
1715 pub fn move_up(&mut self, n: u16, extend_selection: bool) -> bool {
1716 text_area_op::move_up(self, n, extend_selection)
1717 }
1718
1719 #[inline]
1722 pub fn move_down(&mut self, n: u16, extend_selection: bool) -> bool {
1723 text_area_op::move_down(self, n, extend_selection)
1724 }
1725
1726 #[inline]
1730 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1731 text_area_op::move_to_line_start(self, extend_selection)
1732 }
1733
1734 #[inline]
1738 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1739 text_area_op::move_to_line_end(self, extend_selection)
1740 }
1741
1742 #[inline]
1744 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1745 text_area_op::move_to_start(self, extend_selection)
1746 }
1747
1748 #[inline]
1750 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1751 text_area_op::move_to_end(self, extend_selection)
1752 }
1753
1754 #[inline]
1756 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1757 text_area_op::move_to_screen_start(self, extend_selection)
1758 }
1759
1760 #[inline]
1762 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1763 text_area_op::move_to_screen_end(self, extend_selection)
1764 }
1765
1766 #[inline]
1768 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1769 text_area_op::move_to_next_word(self, extend_selection)
1770 }
1771
1772 #[inline]
1774 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1775 text_area_op::move_to_prev_word(self, extend_selection)
1776 }
1777}
1778
1779impl HasScreenCursor for TextAreaState {
1780 #[allow(clippy::question_mark)]
1782 fn screen_cursor(&self) -> Option<(u16, u16)> {
1783 if self.is_focused() {
1784 if self.has_selection() {
1785 None
1786 } else {
1787 let Some(scr_cursor) = self.screen_cursor else {
1788 return None;
1789 };
1790
1791 if !(scr_cursor.0 >= self.inner.x
1792 && scr_cursor.0 <= self.inner.right()
1793 && scr_cursor.1 >= self.inner.y
1794 && scr_cursor.1 < self.inner.bottom())
1795 {
1796 return None;
1797 }
1798 Some(scr_cursor)
1799 }
1800 } else {
1801 None
1802 }
1803 }
1804}
1805
1806impl RelocatableState for TextAreaState {
1807 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1808 self.area.relocate(shift, clip);
1810 self.inner.relocate(shift, clip);
1811 self.hscroll.relocate(shift, clip);
1812 self.vscroll.relocate(shift, clip);
1813 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1814 if let Some(screen_cursor) = self.screen_cursor {
1815 self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
1816 }
1817 }
1818}
1819
1820impl TextAreaState {
1821 fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
1823 match self.text_wrap {
1824 TextWrap::Shift => (
1825 TextWrap2::Shift,
1826 shift_left,
1827 shift_left + self.rendered.width as upos_type,
1828 shift_left + self.rendered.width as upos_type,
1829 ),
1830 TextWrap::Hard => (
1831 TextWrap2::Hard,
1832 0,
1833 self.rendered.width as upos_type,
1834 self.rendered.width as upos_type,
1835 ),
1836 TextWrap::Word(margin) => (
1837 TextWrap2::Word,
1838 0,
1839 self.rendered.width as upos_type,
1840 self.rendered.width.saturating_sub(margin) as upos_type,
1841 ),
1842 }
1843 }
1844
1845 fn fill_cache(
1848 &self,
1849 shift_left: upos_type,
1850 sub_row_offset: upos_type,
1851 rows: Range<upos_type>,
1852 ) -> Result<(), TextError> {
1853 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1854 self.value.fill_cache(
1855 self.rendered,
1856 sub_row_offset,
1857 rows,
1858 self.tab_width,
1859 text_wrap,
1860 self.wrap_ctrl() | self.show_ctrl(),
1861 left_margin,
1862 right_margin,
1863 word_margin,
1864 )
1865 }
1866
1867 fn glyphs2(
1869 &self,
1870 shift_left: upos_type,
1871 sub_row_offset: upos_type,
1872 rows: Range<upos_type>,
1873 ) -> Result<GlyphIter2<'_, <TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
1874 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1875 self.value.glyphs2(
1876 self.rendered,
1877 sub_row_offset,
1878 rows,
1879 self.tab_width,
1880 text_wrap,
1881 self.wrap_ctrl() | self.show_ctrl(),
1882 left_margin,
1883 right_margin,
1884 word_margin,
1885 )
1886 }
1887
1888 fn stc_fill_screen_cache(&self, scr: (upos_type, upos_type, upos_type)) {
1892 let y2 = scr.1 + scr.2;
1893 self.fill_cache(0, scr.0, scr.1..min(y2, self.len_lines()))
1894 .expect("valid-rows");
1895 }
1896
1897 fn stc_screen_row(
1904 &self,
1905 scr: (upos_type, upos_type, upos_type),
1906 pos: TextPosition,
1907 ) -> Option<upos_type> {
1908 if pos < TextPosition::new(scr.0, scr.1) {
1909 return None;
1910 }
1911
1912 let line_breaks = self.value.cache().line_break.borrow();
1913 let range_start = TextPosition::new(scr.0, scr.1);
1914 let y2 = scr.1 + scr.2;
1915 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
1916
1917 let mut start_pos = TextPosition::new(scr.0, scr.1);
1918 let mut scr_row = 0;
1919 for (_key, cache) in line_breaks.range(range_start..range_end) {
1920 if pos < cache.start_pos {
1921 return Some(scr_row);
1922 }
1923 scr_row += 1;
1924 start_pos = cache.start_pos;
1925 }
1926
1927 if pos == start_pos {
1929 return Some(scr_row);
1930 }
1931
1932 None
1933 }
1934
1935 fn stc_sub_row_offset(
1942 &self,
1943 scr: (upos_type, upos_type, upos_type),
1944 mut scr_row: upos_type,
1945 ) -> (upos_type, upos_type) {
1946 let line_breaks = self.value.cache().line_break.borrow();
1947 let range_start = TextPosition::new(scr.0, scr.1);
1948 let y2 = scr.1 + scr.2;
1949 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
1950
1951 let mut start_pos = (scr.0, scr.1);
1952 for (_key, cache) in line_breaks.range(range_start..range_end) {
1953 if scr_row == 0 {
1954 return start_pos;
1955 }
1956 scr_row -= 1;
1957 start_pos = (cache.start_pos.x, cache.start_pos.y);
1958 }
1959
1960 start_pos
1962 }
1963}
1964
1965impl TextAreaState {
1966 #[allow(clippy::needless_return)]
1968 pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
1969 let scr_pos = (
1970 scr_pos.0 + self.dark_offset.0 as i16,
1971 scr_pos.1 + self.dark_offset.1 as i16,
1972 );
1973
1974 match self.text_wrap {
1975 TextWrap::Shift => {
1976 let (ox, _, oy) = self.clean_offset();
1977
1978 if oy >= self.len_lines() {
1979 return None;
1980 }
1981
1982 if scr_pos.1 < 0 {
1983 return Some(TextPosition::new(
1985 0,
1986 oy.saturating_add_signed(scr_pos.1 as ipos_type),
1987 ));
1988 } else if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
1989 return Some(TextPosition::new(0, self.len_lines().saturating_sub(1)));
1991 }
1992
1993 let pos_y = oy + scr_pos.1 as upos_type;
1994
1995 if scr_pos.0 < 0 {
1996 return Some(TextPosition::new(
1997 ox.saturating_add_signed(scr_pos.0 as ipos_type),
1998 pos_y,
1999 ));
2000 } else if scr_pos.0 as u16 >= self.rendered.width {
2001 return Some(TextPosition::new(
2002 min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
2003 pos_y,
2004 ));
2005 } else {
2006 let mut start_pos = TextPosition::new(0, pos_y);
2007 for g in self
2008 .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
2009 .expect("valid-position")
2010 {
2011 if g.contains_screen_x(scr_pos.0 as u16) {
2012 return Some(TextPosition::new(g.pos().x, pos_y));
2013 }
2014 start_pos = g.pos();
2015 }
2016 Some(start_pos)
2017 }
2018 }
2019 TextWrap::Hard | TextWrap::Word(_) => {
2020 let (_, sub_row_offset, oy) = self.clean_offset();
2021
2022 if oy >= self.len_lines() {
2023 return None;
2024 }
2025
2026 if scr_pos.1 < 0 {
2027 let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2036
2037 self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2038
2039 let n_start_pos = 'f: {
2040 let line_break = self.value.cache().line_break.borrow();
2041 let start_range = TextPosition::new(0, ry);
2042 let end_range = TextPosition::new(sub_row_offset, oy);
2043
2044 let mut nrows = scr_pos.1.unsigned_abs();
2045 for (_break_pos, cache) in line_break.range(start_range..end_range).rev() {
2046 if nrows == 0 {
2047 break 'f cache.start_pos;
2048 }
2049 nrows -= 1;
2050 }
2051 TextPosition::new(0, ry)
2052 };
2053
2054 if scr_pos.0 < 0 {
2056 return Some(n_start_pos);
2057 }
2058
2059 let min_row = n_start_pos.y;
2060 let max_row = min(n_start_pos.y + 1, self.len_lines());
2061 for g in self
2062 .glyphs2(0, n_start_pos.x, min_row..max_row)
2063 .expect("valid-rows")
2064 {
2065 if g.contains_screen_x(scr_pos.0 as u16) {
2066 return Some(g.pos());
2067 }
2068 }
2069
2070 return Some(n_start_pos);
2072 } else {
2073 let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2074
2075 let n_start_pos = if scr_pos.1 == 0 {
2077 TextPosition::new(sub_row_offset, oy)
2078 } else {
2079 self.fill_cache(
2081 0,
2082 sub_row_offset,
2083 oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2084 )
2085 .expect("valid-rows");
2086
2087 'f: {
2088 let text_range = self.value.cache().line_break.borrow();
2089 let start_range = TextPosition::new(sub_row_offset, oy);
2090 let end_range = TextPosition::new(0, self.len_lines());
2091
2092 let mut nrows = scr_pos.1 - 1;
2093 let mut start_pos = TextPosition::new(sub_row_offset, oy);
2094 for (_break_pos, cache) in text_range.range(start_range..end_range) {
2095 if nrows == 0 {
2096 break 'f cache.start_pos;
2097 }
2098 start_pos = cache.start_pos;
2099 nrows -= 1;
2100 }
2101 start_pos
2102 }
2103 };
2104
2105 let min_row = n_start_pos.y;
2106 let max_row = min(n_start_pos.y + 1, self.len_lines());
2107 for g in self
2108 .glyphs2(0, n_start_pos.x, min_row..max_row)
2109 .expect("valid-rows")
2110 {
2111 if g.contains_screen_x(scr_pos.0) {
2112 return Some(g.pos());
2113 }
2114 }
2115
2116 return Some(n_start_pos);
2118 }
2119 }
2120 }
2121 }
2122
2123 pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
2125 let scr_pos = (
2126 scr_pos.0 as i16 - self.inner.x as i16,
2127 scr_pos.1 as i16 - self.inner.y as i16,
2128 );
2129 self.relative_screen_to_pos(scr_pos)
2130 }
2131
2132 #[allow(clippy::explicit_counter_loop)]
2142 pub fn pos_to_relative_screen(&self, pos: impl Into<TextPosition>) -> Option<(i16, i16)> {
2143 let pos = pos.into();
2144 match self.text_wrap {
2145 TextWrap::Shift => {
2146 let (ox, _, oy) = self.clean_offset();
2147
2148 if oy > self.len_lines() {
2149 return None;
2150 }
2151 if pos.y < oy {
2152 return None;
2153 }
2154 if pos.y > self.len_lines() {
2155 return None;
2156 }
2157 if pos.y - oy >= self.rendered.height as u32 {
2158 return None;
2159 }
2160
2161 let screen_y = (pos.y - oy) as u16;
2162
2163 let screen_x = 'f: {
2164 for g in self
2165 .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2166 .expect("valid-row")
2167 {
2168 if g.pos().x == pos.x {
2169 break 'f g.screen_pos().0;
2170 } else if g.line_break() {
2171 break 'f g.screen_pos().0;
2172 }
2173 }
2174 0
2176 };
2177 assert!(screen_x <= self.rendered.width);
2178
2179 Some((
2180 screen_x as i16 - self.dark_offset.0 as i16,
2181 screen_y as i16 - self.dark_offset.1 as i16,
2182 ))
2183 }
2184 TextWrap::Hard | TextWrap::Word(_) => {
2185 let (_, sub_row_offset, oy) = self.clean_offset();
2186
2187 if oy > self.len_lines() {
2188 return None;
2189 }
2190 if pos.y < oy {
2191 return None;
2192 }
2193 if pos.y > self.len_lines() {
2194 return None;
2195 }
2196
2197 let page = self.rendered.height as upos_type;
2198 let scr = (sub_row_offset, oy, page);
2199 self.stc_fill_screen_cache(scr);
2200 let (screen_y, start_pos) = if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2201 if pos_row >= page {
2202 return None;
2204 }
2205 let start_pos = self.stc_sub_row_offset(scr, pos_row);
2206 (pos_row, start_pos)
2207 } else {
2208 return None;
2210 };
2211
2212 let screen_x = 'f: {
2213 for g in self
2214 .glyphs2(
2215 0,
2216 start_pos.0,
2217 start_pos.1..min(start_pos.1 + 1, self.len_lines()),
2218 )
2219 .expect("valid-row")
2220 {
2221 if g.pos().x == pos.x {
2222 break 'f g.screen_pos().0;
2223 } else if g.line_break() {
2224 break 'f g.screen_pos().0;
2225 }
2226 }
2227 0
2229 };
2230 assert!(screen_x <= self.rendered.width);
2231
2232 let scr = (
2233 screen_x as i16 - self.dark_offset.0 as i16,
2234 screen_y as i16 - self.dark_offset.1 as i16,
2235 );
2236 Some(scr)
2237 }
2238 }
2239 }
2240
2241 #[inline]
2244 pub fn pos_to_screen(&self, pos: impl Into<TextPosition>) -> Option<(u16, u16)> {
2245 let scr_pos = self.pos_to_relative_screen(pos.into())?;
2246 if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
2247 Some((
2248 (scr_pos.0 + self.inner.x as i16) as u16,
2249 (scr_pos.1 + self.inner.y as i16) as u16,
2250 ))
2251 } else {
2252 None
2253 }
2254 }
2255
2256 pub fn pos_to_line_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
2258 let pos = pos.into();
2259 match self.text_wrap {
2260 TextWrap::Shift => {
2261 TextPosition::new(0, pos.y)
2263 }
2264 TextWrap::Hard | TextWrap::Word(_) => {
2265 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2266 .expect("valid-row");
2267
2268 let mut start_pos = TextPosition::new(0, pos.y);
2269 for (break_pos, _) in self.value.cache().line_break.borrow().range(
2270 TextPosition::new(0, pos.y)
2271 ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
2272 ) {
2273 if pos >= start_pos && &pos <= break_pos {
2274 break;
2275 }
2276 start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2277 }
2278
2279 start_pos
2280 }
2281 }
2282 }
2283
2284 pub fn pos_to_line_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
2286 let pos = pos.into();
2287
2288 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2289 .expect("valid-row");
2290
2291 let mut end_pos = TextPosition::new(0, pos.y);
2292 for (break_pos, _) in self
2293 .value
2294 .cache()
2295 .line_break
2296 .borrow()
2297 .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2298 {
2299 if pos >= end_pos && &pos <= break_pos {
2300 end_pos = *break_pos;
2301 break;
2302 }
2303 end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2304 }
2305
2306 end_pos
2307 }
2308
2309 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2315 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2316 return false;
2317 };
2318 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2319 self.set_move_col(Some(scr_cursor.0));
2320 }
2321 self.set_cursor(cursor, extend_selection)
2322 }
2323
2324 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2331 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2332 return false;
2333 };
2334
2335 let anchor = self.anchor();
2336 let cursor = if cursor < anchor {
2337 self.word_start(cursor)
2338 } else {
2339 self.word_end(cursor)
2340 };
2341
2342 if !self.is_word_boundary(anchor) {
2344 if cursor < anchor {
2345 self.set_cursor(self.word_end(anchor), false);
2346 } else {
2347 self.set_cursor(self.word_start(anchor), false);
2348 }
2349 }
2350
2351 self.set_cursor(cursor, extend_selection)
2352 }
2353}
2354
2355impl TextAreaState {
2356 pub fn vertical_max_offset(&self) -> upos_type {
2361 self.vscroll.max_offset() as upos_type
2362 }
2363
2364 pub fn vertical_offset(&self) -> upos_type {
2366 self.vscroll.offset() as upos_type
2367 }
2368
2369 pub fn vertical_page(&self) -> upos_type {
2371 self.vscroll.page_len() as upos_type
2372 }
2373
2374 pub fn vertical_scroll(&self) -> upos_type {
2376 self.vscroll.scroll_by() as upos_type
2377 }
2378
2379 pub fn horizontal_max_offset(&self) -> upos_type {
2388 self.hscroll.max_offset() as upos_type
2389 }
2390
2391 pub fn horizontal_offset(&self) -> upos_type {
2393 self.hscroll.offset() as upos_type
2394 }
2395
2396 pub fn horizontal_page(&self) -> upos_type {
2398 self.hscroll.page_len() as upos_type
2399 }
2400
2401 pub fn horizontal_scroll(&self) -> upos_type {
2403 self.hscroll.scroll_by() as upos_type
2404 }
2405
2406 pub fn set_vertical_offset(&mut self, row_offset: upos_type) -> bool {
2413 self.scroll_to_cursor.set(false);
2414 self.sub_row_offset = 0;
2415 self.vscroll.set_offset(row_offset as usize)
2416 }
2417
2418 pub fn set_horizontal_offset(&mut self, col_offset: upos_type) -> bool {
2427 self.scroll_to_cursor.set(false);
2428 self.hscroll.set_offset(col_offset as usize)
2429 }
2430
2431 pub fn scroll_cursor_to_visible(&mut self) {
2437 self.scroll_to_cursor.set(true);
2438 }
2439
2440 pub fn scroll_to_pos(&mut self, pos: impl Into<TextPosition>) -> bool {
2446 let old_offset = self.clean_offset();
2447
2448 let pos = pos.into();
2449
2450 'f: {
2451 match self.text_wrap {
2452 TextWrap::Shift => {
2453 let (ox, _, oy) = old_offset;
2454
2455 let height = self.rendered.height as upos_type;
2456 let width = self.rendered.width as upos_type;
2457 let width = if self.show_ctrl() || self.wrap_ctrl() {
2458 width.saturating_sub(1)
2459 } else {
2460 width
2461 };
2462
2463 let noy = if pos.y < oy.saturating_sub(height) {
2464 pos.y.saturating_sub(height * 4 / 10)
2465 } else if pos.y < oy {
2466 pos.y
2467 } else if pos.y >= oy + 2 * height {
2468 pos.y.saturating_sub(height * 6 / 10)
2469 } else if pos.y >= oy + height {
2470 pos.y.saturating_sub(height.saturating_sub(1))
2471 } else {
2472 oy
2473 };
2474
2475 let nox = if pos.x < ox {
2476 pos.x
2477 } else if pos.x >= ox + width {
2478 pos.x.saturating_sub(width) + 1
2479 } else {
2480 ox
2481 };
2482
2483 self.set_offset((nox, noy));
2484 self.set_sub_row_offset(0);
2485 }
2486 TextWrap::Hard | TextWrap::Word(_) => {
2487 let (_ox, sub_row_offset, oy) = old_offset;
2488 let page = self.rendered.height as upos_type;
2489
2490 let scr = (0, oy.saturating_sub(page), 3 * page);
2492 self.stc_fill_screen_cache(scr);
2493 if let Some(off_row) =
2494 self.stc_screen_row(scr, TextPosition::new(sub_row_offset, oy))
2495 {
2496 if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2497 if pos_row < off_row && pos_row >= off_row.saturating_sub(page) {
2498 let noff_row = pos_row;
2499 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2500 self.set_offset((0, noy));
2501 self.set_sub_row_offset(nsub_row_offset);
2502 break 'f;
2503 } else if pos_row >= off_row + page && pos_row < off_row + 2 * page {
2504 let noff_row = pos_row.saturating_sub(page.saturating_sub(1));
2505 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2506 self.set_offset((0, noy));
2507 self.set_sub_row_offset(nsub_row_offset);
2508 break 'f;
2509 } else if pos_row >= off_row && pos_row < off_row + page {
2510 break 'f;
2511 }
2512 }
2513 }
2514
2515 let alt_scr = (0, pos.y.saturating_sub(page), 3 * page);
2517 self.stc_fill_screen_cache(alt_scr);
2518 if let Some(alt_scr_row) = self.stc_screen_row(alt_scr, pos) {
2519 let noff_row = alt_scr_row.saturating_sub(page * 5 / 10);
2520 let (nsub_row_offset, noy) = self.stc_sub_row_offset(alt_scr, noff_row);
2521 self.set_offset((0, noy));
2522 self.set_sub_row_offset(nsub_row_offset);
2523 } else {
2524 self.set_offset((0, pos.y));
2525 self.set_sub_row_offset(0);
2526 }
2527 }
2528 }
2529 }
2530
2531 old_offset != self.clean_offset()
2532 }
2533
2534 pub fn scroll_to_row(&mut self, pos: upos_type) -> bool {
2543 self.scroll_to_cursor.set(false);
2544
2545 match self.text_wrap {
2546 TextWrap::Shift => self.vscroll.scroll_to_pos(pos as usize),
2547 TextWrap::Hard | TextWrap::Word(_) => self
2548 .vscroll
2549 .set_offset(self.vscroll.limited_offset(pos as usize)),
2550 }
2551 }
2552
2553 pub fn scroll_to_col(&mut self, pos: upos_type) -> bool {
2561 self.scroll_to_cursor.set(false);
2562 self.hscroll.set_offset(pos as usize)
2563 }
2564
2565 pub fn scroll_up(&mut self, delta: upos_type) -> bool {
2571 if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2572 self.sub_row_offset = pos.x;
2573 self.vscroll.set_offset(pos.y as usize);
2574 true
2575 } else {
2576 false
2577 }
2578 }
2579
2580 pub fn scroll_down(&mut self, delta: upos_type) -> bool {
2586 if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2587 self.sub_row_offset = pos.x;
2588 self.vscroll.set_offset(pos.y as usize);
2589 true
2590 } else {
2591 false
2592 }
2593 }
2594
2595 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
2607 self.hscroll
2608 .set_offset(self.hscroll.offset.saturating_add(delta as usize))
2609 }
2610
2611 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
2623 self.hscroll
2624 .set_offset(self.hscroll.offset.saturating_sub(delta as usize))
2625 }
2626}
2627
2628impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2629 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2630 fn tc(r: bool) -> TextOutcome {
2632 if r {
2633 TextOutcome::TextChanged
2634 } else {
2635 TextOutcome::Unchanged
2636 }
2637 }
2638
2639 let mut r =
2640 if self.is_focused() {
2641 match event {
2642 ct_event!(key press c)
2643 | ct_event!(key press SHIFT-c)
2644 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2645 ct_event!(keycode press Tab) => {
2646 tc(if !self.focus.gained() {
2648 self.insert_tab()
2649 } else {
2650 false
2651 })
2652 }
2653 ct_event!(keycode press SHIFT-BackTab) => {
2654 tc(if !self.focus.gained() {
2656 self.insert_backtab()
2657 } else {
2658 false
2659 })
2660 }
2661 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2662 ct_event!(keycode press Backspace)
2663 | ct_event!(keycode press SHIFT-Backspace) => tc(self.delete_prev_char()),
2664 ct_event!(keycode press Delete) | ct_event!(keycode press SHIFT-Delete) => {
2665 tc(self.delete_next_char())
2666 }
2667 ct_event!(keycode press CONTROL-Backspace)
2668 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2669 ct_event!(keycode press CONTROL-Delete)
2670 | ct_event!(keycode press ALT-Delete) => tc(self.delete_next_word()),
2671 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2672 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2673 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2674 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2675 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2676 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2677
2678 ct_event!(key release _)
2679 | ct_event!(key release SHIFT-_)
2680 | ct_event!(key release CONTROL_ALT-_)
2681 | ct_event!(keycode release Tab)
2682 | ct_event!(keycode release Enter)
2683 | ct_event!(keycode release Backspace)
2684 | ct_event!(keycode release Delete)
2685 | ct_event!(keycode release CONTROL-Backspace)
2686 | ct_event!(keycode release ALT-Backspace)
2687 | ct_event!(keycode release CONTROL-Delete)
2688 | ct_event!(key release CONTROL-'x')
2689 | ct_event!(key release CONTROL-'v')
2690 | ct_event!(key release CONTROL-'d')
2691 | ct_event!(key release CONTROL-'y')
2692 | ct_event!(key release CONTROL-'z')
2693 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2694 _ => TextOutcome::Continue,
2695 }
2696 } else {
2697 TextOutcome::Continue
2698 };
2699 if r == TextOutcome::Continue {
2700 r = self.handle(event, ReadOnly);
2701 }
2702 r
2703 }
2704}
2705
2706pub const MATCH_STYLE: usize = 100_001;
2708
2709impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
2710 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2711 let mut r = if self.is_focused() {
2712 match event {
2713 ct_event!(keycode press Left) => self.move_left(1, false).into(),
2714 ct_event!(keycode press Right) => self.move_right(1, false).into(),
2715 ct_event!(keycode press Up) => self.move_up(1, false).into(),
2716 ct_event!(keycode press Down) => self.move_down(1, false).into(),
2717 ct_event!(keycode press PageUp) => {
2718 self.move_up(self.vertical_page() as u16, false).into()
2719 }
2720 ct_event!(keycode press PageDown) => {
2721 self.move_down(self.vertical_page() as u16, false).into()
2722 }
2723 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
2724 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
2725 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
2726 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
2727 ct_event!(keycode press CONTROL-Up) => false.into(),
2728 ct_event!(keycode press CONTROL-Down) => false.into(),
2729 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
2730 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
2731 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
2732 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
2733
2734 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
2735 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
2736 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
2737 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
2738 ct_event!(keycode press ALT-PageUp) => {
2739 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
2740 }
2741 ct_event!(keycode press ALT-PageDown) => {
2742 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
2743 }
2744 ct_event!(keycode press ALT_SHIFT-PageUp) => {
2745 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
2746 }
2747 ct_event!(keycode press ALT_SHIFT-PageDown) => {
2748 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
2749 }
2750
2751 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
2752 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
2753 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
2754 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
2755 ct_event!(keycode press SHIFT-PageUp) => {
2756 self.move_up(self.vertical_page() as u16, true).into()
2757 }
2758 ct_event!(keycode press SHIFT-PageDown) => {
2759 self.move_down(self.vertical_page() as u16, true).into()
2760 }
2761 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
2762 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
2763 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
2764 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
2765 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
2766 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
2767 ct_event!(key press CONTROL-'a') => self.select_all().into(),
2768 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
2769
2770 ct_event!(keycode press F(3)) => self.move_to_next_match().into(),
2771 ct_event!(keycode press SHIFT-F(3)) => self.move_to_prev_match().into(),
2772
2773 ct_event!(keycode release Left)
2774 | ct_event!(keycode release Right)
2775 | ct_event!(keycode release Up)
2776 | ct_event!(keycode release Down)
2777 | ct_event!(keycode release PageUp)
2778 | ct_event!(keycode release PageDown)
2779 | ct_event!(keycode release Home)
2780 | ct_event!(keycode release End)
2781 | ct_event!(keycode release CONTROL-Left)
2782 | ct_event!(keycode release CONTROL-Right)
2783 | ct_event!(keycode release CONTROL-Up)
2784 | ct_event!(keycode release CONTROL-Down)
2785 | ct_event!(keycode release CONTROL-PageUp)
2786 | ct_event!(keycode release CONTROL-PageDown)
2787 | ct_event!(keycode release CONTROL-Home)
2788 | ct_event!(keycode release CONTROL-End)
2789 | ct_event!(keycode release ALT-Left)
2790 | ct_event!(keycode release ALT-Right)
2791 | ct_event!(keycode release ALT-Up)
2792 | ct_event!(keycode release ALT-Down)
2793 | ct_event!(keycode release ALT-PageUp)
2794 | ct_event!(keycode release ALT-PageDown)
2795 | ct_event!(keycode release ALT_SHIFT-PageUp)
2796 | ct_event!(keycode release ALT_SHIFT-PageDown)
2797 | ct_event!(keycode release SHIFT-Left)
2798 | ct_event!(keycode release SHIFT-Right)
2799 | ct_event!(keycode release SHIFT-Up)
2800 | ct_event!(keycode release SHIFT-Down)
2801 | ct_event!(keycode release SHIFT-PageUp)
2802 | ct_event!(keycode release SHIFT-PageDown)
2803 | ct_event!(keycode release SHIFT-Home)
2804 | ct_event!(keycode release SHIFT-End)
2805 | ct_event!(keycode release CONTROL_SHIFT-Left)
2806 | ct_event!(keycode release CONTROL_SHIFT-Right)
2807 | ct_event!(keycode release CONTROL_SHIFT-Home)
2808 | ct_event!(keycode release CONTROL_SHIFT-End)
2809 | ct_event!(key release CONTROL-'a')
2810 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2811 _ => TextOutcome::Continue,
2812 }
2813 } else {
2814 TextOutcome::Continue
2815 };
2816
2817 if r == TextOutcome::Continue {
2818 r = self.handle(event, MouseOnly);
2819 }
2820 r
2821 }
2822}
2823
2824impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
2825 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
2826 flow!(match event {
2827 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2828 let cx = m.column as i16 - self.inner.x as i16;
2829 let cy = m.row as i16 - self.inner.y as i16;
2830 self.set_screen_cursor((cx, cy), true).into()
2831 }
2832 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2833 let cx = m.column as i16 - self.inner.x as i16;
2834 let cy = m.row as i16 - self.inner.y as i16;
2835 self.set_screen_cursor_words((cx, cy), true).into()
2836 }
2837 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2838 if let Some(test) = self.screen_to_pos((m.column, m.row)) {
2839 let start = self.word_start(test);
2840 let end = self.word_end(test);
2841 self.set_selection(start, end).into()
2842 } else {
2843 TextOutcome::Unchanged
2844 }
2845 }
2846 ct_event!(mouse down Left for column,row) => {
2847 if self.inner.contains((*column, *row).into()) {
2848 let cx = (column - self.inner.x) as i16;
2849 let cy = (row - self.inner.y) as i16;
2850 self.set_screen_cursor((cx, cy), false).into()
2851 } else {
2852 TextOutcome::Continue
2853 }
2854 }
2855 ct_event!(mouse down SHIFT-Left for column,row) => {
2856 if self.inner.contains((*column, *row).into()) {
2857 let cx = (column - self.inner.x) as i16;
2858 let cy = (row - self.inner.y) as i16;
2859 self.set_screen_cursor((cx, cy), true).into()
2860 } else {
2861 TextOutcome::Continue
2862 }
2863 }
2864 ct_event!(mouse down CONTROL-Left for column,row) => {
2865 if self.inner.contains((*column, *row).into()) {
2866 let cx = (column - self.inner.x) as i16;
2867 let cy = (row - self.inner.y) as i16;
2868 self.set_screen_cursor((cx, cy), true).into()
2869 } else {
2870 TextOutcome::Continue
2871 }
2872 }
2873 ct_event!(mouse down ALT-Left for column,row) => {
2874 if self.inner.contains((*column, *row).into()) {
2875 let cx = (column - self.inner.x) as i16;
2876 let cy = (row - self.inner.y) as i16;
2877 self.set_screen_cursor_words((cx, cy), true).into()
2878 } else {
2879 TextOutcome::Continue
2880 }
2881 }
2882 _ => TextOutcome::Continue,
2883 });
2884
2885 let mut sas = ScrollAreaState::new()
2886 .area(self.inner)
2887 .h_scroll(&mut self.hscroll)
2888 .v_scroll(&mut self.vscroll);
2889 let r = match sas.handle(event, MouseOnly) {
2890 ScrollOutcome::Up(v) => self.scroll_up(v as upos_type),
2891 ScrollOutcome::Down(v) => self.scroll_down(v as upos_type),
2892 ScrollOutcome::Left(v) => self.scroll_left(v as upos_type),
2893 ScrollOutcome::Right(v) => self.scroll_right(v as upos_type),
2894 ScrollOutcome::VPos(v) => self.scroll_to_row(v as upos_type),
2895 ScrollOutcome::HPos(v) => self.scroll_to_col(v as upos_type),
2896 _ => false,
2897 };
2898 if r {
2899 return TextOutcome::Changed;
2900 }
2901
2902 TextOutcome::Continue
2903 }
2904}
2905
2906pub fn handle_events(
2910 state: &mut TextAreaState,
2911 focus: bool,
2912 event: &crossterm::event::Event,
2913) -> TextOutcome {
2914 state.focus.set(focus);
2915 state.handle(event, Regular)
2916}
2917
2918pub fn handle_readonly_events(
2922 state: &mut TextAreaState,
2923 focus: bool,
2924 event: &crossterm::event::Event,
2925) -> TextOutcome {
2926 state.focus.set(focus);
2927 state.handle(event, ReadOnly)
2928}
2929
2930pub fn handle_mouse_events(
2932 state: &mut TextAreaState,
2933 event: &crossterm::event::Event,
2934) -> TextOutcome {
2935 state.handle(event, MouseOnly)
2936}