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 style: Style,
79 block: Option<Block<'a>>,
80 hscroll: Option<Scroll<'a>>,
81 h_max_offset: Option<upos_type>,
82 h_overscroll: Option<upos_type>,
83 vscroll: Option<Scroll<'a>>,
84
85 focus_style: Option<Style>,
86 select_style: Option<Style>,
87 text_style: HashMap<usize, Style>,
88
89 text_wrap: Option<TextWrap>,
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.block.is_some() {
241 self.block = styles.block;
242 }
243 if let Some(border_style) = styles.border_style {
244 self.block = self.block.map(|v| v.border_style(border_style));
245 }
246 if let Some(title_style) = styles.title_style {
247 self.block = self.block.map(|v| v.title_style(title_style));
248 }
249 self.block = self.block.map(|v| v.style(self.style));
250
251 if let Some(styles) = styles.scroll {
252 self.hscroll = self.hscroll.map(|v| v.styles(styles.clone()));
253 self.vscroll = self.vscroll.map(|v| v.styles(styles));
254 }
255 if styles.focus.is_some() {
256 self.focus_style = styles.focus;
257 }
258 if styles.select.is_some() {
259 self.select_style = styles.select;
260 }
261
262 self
263 }
264
265 pub fn style(mut self, style: Style) -> Self {
267 self.style = style;
268 self.block = self.block.map(|v| v.style(style));
269 self
270 }
271
272 pub fn focus_style(mut self, style: Style) -> Self {
274 self.focus_style = Some(style);
275 self.block = self.block.map(|v| v.style(self.style));
276 self
277 }
278
279 pub fn select_style(mut self, style: Style) -> Self {
281 self.select_style = Some(style);
282 self
283 }
284
285 pub fn text_style_idx(mut self, idx: usize, style: Style) -> Self {
290 self.text_style.insert(idx, style);
291 self
292 }
293
294 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
299 for (i, s) in styles.into_iter().enumerate() {
300 self.text_style.insert(i, s);
301 }
302 self
303 }
304
305 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
310 for (i, s) in styles.into_iter() {
311 self.text_style.insert(i, s.into());
312 }
313 self
314 }
315
316 #[inline]
318 pub fn block(mut self, block: Block<'a>) -> Self {
319 self.block = Some(block);
320 self
321 }
322
323 pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
325 self.hscroll = Some(scroll.clone().override_horizontal());
326 self.vscroll = Some(scroll.override_vertical());
327 self
328 }
329
330 pub fn hscroll(mut self, scroll: Scroll<'a>) -> Self {
332 self.hscroll = Some(scroll.override_horizontal());
333 self
334 }
335
336 pub fn text_wrap(mut self, wrap: TextWrap) -> Self {
338 self.text_wrap = Some(wrap);
339 self
340 }
341
342 pub fn set_horizontal_max_offset(mut self, offset: u32) -> Self {
359 self.h_max_offset = Some(offset);
360 self
361 }
362
363 pub fn set_horizontal_overscroll(mut self, overscroll: u32) -> Self {
370 self.h_overscroll = Some(overscroll);
371 self
372 }
373
374 pub fn vscroll(mut self, scroll: Scroll<'a>) -> Self {
376 self.vscroll = Some(scroll.override_vertical());
377 self
378 }
379
380 pub fn width(&self) -> u16 {
382 0
383 }
384
385 pub fn height(&self) -> u16 {
387 1
388 }
389}
390
391impl<'a> StatefulWidget for &TextArea<'a> {
392 type State = TextAreaState;
393
394 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
395 render_text_area(self, area, buf, state);
396 }
397}
398
399impl StatefulWidget for TextArea<'_> {
400 type State = TextAreaState;
401
402 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
403 render_text_area(&self, area, buf, state);
404 }
405}
406
407fn render_text_area(
408 widget: &TextArea<'_>,
409 area: Rect,
410 buf: &mut Buffer,
411 state: &mut TextAreaState,
412) {
413 state.area = area;
414 state.screen_cursor = None;
415 if let Some(text_wrap) = widget.text_wrap {
416 state.text_wrap = text_wrap;
417 }
418
419 let style = widget.style;
420 let focus_style = if let Some(focus_style) = widget.focus_style {
421 focus_style
422 } else {
423 style
424 };
425 let select_style = if let Some(select_style) = widget.select_style {
426 select_style
427 } else {
428 Style::default().black().on_yellow()
429 };
430 let (style, select_style) = if state.is_focused() {
431 (
432 style.patch(focus_style),
433 style.patch(focus_style).patch(select_style),
434 )
435 } else {
436 (style, style.patch(select_style))
437 };
438
439 state.area = area;
441 state.screen_cursor = None;
442 state.inner = ScrollArea::new()
443 .block(widget.block.as_ref())
444 .h_scroll(widget.hscroll.as_ref())
445 .v_scroll(widget.vscroll.as_ref())
446 .inner(area, Some(&state.hscroll), Some(&state.vscroll));
447 state.rendered = state.inner.as_size();
448
449 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
450 state.hscroll.set_max_offset(0);
451 state.hscroll.set_overscroll_by(None);
452 } else {
453 if let Some(h_max_offset) = widget.h_max_offset {
454 state.hscroll.set_max_offset(h_max_offset as usize);
455 }
456 if let Some(h_overscroll) = widget.h_overscroll {
457 state.hscroll.set_overscroll_by(Some(h_overscroll as usize));
458 }
459 }
460 state.hscroll.set_page_len(state.inner.width as usize);
461
462 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
463 state
464 .vscroll
465 .set_max_offset(state.len_lines().saturating_sub(1) as usize);
466 } else {
467 state.vscroll.set_max_offset(
468 state
469 .len_lines()
470 .saturating_sub(state.inner.height as upos_type) as usize,
471 );
472 }
473 state.vscroll.set_page_len(state.inner.height as usize);
474
475 if state.scroll_to_cursor.get() {
476 state.scroll_to_pos(state.cursor());
477 }
478
479 ScrollArea::new()
481 .block(widget.block.as_ref())
482 .h_scroll(widget.hscroll.as_ref())
483 .v_scroll(widget.vscroll.as_ref())
484 .style(style)
485 .render(
486 area,
487 buf,
488 &mut ScrollAreaState::new()
489 .h_scroll(&mut state.hscroll)
490 .v_scroll(&mut state.vscroll),
491 );
492
493 if state.inner.width == 0 || state.inner.height == 0 {
494 return;
496 }
497 if state.vscroll.offset() > state.value.len_lines() as usize {
498 return;
500 }
501
502 let (shift_left, sub_row_offset, start_row) = state.clean_offset();
503 let page_rows = start_row
504 ..min(
505 start_row + state.inner.height as upos_type,
506 state.value.len_lines(),
507 );
508 let page_bytes = state
509 .try_bytes_at_range(TextRange::new(
510 (sub_row_offset, page_rows.start),
511 (0, page_rows.end),
512 ))
513 .expect("valid_rows");
514 let selection = state.selection();
516 let mut styles = Vec::new();
517
518 for g in state
519 .glyphs2(shift_left, sub_row_offset, page_rows)
520 .expect("valid_offset")
521 {
522 let screen_pos = g.screen_pos();
524
525 if screen_pos.1 >= state.inner.height {
526 break;
527 }
528
529 if g.screen_width() > 0 {
530 let mut style = style;
531 state
533 .value
534 .styles_at_page(g.text_bytes().start, page_bytes.clone(), &mut styles);
535 for style_nr in &styles {
536 if let Some(s) = widget.text_style.get(style_nr) {
537 style = style.patch(*s);
538 }
539 }
540 if selection.contains_pos(g.pos()) {
542 style = style.patch(select_style);
543 };
544
545 if let Some(cell) =
547 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
548 {
549 cell.set_symbol(g.glyph());
550 cell.set_style(style);
551 }
552 for d in 1..g.screen_width() {
554 if let Some(cell) = buf.cell_mut((
555 state.inner.x + screen_pos.0 + d,
556 state.inner.y + screen_pos.1,
557 )) {
558 cell.reset();
559 cell.set_style(style);
560 }
561 }
562 }
563 }
564
565 state.screen_cursor = state.pos_to_screen(state.cursor());
566}
567
568impl Default for TextAreaState {
569 fn default() -> Self {
570 #[cfg(windows)]
571 const LINE_ENDING: &str = "\r\n";
572
573 #[cfg(not(windows))]
574 const LINE_ENDING: &str = "\n";
575
576 let mut s = Self {
577 area: Default::default(),
578 inner: Default::default(),
579 rendered: Default::default(),
580 screen_cursor: Default::default(),
581 hscroll: Default::default(),
582 vscroll: Default::default(),
583 sub_row_offset: 0,
584 dark_offset: Default::default(),
585 scroll_to_cursor: Default::default(),
586 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
587 move_col: Default::default(),
588 auto_indent: true,
589 auto_quote: true,
590 text_wrap: TextWrap::Shift,
591 newline: LINE_ENDING.to_string(),
592 tab_width: 8,
593 expand_tabs: true,
594 focus: Default::default(),
595 mouse: Default::default(),
596 non_exhaustive: NonExhaustive,
597 };
598 s.hscroll.set_max_offset(255);
599 s.hscroll.set_overscroll_by(Some(16384));
600 s
601 }
602}
603
604impl HasFocus for TextAreaState {
605 fn build(&self, builder: &mut FocusBuilder) {
606 builder.leaf_widget(self);
607 }
608
609 fn focus(&self) -> FocusFlag {
610 self.focus.clone()
611 }
612
613 fn area(&self) -> Rect {
614 self.area
615 }
616
617 fn navigable(&self) -> Navigation {
618 Navigation::Reach
619 }
620}
621
622impl TextAreaState {
623 #[inline]
625 pub fn new() -> Self {
626 Self::default()
627 }
628
629 #[inline]
631 pub fn named(name: &str) -> Self {
632 Self {
633 focus: FocusFlag::new().with_name(name),
634 ..Default::default()
635 }
636 }
637
638 #[inline]
644 pub fn set_newline(&mut self, br: impl Into<String>) {
645 self.newline = br.into();
646 }
647
648 #[inline]
650 pub fn newline(&self) -> &str {
651 &self.newline
652 }
653
654 #[inline]
656 pub fn set_auto_indent(&mut self, indent: bool) {
657 self.auto_indent = indent;
658 }
659
660 #[inline]
662 pub fn set_auto_quote(&mut self, quote: bool) {
663 self.auto_quote = quote;
664 }
665
666 #[inline]
668 pub fn set_tab_width(&mut self, tabs: u32) {
669 self.tab_width = tabs;
670 }
671
672 #[inline]
674 pub fn tab_width(&self) -> u32 {
675 self.tab_width
676 }
677
678 #[inline]
680 pub fn set_expand_tabs(&mut self, expand: bool) {
681 self.expand_tabs = expand;
682 }
683
684 #[inline]
686 pub fn expand_tabs(&self) -> bool {
687 self.expand_tabs
688 }
689
690 #[inline]
692 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
693 self.value.set_glyph_ctrl(show_ctrl);
694 }
695
696 pub fn show_ctrl(&self) -> bool {
698 self.value.glyph_ctrl()
699 }
700
701 #[inline]
704 pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
705 self.value.set_wrap_ctrl(wrap_ctrl);
706 }
707
708 pub fn wrap_ctrl(&self) -> bool {
711 self.value.wrap_ctrl()
712 }
713
714 pub fn set_text_wrap(&mut self, text_wrap: TextWrap) {
727 self.text_wrap = text_wrap;
728 }
729
730 pub fn text_wrap(&self) -> TextWrap {
732 self.text_wrap
733 }
734
735 #[inline]
745 pub fn set_move_col(&mut self, col: Option<i16>) {
746 self.move_col = col;
747 }
748
749 #[inline]
751 pub fn move_col(&mut self) -> Option<i16> {
752 self.move_col
753 }
754}
755
756impl TextAreaState {
757 #[inline]
760 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
761 match clip {
762 None => self.value.set_clipboard(None),
763 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
764 }
765 }
766
767 #[inline]
770 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
771 self.value.clipboard()
772 }
773
774 #[inline]
776 pub fn copy_to_clip(&mut self) -> bool {
777 let Some(clip) = self.value.clipboard() else {
778 return false;
779 };
780
781 _ = clip.set_string(self.selected_text().as_ref());
782 false
783 }
784
785 #[inline]
787 pub fn cut_to_clip(&mut self) -> bool {
788 let Some(clip) = self.value.clipboard() else {
789 return false;
790 };
791
792 match clip.set_string(self.selected_text().as_ref()) {
793 Ok(_) => self.delete_range(self.selection()),
794 Err(_) => false,
795 }
796 }
797
798 #[inline]
800 pub fn paste_from_clip(&mut self) -> bool {
801 let Some(clip) = self.value.clipboard() else {
802 return false;
803 };
804
805 if let Ok(text) = clip.get_string() {
806 self.insert_str(text)
807 } else {
808 false
809 }
810 }
811}
812
813impl TextAreaState {
814 #[inline]
821 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
822 match undo {
823 None => self.value.set_undo_buffer(None),
824 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
825 }
826 }
827
828 #[inline]
830 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
831 self.value.undo_buffer()
832 }
833
834 #[inline]
836 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
837 self.value.undo_buffer_mut()
838 }
839
840 #[inline]
846 pub fn begin_undo_seq(&mut self) {
847 self.value.begin_undo_seq()
848 }
849
850 #[inline]
852 pub fn end_undo_seq(&mut self) {
853 self.value.end_undo_seq()
854 }
855
856 #[inline]
861 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
862 self.value.recent_replay_log()
863 }
864
865 #[inline]
869 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
870 self.value.replay_log(replay)
871 }
872
873 #[inline]
875 pub fn undo(&mut self) -> bool {
876 self.value.undo()
877 }
878
879 #[inline]
881 pub fn redo(&mut self) -> bool {
882 self.value.redo()
883 }
884}
885
886impl TextAreaState {
887 #[inline]
904 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
905 self.value.set_styles(styles);
906 }
907
908 #[inline]
919 pub fn set_range_styles(&mut self, styles: Vec<(TextRange, usize)>) -> Result<(), TextError> {
920 let mut mapped = Vec::with_capacity(styles.len());
921 for (r, s) in styles {
922 let rr = self.value.bytes_at_range(r)?;
923 mapped.push((rr, s));
924 }
925 self.value.set_styles(mapped);
926 Ok(())
927 }
928
929 #[inline]
934 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
935 self.value.add_style(range, style);
936 }
937
938 #[inline]
942 pub fn add_range_style(
943 &mut self,
944 range: impl Into<TextRange>,
945 style: usize,
946 ) -> Result<(), TextError> {
947 let r = self.value.bytes_at_range(range.into())?;
948 self.value.add_style(r, style);
949 Ok(())
950 }
951
952 #[inline]
954 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
955 self.value.remove_style(range, style);
956 }
957
958 #[inline]
960 pub fn remove_style_fully(&mut self, style: usize) {
961 self.value.remove_style_fully(style);
962 }
963
964 #[inline]
966 pub fn remove_range_style(
967 &mut self,
968 range: impl Into<TextRange>,
969 style: usize,
970 ) -> Result<(), TextError> {
971 let r = self.value.bytes_at_range(range.into())?;
972 self.value.remove_style(r, style);
973 Ok(())
974 }
975
976 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
978 self.value.styles_in(range, buf)
979 }
980
981 pub fn styles_in_match(
983 &self,
984 range: Range<usize>,
985 style: usize,
986 buf: &mut Vec<(Range<usize>, usize)>,
987 ) {
988 self.value.styles_in_match(range, style, buf);
989 }
990
991 #[inline]
993 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
994 self.value.styles_at(byte_pos, buf)
995 }
996
997 #[inline]
1000 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
1001 self.value.styles_at_match(byte_pos, style)
1002 }
1003
1004 #[inline]
1006 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
1007 self.value.styles()
1008 }
1009}
1010
1011impl TextAreaState {
1012 #[inline]
1014 pub fn offset(&self) -> (upos_type, upos_type) {
1015 (
1016 self.hscroll.offset() as upos_type,
1017 self.vscroll.offset() as upos_type,
1018 )
1019 }
1020
1021 #[inline]
1026 pub fn set_offset(&mut self, offset: (upos_type, upos_type)) -> bool {
1027 self.scroll_to_cursor.set(false);
1028 let c = self.hscroll.set_offset(offset.0 as usize);
1029 let r = self.vscroll.set_offset(offset.1 as usize);
1030 r || c
1031 }
1032
1033 pub fn set_sub_row_offset(&mut self, sub_row_offset: upos_type) -> bool {
1045 self.scroll_to_cursor.set(false);
1046 let old = self.sub_row_offset;
1047 self.sub_row_offset = sub_row_offset;
1048 sub_row_offset != old
1049 }
1050
1051 pub fn sub_row_offset(&self) -> upos_type {
1056 self.sub_row_offset
1057 }
1058
1059 fn clean_offset(&self) -> (upos_type, upos_type, upos_type) {
1064 let ox = self.hscroll.offset as upos_type;
1065 let mut oy = self.vscroll.offset as upos_type;
1066
1067 if oy >= self.len_lines() {
1069 oy = 0;
1070 }
1071
1072 match self.text_wrap {
1073 TextWrap::Shift => (ox, 0, oy),
1074 TextWrap::Hard | TextWrap::Word(_) => {
1075 if let Ok(max_col) = self.try_line_width(oy) {
1077 (0, min(self.sub_row_offset, max_col), oy)
1078 } else {
1079 (0, 0, oy)
1080 }
1081 }
1082 }
1083 }
1084
1085 #[inline]
1087 pub fn cursor(&self) -> TextPosition {
1088 self.value.cursor()
1089 }
1090
1091 #[inline]
1093 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
1094 self.scroll_cursor_to_visible();
1095 self.value.set_cursor(cursor.into(), extend_selection)
1096 }
1097
1098 #[inline]
1100 pub fn anchor(&self) -> TextPosition {
1101 self.value.anchor()
1102 }
1103
1104 #[inline]
1106 pub fn has_selection(&self) -> bool {
1107 self.value.has_selection()
1108 }
1109
1110 #[inline]
1112 pub fn selection(&self) -> TextRange {
1113 self.value.selection()
1114 }
1115
1116 #[inline]
1119 pub fn set_selection(
1120 &mut self,
1121 anchor: impl Into<TextPosition>,
1122 cursor: impl Into<TextPosition>,
1123 ) -> bool {
1124 self.scroll_cursor_to_visible();
1125 self.value.set_selection(anchor.into(), cursor.into())
1126 }
1127
1128 #[inline]
1131 pub fn select_all(&mut self) -> bool {
1132 self.scroll_cursor_to_visible();
1133 self.value.select_all()
1134 }
1135
1136 #[inline]
1138 pub fn selected_text(&self) -> Cow<'_, str> {
1139 self.value
1140 .str_slice(self.value.selection())
1141 .expect("valid_selection")
1142 }
1143}
1144
1145impl TextAreaState {
1146 #[inline]
1148 pub fn clear(&mut self) -> bool {
1149 if !self.is_empty() {
1150 self.value.clear();
1151 true
1152 } else {
1153 false
1154 }
1155 }
1156
1157 #[inline]
1161 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1162 self.scroll_to_cursor.set(false);
1163 self.vscroll.set_offset(0);
1164 self.hscroll.set_offset(0);
1165 self.set_sub_row_offset(0);
1166 self.set_move_col(None);
1167
1168 self.value.set_text(TextRope::new_text(s.as_ref()));
1169 }
1170
1171 #[inline]
1173 pub fn text(&self) -> String {
1174 self.value.text().string()
1175 }
1176
1177 #[inline]
1180 pub fn set_rope(&mut self, r: Rope) {
1181 self.scroll_to_cursor.set(false);
1182 self.vscroll.set_offset(0);
1183 self.hscroll.set_offset(0);
1184 self.set_sub_row_offset(0);
1185 self.set_move_col(None);
1186
1187 self.value.set_text(TextRope::new_rope(r));
1188 }
1189
1190 #[inline]
1192 pub fn rope(&self) -> &Rope {
1193 self.value.text().rope()
1194 }
1195
1196 #[inline]
1200 pub fn set_value<S: AsRef<str>>(&mut self, s: S) {
1201 self.set_text(s);
1202 }
1203
1204 #[inline]
1206 pub fn value(&self) -> String {
1207 self.text()
1208 }
1209
1210 #[inline]
1212 pub fn is_empty(&self) -> bool {
1213 self.value.is_empty()
1214 }
1215
1216 #[inline]
1220 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1221 self.value.str_slice_byte(range).expect("valid_range")
1222 }
1223
1224 #[inline]
1226 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1227 self.value.str_slice_byte(range)
1228 }
1229
1230 #[inline]
1234 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
1235 self.value.str_slice(range.into()).expect("valid_range")
1236 }
1237
1238 #[inline]
1240 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
1241 self.value.str_slice(range.into())
1242 }
1243
1244 #[inline]
1246 pub fn len_lines(&self) -> upos_type {
1247 self.value.len_lines()
1248 }
1249
1250 #[inline]
1252 pub fn len_bytes(&self) -> usize {
1253 self.value.len_bytes()
1254 }
1255
1256 #[inline]
1260 pub fn line_width(&self, row: upos_type) -> upos_type {
1261 self.try_line_width(row).expect("valid_row")
1262 }
1263
1264 #[inline]
1266 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1267 self.value.line_width(row)
1268 }
1269
1270 #[inline]
1275 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
1276 self.value.line_at(row).expect("valid_row")
1277 }
1278
1279 #[inline]
1282 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1283 self.value.line_at(row)
1284 }
1285
1286 #[inline]
1290 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
1291 self.value.lines_at(row).expect("valid_row")
1292 }
1293
1294 #[inline]
1296 pub fn try_lines_at(
1297 &self,
1298 row: upos_type,
1299 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1300 self.value.lines_at(row)
1301 }
1302
1303 #[inline]
1308 pub fn line_graphemes(&self, row: upos_type) -> <TextRope as TextStore>::GraphemeIter<'_> {
1309 self.value.line_graphemes(row).expect("valid_row")
1310 }
1311
1312 #[inline]
1315 pub fn try_line_graphemes(
1316 &self,
1317 row: upos_type,
1318 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1319 self.value.line_graphemes(row)
1320 }
1321
1322 #[inline]
1326 pub fn text_graphemes(
1327 &self,
1328 pos: impl Into<TextPosition>,
1329 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1330 self.value.text_graphemes(pos.into()).expect("valid_pos")
1331 }
1332
1333 #[inline]
1335 pub fn try_text_graphemes(
1336 &self,
1337 pos: impl Into<TextPosition>,
1338 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1339 self.value.text_graphemes(pos.into())
1340 }
1341
1342 #[inline]
1346 pub fn graphemes(
1347 &self,
1348 range: impl Into<TextRange>,
1349 pos: impl Into<TextPosition>,
1350 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1351 self.value
1352 .graphemes(range.into(), pos.into())
1353 .expect("valid_args")
1354 }
1355
1356 #[inline]
1358 pub fn try_graphemes(
1359 &self,
1360 range: impl Into<TextRange>,
1361 pos: impl Into<TextPosition>,
1362 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1363 self.value.graphemes(range.into(), pos.into())
1364 }
1365
1366 #[inline]
1371 pub fn byte_at(&self, pos: impl Into<TextPosition>) -> Range<usize> {
1372 self.value.byte_at(pos.into()).expect("valid_pos")
1373 }
1374
1375 #[inline]
1378 pub fn try_byte_at(&self, pos: impl Into<TextPosition>) -> Result<Range<usize>, TextError> {
1379 self.value.byte_at(pos.into())
1380 }
1381
1382 #[inline]
1386 pub fn bytes_at_range(&self, range: impl Into<TextRange>) -> Range<usize> {
1387 self.value
1388 .bytes_at_range(range.into())
1389 .expect("valid_range")
1390 }
1391
1392 #[inline]
1394 pub fn try_bytes_at_range(
1395 &self,
1396 range: impl Into<TextRange>,
1397 ) -> Result<Range<usize>, TextError> {
1398 self.value.bytes_at_range(range.into())
1399 }
1400
1401 #[inline]
1406 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1407 self.value.byte_pos(byte).expect("valid_pos")
1408 }
1409
1410 #[inline]
1413 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1414 self.value.byte_pos(byte)
1415 }
1416
1417 #[inline]
1421 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1422 self.value.byte_range(bytes).expect("valid_range")
1423 }
1424
1425 #[inline]
1427 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1428 self.value.byte_range(bytes)
1429 }
1430}
1431
1432impl TextAreaState {
1433 #[inline]
1439 pub fn insert_char(&mut self, c: char) -> bool {
1440 match c {
1441 '\n' => text_area_op::insert_newline(self),
1442 '\t' => text_area_op::insert_tab(self),
1443 c => text_area_op::insert_char(self, c),
1444 }
1445 }
1446
1447 #[inline]
1453 pub fn insert_tab(&mut self) -> bool {
1454 text_area_op::insert_tab(self)
1455 }
1456
1457 #[inline]
1462 pub fn insert_backtab(&mut self) -> bool {
1463 text_area_op::insert_backtab(self)
1464 }
1465
1466 #[inline]
1469 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1470 text_area_op::insert_str(self, t.as_ref())
1471 }
1472
1473 #[inline]
1478 pub fn insert_newline(&mut self) -> bool {
1479 text_area_op::insert_newline(self)
1480 }
1481
1482 #[inline]
1486 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1487 text_area_op::delete_range(self, range.into()).expect("valid_range")
1488 }
1489
1490 #[inline]
1492 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1493 text_area_op::delete_range(self, range.into())
1494 }
1495
1496 #[inline]
1499 pub fn duplicate_text(&mut self) -> bool {
1500 text_area_op::duplicate_text(self)
1501 }
1502
1503 #[inline]
1506 pub fn delete_line(&mut self) -> bool {
1507 text_area_op::delete_line(self)
1508 }
1509
1510 #[inline]
1513 pub fn delete_next_char(&mut self) -> bool {
1514 text_area_op::delete_next_char(self)
1515 }
1516
1517 #[inline]
1520 pub fn delete_prev_char(&mut self) -> bool {
1521 text_area_op::delete_prev_char(self)
1522 }
1523
1524 #[inline]
1529 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1530 core_op::next_word_start(&self.value, pos.into()).expect("valid_pos")
1531 }
1532
1533 #[inline]
1536 pub fn try_next_word_start(
1537 &self,
1538 pos: impl Into<TextPosition>,
1539 ) -> Result<TextPosition, TextError> {
1540 core_op::next_word_start(&self.value, pos.into())
1541 }
1542
1543 #[inline]
1548 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1549 core_op::next_word_end(&self.value, pos.into()).expect("valid_pos")
1550 }
1551
1552 #[inline]
1555 pub fn try_next_word_end(
1556 &self,
1557 pos: impl Into<TextPosition>,
1558 ) -> Result<TextPosition, TextError> {
1559 core_op::next_word_end(&self.value, pos.into())
1560 }
1561
1562 #[inline]
1570 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1571 core_op::prev_word_start(&self.value, pos.into()).expect("valid_pos")
1572 }
1573
1574 #[inline]
1580 pub fn try_prev_word_start(
1581 &self,
1582 pos: impl Into<TextPosition>,
1583 ) -> Result<TextPosition, TextError> {
1584 core_op::prev_word_start(&self.value, pos.into())
1585 }
1586
1587 #[inline]
1593 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1594 core_op::prev_word_end(&self.value, pos.into()).expect("valid_pos")
1595 }
1596
1597 #[inline]
1601 pub fn try_prev_word_end(
1602 &self,
1603 pos: impl Into<TextPosition>,
1604 ) -> Result<TextPosition, TextError> {
1605 core_op::prev_word_end(&self.value, pos.into())
1606 }
1607
1608 #[inline]
1612 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1613 core_op::is_word_boundary(&self.value, pos.into()).expect("valid_pos")
1614 }
1615
1616 #[inline]
1618 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1619 core_op::is_word_boundary(&self.value, pos.into())
1620 }
1621
1622 #[inline]
1627 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1628 core_op::word_start(&self.value, pos.into()).expect("valid_pos")
1629 }
1630
1631 #[inline]
1634 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1635 core_op::word_start(&self.value, pos.into())
1636 }
1637
1638 #[inline]
1643 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1644 core_op::word_end(&self.value, pos.into()).expect("valid_pos")
1645 }
1646
1647 #[inline]
1650 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1651 core_op::word_end(&self.value, pos.into())
1652 }
1653
1654 #[inline]
1659 pub fn delete_next_word(&mut self) -> bool {
1660 text_area_op::delete_next_word(self)
1661 }
1662
1663 #[inline]
1668 pub fn delete_prev_word(&mut self) -> bool {
1669 text_area_op::delete_prev_word(self)
1670 }
1671
1672 pub fn search(&mut self, re: &str) -> Result<bool, TextError> {
1683 match text_area_op::search(self, re, MATCH_STYLE) {
1684 Ok(r) => Ok(r),
1685 Err(_) => Err(TextError::InvalidSearch),
1686 }
1687 }
1688
1689 pub fn move_to_next_match(&mut self) -> bool {
1691 text_area_op::move_to_next_match(self, MATCH_STYLE)
1692 }
1693
1694 pub fn move_to_prev_match(&mut self) -> bool {
1696 text_area_op::move_to_prev_match(self, MATCH_STYLE)
1697 }
1698
1699 pub fn clear_search(&mut self) {
1701 text_area_op::clear_search(self, MATCH_STYLE)
1702 }
1703
1704 #[inline]
1707 pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1708 text_area_op::move_left(self, n, extend_selection)
1709 }
1710
1711 #[inline]
1714 pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1715 text_area_op::move_right(self, n, extend_selection)
1716 }
1717
1718 #[inline]
1721 pub fn move_up(&mut self, n: u16, extend_selection: bool) -> bool {
1722 text_area_op::move_up(self, n, extend_selection)
1723 }
1724
1725 #[inline]
1728 pub fn move_down(&mut self, n: u16, extend_selection: bool) -> bool {
1729 text_area_op::move_down(self, n, extend_selection)
1730 }
1731
1732 #[inline]
1736 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1737 text_area_op::move_to_line_start(self, extend_selection)
1738 }
1739
1740 #[inline]
1744 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1745 text_area_op::move_to_line_end(self, extend_selection)
1746 }
1747
1748 #[inline]
1750 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1751 text_area_op::move_to_start(self, extend_selection)
1752 }
1753
1754 #[inline]
1756 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1757 text_area_op::move_to_end(self, extend_selection)
1758 }
1759
1760 #[inline]
1762 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1763 text_area_op::move_to_screen_start(self, extend_selection)
1764 }
1765
1766 #[inline]
1768 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1769 text_area_op::move_to_screen_end(self, extend_selection)
1770 }
1771
1772 #[inline]
1774 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1775 text_area_op::move_to_next_word(self, extend_selection)
1776 }
1777
1778 #[inline]
1780 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1781 text_area_op::move_to_prev_word(self, extend_selection)
1782 }
1783}
1784
1785impl HasScreenCursor for TextAreaState {
1786 #[allow(clippy::question_mark)]
1788 fn screen_cursor(&self) -> Option<(u16, u16)> {
1789 if self.is_focused() {
1790 if self.has_selection() {
1791 None
1792 } else {
1793 let Some(scr_cursor) = self.screen_cursor else {
1794 return None;
1795 };
1796
1797 if !(scr_cursor.0 >= self.inner.x
1798 && scr_cursor.0 <= self.inner.right()
1799 && scr_cursor.1 >= self.inner.y
1800 && scr_cursor.1 < self.inner.bottom())
1801 {
1802 return None;
1803 }
1804 Some(scr_cursor)
1805 }
1806 } else {
1807 None
1808 }
1809 }
1810}
1811
1812impl RelocatableState for TextAreaState {
1813 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1814 self.area.relocate(shift, clip);
1816 self.inner.relocate(shift, clip);
1817 self.hscroll.relocate(shift, clip);
1818 self.vscroll.relocate(shift, clip);
1819 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1820 if let Some(screen_cursor) = self.screen_cursor {
1821 self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
1822 }
1823 }
1824}
1825
1826impl TextAreaState {
1827 fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
1829 match self.text_wrap {
1830 TextWrap::Shift => (
1831 TextWrap2::Shift,
1832 shift_left,
1833 shift_left + self.rendered.width as upos_type,
1834 shift_left + self.rendered.width as upos_type,
1835 ),
1836 TextWrap::Hard => (
1837 TextWrap2::Hard,
1838 0,
1839 self.rendered.width as upos_type,
1840 self.rendered.width as upos_type,
1841 ),
1842 TextWrap::Word(margin) => (
1843 TextWrap2::Word,
1844 0,
1845 self.rendered.width as upos_type,
1846 self.rendered.width.saturating_sub(margin) as upos_type,
1847 ),
1848 }
1849 }
1850
1851 fn fill_cache(
1854 &self,
1855 shift_left: upos_type,
1856 sub_row_offset: upos_type,
1857 rows: Range<upos_type>,
1858 ) -> Result<(), TextError> {
1859 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1860 self.value.fill_cache(
1861 self.rendered,
1862 sub_row_offset,
1863 rows,
1864 self.tab_width,
1865 text_wrap,
1866 self.wrap_ctrl() | self.show_ctrl(),
1867 left_margin,
1868 right_margin,
1869 word_margin,
1870 )
1871 }
1872
1873 fn glyphs2(
1875 &self,
1876 shift_left: upos_type,
1877 sub_row_offset: upos_type,
1878 rows: Range<upos_type>,
1879 ) -> Result<GlyphIter2<'_, <TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
1880 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1881 self.value.glyphs2(
1882 self.rendered,
1883 sub_row_offset,
1884 rows,
1885 self.tab_width,
1886 text_wrap,
1887 self.wrap_ctrl() | self.show_ctrl(),
1888 left_margin,
1889 right_margin,
1890 word_margin,
1891 )
1892 }
1893
1894 fn stc_fill_screen_cache(&self, scr: (upos_type, upos_type, upos_type)) {
1898 let y2 = scr.1 + scr.2;
1899 self.fill_cache(0, scr.0, scr.1..min(y2, self.len_lines()))
1900 .expect("valid-rows");
1901 }
1902
1903 fn stc_screen_row(
1910 &self,
1911 scr: (upos_type, upos_type, upos_type),
1912 pos: TextPosition,
1913 ) -> Option<upos_type> {
1914 if pos < TextPosition::new(scr.0, scr.1) {
1915 return None;
1916 }
1917
1918 let line_breaks = self.value.cache().line_break.borrow();
1919 let range_start = TextPosition::new(scr.0, scr.1);
1920 let y2 = scr.1 + scr.2;
1921 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
1922
1923 let mut start_pos = TextPosition::new(scr.0, scr.1);
1924 let mut scr_row = 0;
1925 for (_key, cache) in line_breaks.range(range_start..range_end) {
1926 if pos < cache.start_pos {
1927 return Some(scr_row);
1928 }
1929 scr_row += 1;
1930 start_pos = cache.start_pos;
1931 }
1932
1933 if pos == start_pos {
1935 return Some(scr_row);
1936 }
1937
1938 None
1939 }
1940
1941 fn stc_sub_row_offset(
1948 &self,
1949 scr: (upos_type, upos_type, upos_type),
1950 mut scr_row: upos_type,
1951 ) -> (upos_type, upos_type) {
1952 let line_breaks = self.value.cache().line_break.borrow();
1953 let range_start = TextPosition::new(scr.0, scr.1);
1954 let y2 = scr.1 + scr.2;
1955 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
1956
1957 let mut start_pos = (scr.0, scr.1);
1958 for (_key, cache) in line_breaks.range(range_start..range_end) {
1959 if scr_row == 0 {
1960 return start_pos;
1961 }
1962 scr_row -= 1;
1963 start_pos = (cache.start_pos.x, cache.start_pos.y);
1964 }
1965
1966 start_pos
1968 }
1969}
1970
1971impl TextAreaState {
1972 #[allow(clippy::needless_return)]
1974 pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
1975 let scr_pos = (
1976 scr_pos.0 + self.dark_offset.0 as i16,
1977 scr_pos.1 + self.dark_offset.1 as i16,
1978 );
1979
1980 match self.text_wrap {
1981 TextWrap::Shift => {
1982 let (ox, _, oy) = self.clean_offset();
1983
1984 if oy >= self.len_lines() {
1985 return None;
1986 }
1987
1988 if scr_pos.1 < 0 {
1989 return Some(TextPosition::new(
1991 0,
1992 oy.saturating_add_signed(scr_pos.1 as ipos_type),
1993 ));
1994 } else if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
1995 return Some(TextPosition::new(0, self.len_lines().saturating_sub(1)));
1997 }
1998
1999 let pos_y = oy + scr_pos.1 as upos_type;
2000
2001 if scr_pos.0 < 0 {
2002 return Some(TextPosition::new(
2003 ox.saturating_add_signed(scr_pos.0 as ipos_type),
2004 pos_y,
2005 ));
2006 } else if scr_pos.0 as u16 >= self.rendered.width {
2007 return Some(TextPosition::new(
2008 min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
2009 pos_y,
2010 ));
2011 } else {
2012 let mut start_pos = TextPosition::new(0, pos_y);
2013 for g in self
2014 .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
2015 .expect("valid-position")
2016 {
2017 if g.contains_screen_x(scr_pos.0 as u16) {
2018 return Some(TextPosition::new(g.pos().x, pos_y));
2019 }
2020 start_pos = g.pos();
2021 }
2022 Some(start_pos)
2023 }
2024 }
2025 TextWrap::Hard | TextWrap::Word(_) => {
2026 let (_, sub_row_offset, oy) = self.clean_offset();
2027
2028 if oy >= self.len_lines() {
2029 return None;
2030 }
2031
2032 if scr_pos.1 < 0 {
2033 let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2042
2043 self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2044
2045 let n_start_pos = 'f: {
2046 let line_break = self.value.cache().line_break.borrow();
2047 let start_range = TextPosition::new(0, ry);
2048 let end_range = TextPosition::new(sub_row_offset, oy);
2049
2050 let mut nrows = scr_pos.1.unsigned_abs();
2051 for (_break_pos, cache) in line_break.range(start_range..end_range).rev() {
2052 if nrows == 0 {
2053 break 'f cache.start_pos;
2054 }
2055 nrows -= 1;
2056 }
2057 TextPosition::new(0, ry)
2058 };
2059
2060 if scr_pos.0 < 0 {
2062 return Some(n_start_pos);
2063 }
2064
2065 let min_row = n_start_pos.y;
2066 let max_row = min(n_start_pos.y + 1, self.len_lines());
2067 for g in self
2068 .glyphs2(0, n_start_pos.x, min_row..max_row)
2069 .expect("valid-rows")
2070 {
2071 if g.contains_screen_x(scr_pos.0 as u16) {
2072 return Some(g.pos());
2073 }
2074 }
2075
2076 return Some(n_start_pos);
2078 } else {
2079 let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2080
2081 let n_start_pos = if scr_pos.1 == 0 {
2083 TextPosition::new(sub_row_offset, oy)
2084 } else {
2085 self.fill_cache(
2087 0,
2088 sub_row_offset,
2089 oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2090 )
2091 .expect("valid-rows");
2092
2093 'f: {
2094 let text_range = self.value.cache().line_break.borrow();
2095 let start_range = TextPosition::new(sub_row_offset, oy);
2096 let end_range = TextPosition::new(0, self.len_lines());
2097
2098 let mut nrows = scr_pos.1 - 1;
2099 let mut start_pos = TextPosition::new(sub_row_offset, oy);
2100 for (_break_pos, cache) in text_range.range(start_range..end_range) {
2101 if nrows == 0 {
2102 break 'f cache.start_pos;
2103 }
2104 start_pos = cache.start_pos;
2105 nrows -= 1;
2106 }
2107 start_pos
2108 }
2109 };
2110
2111 let min_row = n_start_pos.y;
2112 let max_row = min(n_start_pos.y + 1, self.len_lines());
2113 for g in self
2114 .glyphs2(0, n_start_pos.x, min_row..max_row)
2115 .expect("valid-rows")
2116 {
2117 if g.contains_screen_x(scr_pos.0) {
2118 return Some(g.pos());
2119 }
2120 }
2121
2122 return Some(n_start_pos);
2124 }
2125 }
2126 }
2127 }
2128
2129 pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
2131 let scr_pos = (
2132 scr_pos.0 as i16 - self.inner.x as i16,
2133 scr_pos.1 as i16 - self.inner.y as i16,
2134 );
2135 self.relative_screen_to_pos(scr_pos)
2136 }
2137
2138 #[allow(clippy::explicit_counter_loop)]
2148 pub fn pos_to_relative_screen(&self, pos: impl Into<TextPosition>) -> Option<(i16, i16)> {
2149 let pos = pos.into();
2150 match self.text_wrap {
2151 TextWrap::Shift => {
2152 let (ox, _, oy) = self.clean_offset();
2153
2154 if oy > self.len_lines() {
2155 return None;
2156 }
2157 if pos.y < oy {
2158 return None;
2159 }
2160 if pos.y > self.len_lines() {
2161 return None;
2162 }
2163 if pos.y - oy >= self.rendered.height as u32 {
2164 return None;
2165 }
2166
2167 let screen_y = (pos.y - oy) as u16;
2168
2169 let screen_x = 'f: {
2170 for g in self
2171 .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2172 .expect("valid-row")
2173 {
2174 if g.pos().x == pos.x {
2175 break 'f g.screen_pos().0;
2176 } else if g.line_break() {
2177 break 'f g.screen_pos().0;
2178 }
2179 }
2180 0
2182 };
2183 assert!(screen_x <= self.rendered.width);
2184
2185 Some((
2186 screen_x as i16 - self.dark_offset.0 as i16,
2187 screen_y as i16 - self.dark_offset.1 as i16,
2188 ))
2189 }
2190 TextWrap::Hard | TextWrap::Word(_) => {
2191 let (_, sub_row_offset, oy) = self.clean_offset();
2192
2193 if oy > self.len_lines() {
2194 return None;
2195 }
2196 if pos.y < oy {
2197 return None;
2198 }
2199 if pos.y > self.len_lines() {
2200 return None;
2201 }
2202
2203 let page = self.rendered.height as upos_type;
2204 let scr = (sub_row_offset, oy, page);
2205 self.stc_fill_screen_cache(scr);
2206 let (screen_y, start_pos) = if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2207 if pos_row >= page {
2208 return None;
2210 }
2211 let start_pos = self.stc_sub_row_offset(scr, pos_row);
2212 (pos_row, start_pos)
2213 } else {
2214 return None;
2216 };
2217
2218 let screen_x = 'f: {
2219 for g in self
2220 .glyphs2(
2221 0,
2222 start_pos.0,
2223 start_pos.1..min(start_pos.1 + 1, self.len_lines()),
2224 )
2225 .expect("valid-row")
2226 {
2227 if g.pos().x == pos.x {
2228 break 'f g.screen_pos().0;
2229 } else if g.line_break() {
2230 break 'f g.screen_pos().0;
2231 }
2232 }
2233 0
2235 };
2236 assert!(screen_x <= self.rendered.width);
2237
2238 let scr = (
2239 screen_x as i16 - self.dark_offset.0 as i16,
2240 screen_y as i16 - self.dark_offset.1 as i16,
2241 );
2242 Some(scr)
2243 }
2244 }
2245 }
2246
2247 #[inline]
2250 pub fn pos_to_screen(&self, pos: impl Into<TextPosition>) -> Option<(u16, u16)> {
2251 let scr_pos = self.pos_to_relative_screen(pos.into())?;
2252 if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
2253 Some((
2254 (scr_pos.0 + self.inner.x as i16) as u16,
2255 (scr_pos.1 + self.inner.y as i16) as u16,
2256 ))
2257 } else {
2258 None
2259 }
2260 }
2261
2262 pub fn pos_to_line_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
2264 let pos = pos.into();
2265 match self.text_wrap {
2266 TextWrap::Shift => {
2267 TextPosition::new(0, pos.y)
2269 }
2270 TextWrap::Hard | TextWrap::Word(_) => {
2271 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2272 .expect("valid-row");
2273
2274 let mut start_pos = TextPosition::new(0, pos.y);
2275 for (break_pos, _) in self.value.cache().line_break.borrow().range(
2276 TextPosition::new(0, pos.y)
2277 ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
2278 ) {
2279 if pos >= start_pos && &pos <= break_pos {
2280 break;
2281 }
2282 start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2283 }
2284
2285 start_pos
2286 }
2287 }
2288 }
2289
2290 pub fn pos_to_line_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
2292 let pos = pos.into();
2293
2294 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2295 .expect("valid-row");
2296
2297 let mut end_pos = TextPosition::new(0, pos.y);
2298 for (break_pos, _) in self
2299 .value
2300 .cache()
2301 .line_break
2302 .borrow()
2303 .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2304 {
2305 if pos >= end_pos && &pos <= break_pos {
2306 end_pos = *break_pos;
2307 break;
2308 }
2309 end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2310 }
2311
2312 end_pos
2313 }
2314
2315 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2321 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2322 return false;
2323 };
2324 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2325 self.set_move_col(Some(scr_cursor.0));
2326 }
2327 self.set_cursor(cursor, extend_selection)
2328 }
2329
2330 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2337 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2338 return false;
2339 };
2340
2341 let anchor = self.anchor();
2342 let cursor = if cursor < anchor {
2343 self.word_start(cursor)
2344 } else {
2345 self.word_end(cursor)
2346 };
2347
2348 if !self.is_word_boundary(anchor) {
2350 if cursor < anchor {
2351 self.set_cursor(self.word_end(anchor), false);
2352 } else {
2353 self.set_cursor(self.word_start(anchor), false);
2354 }
2355 }
2356
2357 self.set_cursor(cursor, extend_selection)
2358 }
2359}
2360
2361impl TextAreaState {
2362 pub fn vertical_max_offset(&self) -> upos_type {
2367 self.vscroll.max_offset() as upos_type
2368 }
2369
2370 pub fn vertical_offset(&self) -> upos_type {
2372 self.vscroll.offset() as upos_type
2373 }
2374
2375 pub fn vertical_page(&self) -> upos_type {
2377 self.vscroll.page_len() as upos_type
2378 }
2379
2380 pub fn vertical_scroll(&self) -> upos_type {
2382 self.vscroll.scroll_by() as upos_type
2383 }
2384
2385 pub fn horizontal_max_offset(&self) -> upos_type {
2394 self.hscroll.max_offset() as upos_type
2395 }
2396
2397 pub fn horizontal_offset(&self) -> upos_type {
2399 self.hscroll.offset() as upos_type
2400 }
2401
2402 pub fn horizontal_page(&self) -> upos_type {
2404 self.hscroll.page_len() as upos_type
2405 }
2406
2407 pub fn horizontal_scroll(&self) -> upos_type {
2409 self.hscroll.scroll_by() as upos_type
2410 }
2411
2412 pub fn set_vertical_offset(&mut self, row_offset: upos_type) -> bool {
2419 self.scroll_to_cursor.set(false);
2420 self.sub_row_offset = 0;
2421 self.vscroll.set_offset(row_offset as usize)
2422 }
2423
2424 pub fn set_horizontal_offset(&mut self, col_offset: upos_type) -> bool {
2433 self.scroll_to_cursor.set(false);
2434 self.hscroll.set_offset(col_offset as usize)
2435 }
2436
2437 pub fn scroll_cursor_to_visible(&mut self) {
2443 self.scroll_to_cursor.set(true);
2444 }
2445
2446 pub fn scroll_to_pos(&mut self, pos: impl Into<TextPosition>) -> bool {
2452 let old_offset = self.clean_offset();
2453
2454 let pos = pos.into();
2455
2456 'f: {
2457 match self.text_wrap {
2458 TextWrap::Shift => {
2459 let (ox, _, oy) = old_offset;
2460
2461 let height = self.rendered.height as upos_type;
2462 let width = self.rendered.width as upos_type;
2463 let width = if self.show_ctrl() || self.wrap_ctrl() {
2464 width.saturating_sub(1)
2465 } else {
2466 width
2467 };
2468
2469 let noy = if pos.y < oy.saturating_sub(height) {
2470 pos.y.saturating_sub(height * 4 / 10)
2471 } else if pos.y < oy {
2472 pos.y
2473 } else if pos.y >= oy + 2 * height {
2474 pos.y.saturating_sub(height * 6 / 10)
2475 } else if pos.y >= oy + height {
2476 pos.y.saturating_sub(height.saturating_sub(1))
2477 } else {
2478 oy
2479 };
2480
2481 let nox = if pos.x < ox {
2482 pos.x
2483 } else if pos.x >= ox + width {
2484 pos.x.saturating_sub(width) + 1
2485 } else {
2486 ox
2487 };
2488
2489 self.set_offset((nox, noy));
2490 self.set_sub_row_offset(0);
2491 }
2492 TextWrap::Hard | TextWrap::Word(_) => {
2493 let (_ox, sub_row_offset, oy) = old_offset;
2494 let page = self.rendered.height as upos_type;
2495
2496 let scr = (0, oy.saturating_sub(page), 3 * page);
2498 self.stc_fill_screen_cache(scr);
2499 if let Some(off_row) =
2500 self.stc_screen_row(scr, TextPosition::new(sub_row_offset, oy))
2501 {
2502 if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2503 if pos_row < off_row && pos_row >= off_row.saturating_sub(page) {
2504 let noff_row = pos_row;
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 + page && pos_row < off_row + 2 * page {
2510 let noff_row = pos_row.saturating_sub(page.saturating_sub(1));
2511 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2512 self.set_offset((0, noy));
2513 self.set_sub_row_offset(nsub_row_offset);
2514 break 'f;
2515 } else if pos_row >= off_row && pos_row < off_row + page {
2516 break 'f;
2517 }
2518 }
2519 }
2520
2521 let alt_scr = (0, pos.y.saturating_sub(page), 3 * page);
2523 self.stc_fill_screen_cache(alt_scr);
2524 if let Some(alt_scr_row) = self.stc_screen_row(alt_scr, pos) {
2525 let noff_row = alt_scr_row.saturating_sub(page * 5 / 10);
2526 let (nsub_row_offset, noy) = self.stc_sub_row_offset(alt_scr, noff_row);
2527 self.set_offset((0, noy));
2528 self.set_sub_row_offset(nsub_row_offset);
2529 } else {
2530 self.set_offset((0, pos.y));
2531 self.set_sub_row_offset(0);
2532 }
2533 }
2534 }
2535 }
2536
2537 old_offset != self.clean_offset()
2538 }
2539
2540 pub fn scroll_to_row(&mut self, pos: upos_type) -> bool {
2549 self.scroll_to_cursor.set(false);
2550
2551 match self.text_wrap {
2552 TextWrap::Shift => self.vscroll.scroll_to_pos(pos as usize),
2553 TextWrap::Hard | TextWrap::Word(_) => self
2554 .vscroll
2555 .set_offset(self.vscroll.limited_offset(pos as usize)),
2556 }
2557 }
2558
2559 pub fn scroll_to_col(&mut self, pos: upos_type) -> bool {
2567 self.scroll_to_cursor.set(false);
2568 self.hscroll.set_offset(pos as usize)
2569 }
2570
2571 pub fn scroll_up(&mut self, delta: upos_type) -> bool {
2577 if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2578 self.sub_row_offset = pos.x;
2579 self.vscroll.set_offset(pos.y as usize);
2580 true
2581 } else {
2582 false
2583 }
2584 }
2585
2586 pub fn scroll_down(&mut self, delta: upos_type) -> bool {
2592 if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2593 self.sub_row_offset = pos.x;
2594 self.vscroll.set_offset(pos.y as usize);
2595 true
2596 } else {
2597 false
2598 }
2599 }
2600
2601 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
2613 self.hscroll
2614 .set_offset(self.hscroll.offset.saturating_add(delta as usize))
2615 }
2616
2617 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
2629 self.hscroll
2630 .set_offset(self.hscroll.offset.saturating_sub(delta as usize))
2631 }
2632}
2633
2634impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2635 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2636 fn tc(r: bool) -> TextOutcome {
2638 if r {
2639 TextOutcome::TextChanged
2640 } else {
2641 TextOutcome::Unchanged
2642 }
2643 }
2644
2645 let mut r =
2646 if self.is_focused() {
2647 match event {
2648 ct_event!(key press c)
2649 | ct_event!(key press SHIFT-c)
2650 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2651 ct_event!(keycode press Tab) => {
2652 tc(if !self.focus.gained() {
2654 self.insert_tab()
2655 } else {
2656 false
2657 })
2658 }
2659 ct_event!(keycode press SHIFT-BackTab) => {
2660 tc(if !self.focus.gained() {
2662 self.insert_backtab()
2663 } else {
2664 false
2665 })
2666 }
2667 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2668 ct_event!(keycode press Backspace)
2669 | ct_event!(keycode press SHIFT-Backspace) => tc(self.delete_prev_char()),
2670 ct_event!(keycode press Delete) | ct_event!(keycode press SHIFT-Delete) => {
2671 tc(self.delete_next_char())
2672 }
2673 ct_event!(keycode press CONTROL-Backspace)
2674 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2675 ct_event!(keycode press CONTROL-Delete)
2676 | ct_event!(keycode press ALT-Delete) => tc(self.delete_next_word()),
2677 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2678 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2679 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2680 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2681 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2682 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2683
2684 ct_event!(key release _)
2685 | ct_event!(key release SHIFT-_)
2686 | ct_event!(key release CONTROL_ALT-_)
2687 | ct_event!(keycode release Tab)
2688 | ct_event!(keycode release Enter)
2689 | ct_event!(keycode release Backspace)
2690 | ct_event!(keycode release Delete)
2691 | ct_event!(keycode release CONTROL-Backspace)
2692 | ct_event!(keycode release ALT-Backspace)
2693 | ct_event!(keycode release CONTROL-Delete)
2694 | ct_event!(key release CONTROL-'x')
2695 | ct_event!(key release CONTROL-'v')
2696 | ct_event!(key release CONTROL-'d')
2697 | ct_event!(key release CONTROL-'y')
2698 | ct_event!(key release CONTROL-'z')
2699 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2700 _ => TextOutcome::Continue,
2701 }
2702 } else {
2703 TextOutcome::Continue
2704 };
2705 if r == TextOutcome::Continue {
2706 r = self.handle(event, ReadOnly);
2707 }
2708 r
2709 }
2710}
2711
2712pub const MATCH_STYLE: usize = 100_001;
2714
2715impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
2716 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2717 let mut r = if self.is_focused() {
2718 match event {
2719 ct_event!(keycode press Left) => self.move_left(1, false).into(),
2720 ct_event!(keycode press Right) => self.move_right(1, false).into(),
2721 ct_event!(keycode press Up) => self.move_up(1, false).into(),
2722 ct_event!(keycode press Down) => self.move_down(1, false).into(),
2723 ct_event!(keycode press PageUp) => {
2724 self.move_up(self.vertical_page() as u16, false).into()
2725 }
2726 ct_event!(keycode press PageDown) => {
2727 self.move_down(self.vertical_page() as u16, false).into()
2728 }
2729 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
2730 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
2731 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
2732 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
2733 ct_event!(keycode press CONTROL-Up) => false.into(),
2734 ct_event!(keycode press CONTROL-Down) => false.into(),
2735 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
2736 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
2737 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
2738 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
2739
2740 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
2741 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
2742 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
2743 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
2744 ct_event!(keycode press ALT-PageUp) => {
2745 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
2746 }
2747 ct_event!(keycode press ALT-PageDown) => {
2748 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
2749 }
2750 ct_event!(keycode press ALT_SHIFT-PageUp) => {
2751 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
2752 }
2753 ct_event!(keycode press ALT_SHIFT-PageDown) => {
2754 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
2755 }
2756
2757 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
2758 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
2759 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
2760 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
2761 ct_event!(keycode press SHIFT-PageUp) => {
2762 self.move_up(self.vertical_page() as u16, true).into()
2763 }
2764 ct_event!(keycode press SHIFT-PageDown) => {
2765 self.move_down(self.vertical_page() as u16, true).into()
2766 }
2767 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
2768 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
2769 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
2770 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
2771 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
2772 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
2773 ct_event!(key press CONTROL-'a') => self.select_all().into(),
2774 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
2775
2776 ct_event!(keycode press F(3)) => self.move_to_next_match().into(),
2777 ct_event!(keycode press SHIFT-F(3)) => self.move_to_prev_match().into(),
2778
2779 ct_event!(keycode release Left)
2780 | ct_event!(keycode release Right)
2781 | ct_event!(keycode release Up)
2782 | ct_event!(keycode release Down)
2783 | ct_event!(keycode release PageUp)
2784 | ct_event!(keycode release PageDown)
2785 | ct_event!(keycode release Home)
2786 | ct_event!(keycode release End)
2787 | ct_event!(keycode release CONTROL-Left)
2788 | ct_event!(keycode release CONTROL-Right)
2789 | ct_event!(keycode release CONTROL-Up)
2790 | ct_event!(keycode release CONTROL-Down)
2791 | ct_event!(keycode release CONTROL-PageUp)
2792 | ct_event!(keycode release CONTROL-PageDown)
2793 | ct_event!(keycode release CONTROL-Home)
2794 | ct_event!(keycode release CONTROL-End)
2795 | ct_event!(keycode release ALT-Left)
2796 | ct_event!(keycode release ALT-Right)
2797 | ct_event!(keycode release ALT-Up)
2798 | ct_event!(keycode release ALT-Down)
2799 | ct_event!(keycode release ALT-PageUp)
2800 | ct_event!(keycode release ALT-PageDown)
2801 | ct_event!(keycode release ALT_SHIFT-PageUp)
2802 | ct_event!(keycode release ALT_SHIFT-PageDown)
2803 | ct_event!(keycode release SHIFT-Left)
2804 | ct_event!(keycode release SHIFT-Right)
2805 | ct_event!(keycode release SHIFT-Up)
2806 | ct_event!(keycode release SHIFT-Down)
2807 | ct_event!(keycode release SHIFT-PageUp)
2808 | ct_event!(keycode release SHIFT-PageDown)
2809 | ct_event!(keycode release SHIFT-Home)
2810 | ct_event!(keycode release SHIFT-End)
2811 | ct_event!(keycode release CONTROL_SHIFT-Left)
2812 | ct_event!(keycode release CONTROL_SHIFT-Right)
2813 | ct_event!(keycode release CONTROL_SHIFT-Home)
2814 | ct_event!(keycode release CONTROL_SHIFT-End)
2815 | ct_event!(key release CONTROL-'a')
2816 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2817 _ => TextOutcome::Continue,
2818 }
2819 } else {
2820 TextOutcome::Continue
2821 };
2822
2823 if r == TextOutcome::Continue {
2824 r = self.handle(event, MouseOnly);
2825 }
2826 r
2827 }
2828}
2829
2830impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
2831 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
2832 flow!(match event {
2833 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2834 let cx = m.column as i16 - self.inner.x as i16;
2835 let cy = m.row as i16 - self.inner.y as i16;
2836 self.set_screen_cursor((cx, cy), true).into()
2837 }
2838 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2839 let cx = m.column as i16 - self.inner.x as i16;
2840 let cy = m.row as i16 - self.inner.y as i16;
2841 self.set_screen_cursor_words((cx, cy), true).into()
2842 }
2843 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2844 if let Some(test) = self.screen_to_pos((m.column, m.row)) {
2845 let start = self.word_start(test);
2846 let end = self.word_end(test);
2847 self.set_selection(start, end).into()
2848 } else {
2849 TextOutcome::Unchanged
2850 }
2851 }
2852 ct_event!(mouse down Left for column,row) => {
2853 if self.inner.contains((*column, *row).into()) {
2854 let cx = (column - self.inner.x) as i16;
2855 let cy = (row - self.inner.y) as i16;
2856 self.set_screen_cursor((cx, cy), false).into()
2857 } else {
2858 TextOutcome::Continue
2859 }
2860 }
2861 ct_event!(mouse down SHIFT-Left for column,row) => {
2862 if self.inner.contains((*column, *row).into()) {
2863 let cx = (column - self.inner.x) as i16;
2864 let cy = (row - self.inner.y) as i16;
2865 self.set_screen_cursor((cx, cy), true).into()
2866 } else {
2867 TextOutcome::Continue
2868 }
2869 }
2870 ct_event!(mouse down CONTROL-Left for column,row) => {
2871 if self.inner.contains((*column, *row).into()) {
2872 let cx = (column - self.inner.x) as i16;
2873 let cy = (row - self.inner.y) as i16;
2874 self.set_screen_cursor((cx, cy), true).into()
2875 } else {
2876 TextOutcome::Continue
2877 }
2878 }
2879 ct_event!(mouse down ALT-Left for column,row) => {
2880 if self.inner.contains((*column, *row).into()) {
2881 let cx = (column - self.inner.x) as i16;
2882 let cy = (row - self.inner.y) as i16;
2883 self.set_screen_cursor_words((cx, cy), true).into()
2884 } else {
2885 TextOutcome::Continue
2886 }
2887 }
2888 _ => TextOutcome::Continue,
2889 });
2890
2891 let mut sas = ScrollAreaState::new()
2892 .area(self.inner)
2893 .h_scroll(&mut self.hscroll)
2894 .v_scroll(&mut self.vscroll);
2895 let r = match sas.handle(event, MouseOnly) {
2896 ScrollOutcome::Up(v) => self.scroll_up(v as upos_type),
2897 ScrollOutcome::Down(v) => self.scroll_down(v as upos_type),
2898 ScrollOutcome::Left(v) => self.scroll_left(v as upos_type),
2899 ScrollOutcome::Right(v) => self.scroll_right(v as upos_type),
2900 ScrollOutcome::VPos(v) => self.scroll_to_row(v as upos_type),
2901 ScrollOutcome::HPos(v) => self.scroll_to_col(v as upos_type),
2902 _ => false,
2903 };
2904 if r {
2905 return TextOutcome::Changed;
2906 }
2907
2908 TextOutcome::Continue
2909 }
2910}
2911
2912pub fn handle_events(
2916 state: &mut TextAreaState,
2917 focus: bool,
2918 event: &crossterm::event::Event,
2919) -> TextOutcome {
2920 state.focus.set(focus);
2921 state.handle(event, Regular)
2922}
2923
2924pub fn handle_readonly_events(
2928 state: &mut TextAreaState,
2929 focus: bool,
2930 event: &crossterm::event::Event,
2931) -> TextOutcome {
2932 state.focus.set(focus);
2933 state.handle(event, ReadOnly)
2934}
2935
2936pub fn handle_mouse_events(
2938 state: &mut TextAreaState,
2939 event: &crossterm::event::Event,
2940) -> TextOutcome {
2941 state.handle(event, MouseOnly)
2942}