1use crate::_private::NonExhaustive;
7use crate::clipboard::{Clipboard, global_clipboard};
8use crate::cursor::{CursorType, cursor_type};
9use crate::event::{ReadOnly, TextOutcome};
10use crate::glyph2::{GlyphIter2, TextWrap2};
11use crate::text_core::TextCore;
12use crate::text_core::core_op;
13use crate::text_store::TextStore;
14use crate::text_store::text_rope::TextRope;
15use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
16use crate::{HasScreenCursor, TextError, TextPosition, TextRange, TextStyle, ipos_type, upos_type};
17use crossterm::event::KeyModifiers;
18use rat_event::util::MouseFlags;
19use rat_event::{HandleEvent, MouseOnly, Regular, ct_event, flow};
20use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
21use rat_reloc::{RelocatableState, relocate_dark_offset, relocate_pos_tuple};
22use rat_scrolled::event::ScrollOutcome;
23use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState};
24use ratatui::buffer::Buffer;
25use ratatui::layout::{Rect, Size};
26use ratatui::style::{Style, Stylize};
27use ratatui::widgets::{Block, StatefulWidget};
28use ropey::Rope;
29use std::borrow::Cow;
30use std::cell::Cell;
31use std::cmp::{max, min};
32use std::collections::HashMap;
33use std::ops::Range;
34use std::rc::Rc;
35
36pub mod text_area_op;
37
38#[derive(Debug, Default, Clone)]
78pub struct TextArea<'a> {
79 style: Style,
80 block: Option<Block<'a>>,
81 hscroll: Option<Scroll<'a>>,
82 h_max_offset: Option<upos_type>,
83 h_overscroll: Option<upos_type>,
84 vscroll: Option<Scroll<'a>>,
85
86 focus_style: Option<Style>,
87 select_style: Option<Style>,
88 cursor_style: Option<Style>,
89 text_style: HashMap<usize, Style>,
90
91 text_wrap: Option<TextWrap>,
92}
93
94#[derive(Debug)]
96pub struct TextAreaState {
97 pub area: Rect,
100 pub inner: Rect,
103 pub rendered: Size,
108 screen_cursor: Option<(u16, u16)>,
111
112 pub hscroll: ScrollState,
116 pub vscroll: ScrollState,
119 pub sub_row_offset: upos_type,
124 pub dark_offset: (u16, u16),
127 pub scroll_to_cursor: Rc<Cell<bool>>,
133
134 pub value: TextCore<TextRope>,
136
137 pub move_col: Option<i16>,
146 pub auto_indent: bool,
149 pub auto_quote: bool,
152 pub text_wrap: TextWrap,
155 pub newline: String,
158 pub tab_width: u32,
161 pub expand_tabs: bool,
164
165 pub focus: FocusFlag,
168
169 pub mouse: MouseFlags,
172
173 pub non_exhaustive: NonExhaustive,
174}
175
176impl Clone for TextAreaState {
177 fn clone(&self) -> Self {
178 Self {
179 area: self.area,
180 inner: self.inner,
181 rendered: self.rendered,
182 screen_cursor: self.screen_cursor,
183 hscroll: self.hscroll.clone(),
184 vscroll: self.vscroll.clone(),
185 sub_row_offset: self.sub_row_offset,
186 dark_offset: self.dark_offset,
187 scroll_to_cursor: Rc::new(Cell::new(self.scroll_to_cursor.get())),
188 value: self.value.clone(),
189 move_col: None,
190 auto_indent: self.auto_indent,
191 auto_quote: self.auto_quote,
192 text_wrap: self.text_wrap,
193 newline: self.newline.clone(),
194 tab_width: self.tab_width,
195 expand_tabs: self.expand_tabs,
196 focus: self.focus.new_instance(),
197 mouse: Default::default(),
198 non_exhaustive: NonExhaustive,
199 }
200 }
201}
202
203#[derive(Debug, Default, Clone, Copy)]
205#[non_exhaustive]
206pub enum TextWrap {
207 #[default]
209 Shift,
210 Hard,
212 Word(u16),
222}
223
224impl<'a> TextArea<'a> {
225 pub fn new() -> Self {
227 Self::default()
228 }
229
230 #[inline]
232 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
233 if let Some(styles) = styles {
234 self.styles(styles)
235 } else {
236 self
237 }
238 }
239
240 #[inline]
242 pub fn styles(mut self, styles: TextStyle) -> Self {
243 self.style = styles.style;
244 if styles.block.is_some() {
245 self.block = styles.block;
246 }
247 if let Some(border_style) = styles.border_style {
248 self.block = self.block.map(|v| v.border_style(border_style));
249 }
250 if let Some(title_style) = styles.title_style {
251 self.block = self.block.map(|v| v.title_style(title_style));
252 }
253 self.block = self.block.map(|v| v.style(self.style));
254
255 if let Some(styles) = styles.scroll {
256 self.hscroll = self.hscroll.map(|v| v.styles(styles.clone()));
257 self.vscroll = self.vscroll.map(|v| v.styles(styles));
258 }
259 if styles.focus.is_some() {
260 self.focus_style = styles.focus;
261 }
262 if styles.select.is_some() {
263 self.select_style = styles.select;
264 }
265 if styles.cursor.is_some() {
266 self.cursor_style = styles.cursor;
267 }
268
269 self
270 }
271
272 pub fn style(mut self, style: Style) -> Self {
274 self.style = style;
275 self.block = self.block.map(|v| v.style(style));
276 self
277 }
278
279 pub fn focus_style(mut self, style: Style) -> Self {
281 self.focus_style = Some(style);
282 self.block = self.block.map(|v| v.style(self.style));
283 self
284 }
285
286 pub fn select_style(mut self, style: Style) -> Self {
288 self.select_style = Some(style);
289 self
290 }
291
292 #[inline]
295 pub fn cursor_style(mut self, style: impl Into<Style>) -> Self {
296 self.cursor_style = Some(style.into());
297 self
298 }
299
300 pub fn text_style_idx(mut self, idx: usize, style: Style) -> Self {
305 self.text_style.insert(idx, style);
306 self
307 }
308
309 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
314 for (i, s) in styles.into_iter().enumerate() {
315 self.text_style.insert(i, s);
316 }
317 self
318 }
319
320 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
325 for (i, s) in styles.into_iter() {
326 self.text_style.insert(i, s.into());
327 }
328 self
329 }
330
331 #[inline]
333 pub fn block(mut self, block: Block<'a>) -> Self {
334 self.block = Some(block);
335 self
336 }
337
338 pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
340 self.hscroll = Some(scroll.clone().override_horizontal());
341 self.vscroll = Some(scroll.override_vertical());
342 self
343 }
344
345 pub fn hscroll(mut self, scroll: Scroll<'a>) -> Self {
347 self.hscroll = Some(scroll.override_horizontal());
348 self
349 }
350
351 pub fn text_wrap(mut self, wrap: TextWrap) -> Self {
353 self.text_wrap = Some(wrap);
354 self
355 }
356
357 pub fn set_horizontal_max_offset(mut self, offset: u32) -> Self {
374 self.h_max_offset = Some(offset);
375 self
376 }
377
378 pub fn set_horizontal_overscroll(mut self, overscroll: u32) -> Self {
385 self.h_overscroll = Some(overscroll);
386 self
387 }
388
389 pub fn vscroll(mut self, scroll: Scroll<'a>) -> Self {
391 self.vscroll = Some(scroll.override_vertical());
392 self
393 }
394
395 pub fn width(&self) -> u16 {
397 0
398 }
399
400 pub fn height(&self) -> u16 {
402 1
403 }
404}
405
406impl<'a> StatefulWidget for &TextArea<'a> {
407 type State = TextAreaState;
408
409 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
410 render_text_area(self, area, buf, state);
411 }
412}
413
414impl StatefulWidget for TextArea<'_> {
415 type State = TextAreaState;
416
417 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
418 render_text_area(&self, area, buf, state);
419 }
420}
421
422fn render_text_area(
423 widget: &TextArea<'_>,
424 area: Rect,
425 buf: &mut Buffer,
426 state: &mut TextAreaState,
427) {
428 state.area = area;
429 state.screen_cursor = None;
430 if let Some(text_wrap) = widget.text_wrap {
431 state.text_wrap = text_wrap;
432 }
433
434 let focused = state.is_focused();
435 let cursor_type = cursor_type();
436 let style = widget.style;
437 let focus_style = if let Some(focus_style) = widget.focus_style {
438 focus_style
439 } else {
440 style
441 };
442 let select_style = if let Some(select_style) = widget.select_style {
443 select_style
444 } else {
445 Style::default().black().on_yellow()
446 };
447 let (style, select_style) = if focused {
448 (
449 style.patch(focus_style),
450 style.patch(focus_style).patch(select_style),
451 )
452 } else {
453 (style, style.patch(select_style))
454 };
455 let cursor_style = if cursor_type == CursorType::RenderedCursor {
456 widget
457 .cursor_style
458 .unwrap_or_else(|| Style::default().white().on_red())
459 } else {
460 Style::default()
461 };
462
463 state.area = area;
465 state.screen_cursor = None;
466 state.inner = ScrollArea::new()
467 .block(widget.block.as_ref())
468 .h_scroll(widget.hscroll.as_ref())
469 .v_scroll(widget.vscroll.as_ref())
470 .inner(area, Some(&state.hscroll), Some(&state.vscroll));
471 state.rendered = state.inner.as_size();
472
473 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
474 state.hscroll.set_max_offset(0);
475 state.hscroll.set_overscroll_by(None);
476 } else {
477 if let Some(h_max_offset) = widget.h_max_offset {
478 state.hscroll.set_max_offset(h_max_offset as usize);
479 }
480 if let Some(h_overscroll) = widget.h_overscroll {
481 state.hscroll.set_overscroll_by(Some(h_overscroll as usize));
482 }
483 }
484 state.hscroll.set_page_len(state.inner.width as usize);
485
486 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
487 state
488 .vscroll
489 .set_max_offset(state.len_lines().saturating_sub(1) as usize);
490 } else {
491 state.vscroll.set_max_offset(
492 state
493 .len_lines()
494 .saturating_sub(state.inner.height as upos_type) as usize,
495 );
496 }
497 state.vscroll.set_page_len(state.inner.height as usize);
498
499 if state.scroll_to_cursor.get() {
500 state.scroll_to_pos(state.cursor());
501 }
502
503 ScrollArea::new()
505 .block(widget.block.as_ref())
506 .h_scroll(widget.hscroll.as_ref())
507 .v_scroll(widget.vscroll.as_ref())
508 .style(style)
509 .render(
510 area,
511 buf,
512 &mut ScrollAreaState::new()
513 .h_scroll(&mut state.hscroll)
514 .v_scroll(&mut state.vscroll),
515 );
516
517 if state.inner.width == 0 || state.inner.height == 0 {
518 return;
520 }
521 if state.vscroll.offset() > state.value.len_lines() as usize {
522 return;
524 }
525
526 let (shift_left, sub_row_offset, start_row) = state.clean_offset();
527 let page_rows = start_row
528 ..min(
529 start_row + state.inner.height as upos_type,
530 state.value.len_lines(),
531 );
532 let page_bytes = state
533 .try_bytes_at_range(TextRange::new(
534 (sub_row_offset, page_rows.start),
535 (0, page_rows.end),
536 ))
537 .expect("valid_rows");
538 let selection = state.selection();
540 let cursor = state.cursor();
541 let mut styles = Vec::new();
542
543 let mut screen_pos = (0, 0);
544 for g in state
545 .glyphs2(shift_left, sub_row_offset, page_rows)
546 .expect("valid_offset")
547 {
548 screen_pos = g.screen_pos();
550
551 if screen_pos.1 >= state.inner.height {
552 break;
553 }
554
555 if g.screen_width() > 0 {
556 let mut style = style;
557 state
559 .value
560 .styles_at_page(g.text_bytes().start, page_bytes.clone(), &mut styles);
561 for style_nr in &styles {
562 if let Some(s) = widget.text_style.get(style_nr) {
563 style = style.patch(*s);
564 }
565 }
566 if cursor_type == CursorType::RenderedCursor {
567 if focused && selection.is_empty() && g.pos() == cursor {
568 style = cursor_style;
569 }
570 }
571 if selection.contains_pos(g.pos()) {
573 style = style.patch(select_style);
574 };
575
576 if let Some(cell) =
578 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
579 {
580 cell.set_symbol(g.glyph());
581 cell.set_style(style);
582 }
583 for d in 1..g.screen_width() {
585 if let Some(cell) = buf.cell_mut((
586 state.inner.x + screen_pos.0 + d,
587 state.inner.y + screen_pos.1,
588 )) {
589 cell.reset();
590 cell.set_style(style);
591 }
592 }
593 } else {
594 if cursor_type == CursorType::RenderedCursor {
595 if focused && selection.is_empty() && g.line_break() && g.pos() == cursor {
596 if let Some(cell) =
597 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
598 {
599 cell.set_symbol(" ");
600 cell.set_style(cursor_style);
601 }
602 }
603 }
604 }
605 }
606
607 if cursor_type == CursorType::RenderedCursor {
608 if focused
609 && selection.is_empty()
610 && cursor == TextPosition::new(0, state.len_lines().saturating_sub(1))
611 {
612 let yy = if state.is_empty() { 0 } else { 1 };
613 if let Some(cell) = buf.cell_mut((state.inner.x, state.inner.y + screen_pos.1 + yy)) {
614 cell.set_style(cursor_style);
615 }
616 }
617 } else if cursor_type == CursorType::TerminalCursor {
618 state.screen_cursor = state.pos_to_screen(state.cursor());
619 } else {
620 state.screen_cursor = None;
621 }
622}
623
624impl Default for TextAreaState {
625 fn default() -> Self {
626 #[cfg(windows)]
627 const LINE_ENDING: &str = "\r\n";
628
629 #[cfg(not(windows))]
630 const LINE_ENDING: &str = "\n";
631
632 let mut s = Self {
633 area: Default::default(),
634 inner: Default::default(),
635 rendered: Default::default(),
636 screen_cursor: Default::default(),
637 hscroll: Default::default(),
638 vscroll: Default::default(),
639 sub_row_offset: 0,
640 dark_offset: Default::default(),
641 scroll_to_cursor: Default::default(),
642 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
643 move_col: Default::default(),
644 auto_indent: true,
645 auto_quote: true,
646 text_wrap: TextWrap::Shift,
647 newline: LINE_ENDING.to_string(),
648 tab_width: 8,
649 expand_tabs: true,
650 focus: Default::default(),
651 mouse: Default::default(),
652 non_exhaustive: NonExhaustive,
653 };
654 s.hscroll.set_max_offset(255);
655 s.hscroll.set_overscroll_by(Some(16384));
656 s
657 }
658}
659
660impl HasFocus for TextAreaState {
661 fn build(&self, builder: &mut FocusBuilder) {
662 builder.leaf_widget(self);
663 }
664
665 fn focus(&self) -> FocusFlag {
666 self.focus.clone()
667 }
668
669 fn area(&self) -> Rect {
670 self.area
671 }
672
673 fn navigable(&self) -> Navigation {
674 Navigation::Reach
675 }
676}
677
678impl TextAreaState {
679 #[inline]
681 pub fn new() -> Self {
682 Self::default()
683 }
684
685 #[inline]
687 pub fn named(name: &str) -> Self {
688 Self {
689 focus: FocusFlag::new().with_name(name),
690 ..Default::default()
691 }
692 }
693
694 #[inline]
700 pub fn set_newline(&mut self, br: impl Into<String>) {
701 self.newline = br.into();
702 }
703
704 #[inline]
706 pub fn newline(&self) -> &str {
707 &self.newline
708 }
709
710 #[inline]
712 pub fn set_auto_indent(&mut self, indent: bool) {
713 self.auto_indent = indent;
714 }
715
716 #[inline]
718 pub fn set_auto_quote(&mut self, quote: bool) {
719 self.auto_quote = quote;
720 }
721
722 #[inline]
724 pub fn set_tab_width(&mut self, tabs: u32) {
725 self.tab_width = tabs;
726 }
727
728 #[inline]
730 pub fn tab_width(&self) -> u32 {
731 self.tab_width
732 }
733
734 #[inline]
736 pub fn set_expand_tabs(&mut self, expand: bool) {
737 self.expand_tabs = expand;
738 }
739
740 #[inline]
742 pub fn expand_tabs(&self) -> bool {
743 self.expand_tabs
744 }
745
746 #[inline]
748 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
749 self.value.set_glyph_ctrl(show_ctrl);
750 }
751
752 pub fn show_ctrl(&self) -> bool {
754 self.value.glyph_ctrl()
755 }
756
757 #[inline]
760 pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
761 self.value.set_wrap_ctrl(wrap_ctrl);
762 }
763
764 pub fn wrap_ctrl(&self) -> bool {
767 self.value.wrap_ctrl()
768 }
769
770 pub fn set_text_wrap(&mut self, text_wrap: TextWrap) {
783 self.text_wrap = text_wrap;
784 }
785
786 pub fn text_wrap(&self) -> TextWrap {
788 self.text_wrap
789 }
790
791 #[inline]
801 pub fn set_move_col(&mut self, col: Option<i16>) {
802 self.move_col = col;
803 }
804
805 #[inline]
807 pub fn move_col(&mut self) -> Option<i16> {
808 self.move_col
809 }
810}
811
812impl TextAreaState {
813 #[inline]
816 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
817 match clip {
818 None => self.value.set_clipboard(None),
819 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
820 }
821 }
822
823 #[inline]
826 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
827 self.value.clipboard()
828 }
829
830 #[inline]
832 pub fn copy_to_clip(&mut self) -> bool {
833 let Some(clip) = self.value.clipboard() else {
834 return false;
835 };
836
837 _ = clip.set_string(self.selected_text().as_ref());
838 false
839 }
840
841 #[inline]
843 pub fn cut_to_clip(&mut self) -> bool {
844 let Some(clip) = self.value.clipboard() else {
845 return false;
846 };
847
848 match clip.set_string(self.selected_text().as_ref()) {
849 Ok(_) => self.delete_range(self.selection()),
850 Err(_) => false,
851 }
852 }
853
854 #[inline]
856 pub fn paste_from_clip(&mut self) -> bool {
857 let Some(clip) = self.value.clipboard() else {
858 return false;
859 };
860
861 if let Ok(text) = clip.get_string() {
862 self.insert_str(text)
863 } else {
864 false
865 }
866 }
867}
868
869impl TextAreaState {
870 #[inline]
877 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
878 match undo {
879 None => self.value.set_undo_buffer(None),
880 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
881 }
882 }
883
884 #[inline]
886 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
887 self.value.undo_buffer()
888 }
889
890 #[inline]
892 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
893 self.value.undo_buffer_mut()
894 }
895
896 #[inline]
902 pub fn begin_undo_seq(&mut self) {
903 self.value.begin_undo_seq()
904 }
905
906 #[inline]
908 pub fn end_undo_seq(&mut self) {
909 self.value.end_undo_seq()
910 }
911
912 #[inline]
917 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
918 self.value.recent_replay_log()
919 }
920
921 #[inline]
925 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
926 self.value.replay_log(replay)
927 }
928
929 #[inline]
931 pub fn undo(&mut self) -> bool {
932 self.value.undo()
933 }
934
935 #[inline]
937 pub fn redo(&mut self) -> bool {
938 self.value.redo()
939 }
940}
941
942impl TextAreaState {
943 #[inline]
960 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
961 self.value.set_styles(styles);
962 }
963
964 #[inline]
975 pub fn set_range_styles(&mut self, styles: Vec<(TextRange, usize)>) -> Result<(), TextError> {
976 let mut mapped = Vec::with_capacity(styles.len());
977 for (r, s) in styles {
978 let rr = self.value.bytes_at_range(r)?;
979 mapped.push((rr, s));
980 }
981 self.value.set_styles(mapped);
982 Ok(())
983 }
984
985 #[inline]
990 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
991 self.value.add_style(range, style);
992 }
993
994 #[inline]
998 pub fn add_range_style(
999 &mut self,
1000 range: impl Into<TextRange>,
1001 style: usize,
1002 ) -> Result<(), TextError> {
1003 let r = self.value.bytes_at_range(range.into())?;
1004 self.value.add_style(r, style);
1005 Ok(())
1006 }
1007
1008 #[inline]
1010 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
1011 self.value.remove_style(range, style);
1012 }
1013
1014 #[inline]
1016 pub fn remove_style_fully(&mut self, style: usize) {
1017 self.value.remove_style_fully(style);
1018 }
1019
1020 #[inline]
1022 pub fn remove_range_style(
1023 &mut self,
1024 range: impl Into<TextRange>,
1025 style: usize,
1026 ) -> Result<(), TextError> {
1027 let r = self.value.bytes_at_range(range.into())?;
1028 self.value.remove_style(r, style);
1029 Ok(())
1030 }
1031
1032 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
1034 self.value.styles_in(range, buf)
1035 }
1036
1037 pub fn styles_in_match(
1039 &self,
1040 range: Range<usize>,
1041 style: usize,
1042 buf: &mut Vec<(Range<usize>, usize)>,
1043 ) {
1044 self.value.styles_in_match(range, style, buf);
1045 }
1046
1047 #[inline]
1049 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
1050 self.value.styles_at(byte_pos, buf)
1051 }
1052
1053 #[inline]
1056 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
1057 self.value.styles_at_match(byte_pos, style)
1058 }
1059
1060 #[inline]
1062 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
1063 self.value.styles()
1064 }
1065}
1066
1067impl TextAreaState {
1068 #[inline]
1070 pub fn offset(&self) -> (upos_type, upos_type) {
1071 (
1072 self.hscroll.offset() as upos_type,
1073 self.vscroll.offset() as upos_type,
1074 )
1075 }
1076
1077 #[inline]
1082 pub fn set_offset(&mut self, offset: (upos_type, upos_type)) -> bool {
1083 self.scroll_to_cursor.set(false);
1084 let c = self.hscroll.set_offset(offset.0 as usize);
1085 let r = self.vscroll.set_offset(offset.1 as usize);
1086 r || c
1087 }
1088
1089 pub fn set_sub_row_offset(&mut self, sub_row_offset: upos_type) -> bool {
1101 self.scroll_to_cursor.set(false);
1102 let old = self.sub_row_offset;
1103 self.sub_row_offset = sub_row_offset;
1104 sub_row_offset != old
1105 }
1106
1107 pub fn sub_row_offset(&self) -> upos_type {
1112 self.sub_row_offset
1113 }
1114
1115 fn clean_offset(&self) -> (upos_type, upos_type, upos_type) {
1120 let ox = self.hscroll.offset as upos_type;
1121 let mut oy = self.vscroll.offset as upos_type;
1122
1123 if oy >= self.len_lines() {
1125 oy = 0;
1126 }
1127
1128 match self.text_wrap {
1129 TextWrap::Shift => (ox, 0, oy),
1130 TextWrap::Hard | TextWrap::Word(_) => {
1131 if let Ok(max_col) = self.try_line_width(oy) {
1133 (0, min(self.sub_row_offset, max_col), oy)
1134 } else {
1135 (0, 0, oy)
1136 }
1137 }
1138 }
1139 }
1140
1141 #[inline]
1143 pub fn cursor(&self) -> TextPosition {
1144 self.value.cursor()
1145 }
1146
1147 #[inline]
1149 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
1150 self.scroll_cursor_to_visible();
1151 self.value.set_cursor(cursor.into(), extend_selection)
1152 }
1153
1154 #[inline]
1156 pub fn anchor(&self) -> TextPosition {
1157 self.value.anchor()
1158 }
1159
1160 #[inline]
1162 pub fn has_selection(&self) -> bool {
1163 self.value.has_selection()
1164 }
1165
1166 #[inline]
1168 pub fn selection(&self) -> TextRange {
1169 self.value.selection()
1170 }
1171
1172 #[inline]
1175 pub fn set_selection(
1176 &mut self,
1177 anchor: impl Into<TextPosition>,
1178 cursor: impl Into<TextPosition>,
1179 ) -> bool {
1180 self.scroll_cursor_to_visible();
1181 self.value.set_selection(anchor.into(), cursor.into())
1182 }
1183
1184 #[inline]
1187 pub fn select_all(&mut self) -> bool {
1188 self.scroll_cursor_to_visible();
1189 self.value.select_all()
1190 }
1191
1192 #[inline]
1194 pub fn selected_text(&self) -> Cow<'_, str> {
1195 self.value
1196 .str_slice(self.value.selection())
1197 .expect("valid_selection")
1198 }
1199}
1200
1201impl TextAreaState {
1202 #[inline]
1204 pub fn clear(&mut self) -> bool {
1205 if !self.is_empty() {
1206 self.value.clear();
1207 true
1208 } else {
1209 false
1210 }
1211 }
1212
1213 #[inline]
1217 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1218 self.scroll_to_cursor.set(false);
1219 self.vscroll.set_offset(0);
1220 self.hscroll.set_offset(0);
1221 self.set_sub_row_offset(0);
1222 self.set_move_col(None);
1223
1224 self.value.set_text(TextRope::new_text(s.as_ref()));
1225 }
1226
1227 #[inline]
1229 pub fn text(&self) -> String {
1230 self.value.text().string()
1231 }
1232
1233 #[inline]
1236 pub fn set_rope(&mut self, r: Rope) {
1237 self.scroll_to_cursor.set(false);
1238 self.vscroll.set_offset(0);
1239 self.hscroll.set_offset(0);
1240 self.set_sub_row_offset(0);
1241 self.set_move_col(None);
1242
1243 self.value.set_text(TextRope::new_rope(r));
1244 }
1245
1246 #[inline]
1248 pub fn rope(&self) -> &Rope {
1249 self.value.text().rope()
1250 }
1251
1252 #[inline]
1256 pub fn set_value<S: AsRef<str>>(&mut self, s: S) {
1257 self.set_text(s);
1258 }
1259
1260 #[inline]
1262 pub fn value(&self) -> String {
1263 self.text()
1264 }
1265
1266 #[inline]
1268 pub fn is_empty(&self) -> bool {
1269 self.value.is_empty()
1270 }
1271
1272 #[inline]
1276 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1277 self.value.str_slice_byte(range).expect("valid_range")
1278 }
1279
1280 #[inline]
1282 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1283 self.value.str_slice_byte(range)
1284 }
1285
1286 #[inline]
1290 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
1291 self.value.str_slice(range.into()).expect("valid_range")
1292 }
1293
1294 #[inline]
1296 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
1297 self.value.str_slice(range.into())
1298 }
1299
1300 #[inline]
1302 pub fn len_lines(&self) -> upos_type {
1303 self.value.len_lines()
1304 }
1305
1306 #[inline]
1308 pub fn len_bytes(&self) -> usize {
1309 self.value.len_bytes()
1310 }
1311
1312 #[inline]
1316 pub fn line_width(&self, row: upos_type) -> upos_type {
1317 self.try_line_width(row).expect("valid_row")
1318 }
1319
1320 #[inline]
1322 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1323 self.value.line_width(row)
1324 }
1325
1326 #[inline]
1331 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
1332 self.value.line_at(row).expect("valid_row")
1333 }
1334
1335 #[inline]
1338 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1339 self.value.line_at(row)
1340 }
1341
1342 #[inline]
1346 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
1347 self.value.lines_at(row).expect("valid_row")
1348 }
1349
1350 #[inline]
1352 pub fn try_lines_at(
1353 &self,
1354 row: upos_type,
1355 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1356 self.value.lines_at(row)
1357 }
1358
1359 #[inline]
1364 pub fn line_graphemes(&self, row: upos_type) -> <TextRope as TextStore>::GraphemeIter<'_> {
1365 self.value.line_graphemes(row).expect("valid_row")
1366 }
1367
1368 #[inline]
1371 pub fn try_line_graphemes(
1372 &self,
1373 row: upos_type,
1374 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1375 self.value.line_graphemes(row)
1376 }
1377
1378 #[inline]
1382 pub fn text_graphemes(
1383 &self,
1384 pos: impl Into<TextPosition>,
1385 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1386 self.value.text_graphemes(pos.into()).expect("valid_pos")
1387 }
1388
1389 #[inline]
1391 pub fn try_text_graphemes(
1392 &self,
1393 pos: impl Into<TextPosition>,
1394 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1395 self.value.text_graphemes(pos.into())
1396 }
1397
1398 #[inline]
1402 pub fn graphemes(
1403 &self,
1404 range: impl Into<TextRange>,
1405 pos: impl Into<TextPosition>,
1406 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1407 self.value
1408 .graphemes(range.into(), pos.into())
1409 .expect("valid_args")
1410 }
1411
1412 #[inline]
1414 pub fn try_graphemes(
1415 &self,
1416 range: impl Into<TextRange>,
1417 pos: impl Into<TextPosition>,
1418 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1419 self.value.graphemes(range.into(), pos.into())
1420 }
1421
1422 #[inline]
1427 pub fn byte_at(&self, pos: impl Into<TextPosition>) -> Range<usize> {
1428 self.value.byte_at(pos.into()).expect("valid_pos")
1429 }
1430
1431 #[inline]
1434 pub fn try_byte_at(&self, pos: impl Into<TextPosition>) -> Result<Range<usize>, TextError> {
1435 self.value.byte_at(pos.into())
1436 }
1437
1438 #[inline]
1442 pub fn bytes_at_range(&self, range: impl Into<TextRange>) -> Range<usize> {
1443 self.value
1444 .bytes_at_range(range.into())
1445 .expect("valid_range")
1446 }
1447
1448 #[inline]
1450 pub fn try_bytes_at_range(
1451 &self,
1452 range: impl Into<TextRange>,
1453 ) -> Result<Range<usize>, TextError> {
1454 self.value.bytes_at_range(range.into())
1455 }
1456
1457 #[inline]
1462 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1463 self.value.byte_pos(byte).expect("valid_pos")
1464 }
1465
1466 #[inline]
1469 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1470 self.value.byte_pos(byte)
1471 }
1472
1473 #[inline]
1477 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1478 self.value.byte_range(bytes).expect("valid_range")
1479 }
1480
1481 #[inline]
1483 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1484 self.value.byte_range(bytes)
1485 }
1486}
1487
1488impl TextAreaState {
1489 #[inline]
1495 pub fn insert_char(&mut self, c: char) -> bool {
1496 match c {
1497 '\n' => text_area_op::insert_newline(self),
1498 '\t' => text_area_op::insert_tab(self),
1499 c => text_area_op::insert_char(self, c),
1500 }
1501 }
1502
1503 #[inline]
1509 pub fn insert_tab(&mut self) -> bool {
1510 text_area_op::insert_tab(self)
1511 }
1512
1513 #[inline]
1518 pub fn insert_backtab(&mut self) -> bool {
1519 text_area_op::insert_backtab(self)
1520 }
1521
1522 #[inline]
1525 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1526 text_area_op::insert_str(self, t.as_ref())
1527 }
1528
1529 #[inline]
1534 pub fn insert_newline(&mut self) -> bool {
1535 text_area_op::insert_newline(self)
1536 }
1537
1538 #[inline]
1542 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1543 text_area_op::delete_range(self, range.into()).expect("valid_range")
1544 }
1545
1546 #[inline]
1548 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1549 text_area_op::delete_range(self, range.into())
1550 }
1551
1552 #[inline]
1555 pub fn duplicate_text(&mut self) -> bool {
1556 text_area_op::duplicate_text(self)
1557 }
1558
1559 #[inline]
1562 pub fn delete_line(&mut self) -> bool {
1563 text_area_op::delete_line(self)
1564 }
1565
1566 #[inline]
1569 pub fn delete_next_char(&mut self) -> bool {
1570 text_area_op::delete_next_char(self)
1571 }
1572
1573 #[inline]
1576 pub fn delete_prev_char(&mut self) -> bool {
1577 text_area_op::delete_prev_char(self)
1578 }
1579
1580 #[inline]
1585 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1586 core_op::next_word_start(&self.value, pos.into()).expect("valid_pos")
1587 }
1588
1589 #[inline]
1592 pub fn try_next_word_start(
1593 &self,
1594 pos: impl Into<TextPosition>,
1595 ) -> Result<TextPosition, TextError> {
1596 core_op::next_word_start(&self.value, pos.into())
1597 }
1598
1599 #[inline]
1604 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1605 core_op::next_word_end(&self.value, pos.into()).expect("valid_pos")
1606 }
1607
1608 #[inline]
1611 pub fn try_next_word_end(
1612 &self,
1613 pos: impl Into<TextPosition>,
1614 ) -> Result<TextPosition, TextError> {
1615 core_op::next_word_end(&self.value, pos.into())
1616 }
1617
1618 #[inline]
1626 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1627 core_op::prev_word_start(&self.value, pos.into()).expect("valid_pos")
1628 }
1629
1630 #[inline]
1636 pub fn try_prev_word_start(
1637 &self,
1638 pos: impl Into<TextPosition>,
1639 ) -> Result<TextPosition, TextError> {
1640 core_op::prev_word_start(&self.value, pos.into())
1641 }
1642
1643 #[inline]
1649 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1650 core_op::prev_word_end(&self.value, pos.into()).expect("valid_pos")
1651 }
1652
1653 #[inline]
1657 pub fn try_prev_word_end(
1658 &self,
1659 pos: impl Into<TextPosition>,
1660 ) -> Result<TextPosition, TextError> {
1661 core_op::prev_word_end(&self.value, pos.into())
1662 }
1663
1664 #[inline]
1668 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1669 core_op::is_word_boundary(&self.value, pos.into()).expect("valid_pos")
1670 }
1671
1672 #[inline]
1674 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1675 core_op::is_word_boundary(&self.value, pos.into())
1676 }
1677
1678 #[inline]
1683 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1684 core_op::word_start(&self.value, pos.into()).expect("valid_pos")
1685 }
1686
1687 #[inline]
1690 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1691 core_op::word_start(&self.value, pos.into())
1692 }
1693
1694 #[inline]
1699 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1700 core_op::word_end(&self.value, pos.into()).expect("valid_pos")
1701 }
1702
1703 #[inline]
1706 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1707 core_op::word_end(&self.value, pos.into())
1708 }
1709
1710 #[inline]
1715 pub fn delete_next_word(&mut self) -> bool {
1716 text_area_op::delete_next_word(self)
1717 }
1718
1719 #[inline]
1724 pub fn delete_prev_word(&mut self) -> bool {
1725 text_area_op::delete_prev_word(self)
1726 }
1727
1728 pub fn search(&mut self, re: &str) -> Result<bool, TextError> {
1739 match text_area_op::search(self, re, MATCH_STYLE) {
1740 Ok(r) => Ok(r),
1741 Err(_) => Err(TextError::InvalidSearch),
1742 }
1743 }
1744
1745 pub fn move_to_next_match(&mut self) -> bool {
1747 text_area_op::move_to_next_match(self, MATCH_STYLE)
1748 }
1749
1750 pub fn move_to_prev_match(&mut self) -> bool {
1752 text_area_op::move_to_prev_match(self, MATCH_STYLE)
1753 }
1754
1755 pub fn clear_search(&mut self) {
1757 text_area_op::clear_search(self, MATCH_STYLE)
1758 }
1759
1760 #[inline]
1763 pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1764 text_area_op::move_left(self, n, extend_selection)
1765 }
1766
1767 #[inline]
1770 pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1771 text_area_op::move_right(self, n, extend_selection)
1772 }
1773
1774 #[inline]
1777 pub fn move_up(&mut self, n: u16, extend_selection: bool) -> bool {
1778 text_area_op::move_up(self, n, extend_selection)
1779 }
1780
1781 #[inline]
1784 pub fn move_down(&mut self, n: u16, extend_selection: bool) -> bool {
1785 text_area_op::move_down(self, n, extend_selection)
1786 }
1787
1788 #[inline]
1792 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1793 text_area_op::move_to_line_start(self, extend_selection)
1794 }
1795
1796 #[inline]
1800 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1801 text_area_op::move_to_line_end(self, extend_selection)
1802 }
1803
1804 #[inline]
1806 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1807 text_area_op::move_to_start(self, extend_selection)
1808 }
1809
1810 #[inline]
1812 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1813 text_area_op::move_to_end(self, extend_selection)
1814 }
1815
1816 #[inline]
1818 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1819 text_area_op::move_to_screen_start(self, extend_selection)
1820 }
1821
1822 #[inline]
1824 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1825 text_area_op::move_to_screen_end(self, extend_selection)
1826 }
1827
1828 #[inline]
1830 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1831 text_area_op::move_to_next_word(self, extend_selection)
1832 }
1833
1834 #[inline]
1836 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1837 text_area_op::move_to_prev_word(self, extend_selection)
1838 }
1839}
1840
1841impl HasScreenCursor for TextAreaState {
1842 #[allow(clippy::question_mark)]
1844 fn screen_cursor(&self) -> Option<(u16, u16)> {
1845 if self.is_focused() {
1846 if self.has_selection() {
1847 None
1848 } else {
1849 let Some(scr_cursor) = self.screen_cursor else {
1850 return None;
1851 };
1852
1853 if !(scr_cursor.0 >= self.inner.x
1854 && scr_cursor.0 <= self.inner.right()
1855 && scr_cursor.1 >= self.inner.y
1856 && scr_cursor.1 < self.inner.bottom())
1857 {
1858 return None;
1859 }
1860 Some(scr_cursor)
1861 }
1862 } else {
1863 None
1864 }
1865 }
1866}
1867
1868impl RelocatableState for TextAreaState {
1869 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1870 self.area.relocate(shift, clip);
1872 self.inner.relocate(shift, clip);
1873 self.hscroll.relocate(shift, clip);
1874 self.vscroll.relocate(shift, clip);
1875 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1876 if let Some(screen_cursor) = self.screen_cursor {
1877 self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
1878 }
1879 }
1880}
1881
1882impl TextAreaState {
1883 fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
1885 match self.text_wrap {
1886 TextWrap::Shift => (
1887 TextWrap2::Shift,
1888 shift_left,
1889 shift_left + self.rendered.width as upos_type,
1890 shift_left + self.rendered.width as upos_type,
1891 ),
1892 TextWrap::Hard => (
1893 TextWrap2::Hard,
1894 0,
1895 self.rendered.width as upos_type,
1896 self.rendered.width as upos_type,
1897 ),
1898 TextWrap::Word(margin) => (
1899 TextWrap2::Word,
1900 0,
1901 self.rendered.width as upos_type,
1902 self.rendered.width.saturating_sub(margin) as upos_type,
1903 ),
1904 }
1905 }
1906
1907 fn fill_cache(
1910 &self,
1911 shift_left: upos_type,
1912 sub_row_offset: upos_type,
1913 rows: Range<upos_type>,
1914 ) -> Result<(), TextError> {
1915 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1916 self.value.fill_cache(
1917 self.rendered,
1918 sub_row_offset,
1919 rows,
1920 self.tab_width,
1921 text_wrap,
1922 self.wrap_ctrl() | self.show_ctrl(),
1923 left_margin,
1924 right_margin,
1925 word_margin,
1926 )
1927 }
1928
1929 fn glyphs2(
1931 &self,
1932 shift_left: upos_type,
1933 sub_row_offset: upos_type,
1934 rows: Range<upos_type>,
1935 ) -> Result<GlyphIter2<'_, <TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
1936 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1937 self.value.glyphs2(
1938 self.rendered,
1939 sub_row_offset,
1940 rows,
1941 self.tab_width,
1942 text_wrap,
1943 self.wrap_ctrl() | self.show_ctrl(),
1944 left_margin,
1945 right_margin,
1946 word_margin,
1947 )
1948 }
1949
1950 fn stc_fill_screen_cache(&self, scr: (upos_type, upos_type, upos_type)) {
1954 let y2 = scr.1 + scr.2;
1955 self.fill_cache(0, scr.0, scr.1..min(y2, self.len_lines()))
1956 .expect("valid-rows");
1957 }
1958
1959 fn stc_screen_row(
1966 &self,
1967 scr: (upos_type, upos_type, upos_type),
1968 pos: TextPosition,
1969 ) -> Option<upos_type> {
1970 if pos < TextPosition::new(scr.0, scr.1) {
1971 return None;
1972 }
1973
1974 let line_breaks = self.value.cache().line_break.borrow();
1975 let range_start = TextPosition::new(scr.0, scr.1);
1976 let y2 = scr.1 + scr.2;
1977 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
1978
1979 let mut start_pos = TextPosition::new(scr.0, scr.1);
1980 let mut scr_row = 0;
1981 for (_key, cache) in line_breaks.range(range_start..range_end) {
1982 if pos < cache.start_pos {
1983 return Some(scr_row);
1984 }
1985 scr_row += 1;
1986 start_pos = cache.start_pos;
1987 }
1988
1989 if pos == start_pos {
1991 return Some(scr_row);
1992 }
1993
1994 None
1995 }
1996
1997 fn stc_sub_row_offset(
2004 &self,
2005 scr: (upos_type, upos_type, upos_type),
2006 mut scr_row: upos_type,
2007 ) -> (upos_type, upos_type) {
2008 let line_breaks = self.value.cache().line_break.borrow();
2009 let range_start = TextPosition::new(scr.0, scr.1);
2010 let y2 = scr.1 + scr.2;
2011 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
2012
2013 let mut start_pos = (scr.0, scr.1);
2014 for (_key, cache) in line_breaks.range(range_start..range_end) {
2015 if scr_row == 0 {
2016 return start_pos;
2017 }
2018 scr_row -= 1;
2019 start_pos = (cache.start_pos.x, cache.start_pos.y);
2020 }
2021
2022 start_pos
2024 }
2025}
2026
2027impl TextAreaState {
2028 #[allow(clippy::needless_return)]
2030 pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
2031 let scr_pos = (
2032 scr_pos.0 + self.dark_offset.0 as i16,
2033 scr_pos.1 + self.dark_offset.1 as i16,
2034 );
2035
2036 match self.text_wrap {
2037 TextWrap::Shift => {
2038 let (ox, _, oy) = self.clean_offset();
2039
2040 if oy >= self.len_lines() {
2041 return None;
2042 }
2043
2044 if scr_pos.1 < 0 {
2045 return Some(TextPosition::new(
2047 0,
2048 oy.saturating_add_signed(scr_pos.1 as ipos_type),
2049 ));
2050 } else if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
2051 return Some(TextPosition::new(0, self.len_lines().saturating_sub(1)));
2053 }
2054
2055 let pos_y = oy + scr_pos.1 as upos_type;
2056
2057 if scr_pos.0 < 0 {
2058 return Some(TextPosition::new(
2059 ox.saturating_add_signed(scr_pos.0 as ipos_type),
2060 pos_y,
2061 ));
2062 } else if scr_pos.0 as u16 >= self.rendered.width {
2063 return Some(TextPosition::new(
2064 min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
2065 pos_y,
2066 ));
2067 } else {
2068 let mut start_pos = TextPosition::new(0, pos_y);
2069 for g in self
2070 .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
2071 .expect("valid-position")
2072 {
2073 if g.contains_screen_x(scr_pos.0 as u16) {
2074 return Some(TextPosition::new(g.pos().x, pos_y));
2075 }
2076 start_pos = g.pos();
2077 }
2078 Some(start_pos)
2079 }
2080 }
2081 TextWrap::Hard | TextWrap::Word(_) => {
2082 let (_, sub_row_offset, oy) = self.clean_offset();
2083
2084 if oy >= self.len_lines() {
2085 return None;
2086 }
2087
2088 if scr_pos.1 < 0 {
2089 let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2098
2099 self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2100
2101 let n_start_pos = 'f: {
2102 let line_break = self.value.cache().line_break.borrow();
2103 let start_range = TextPosition::new(0, ry);
2104 let end_range = TextPosition::new(sub_row_offset, oy);
2105
2106 let mut nrows = scr_pos.1.unsigned_abs();
2107 for (_break_pos, cache) in line_break.range(start_range..end_range).rev() {
2108 if nrows == 0 {
2109 break 'f cache.start_pos;
2110 }
2111 nrows -= 1;
2112 }
2113 TextPosition::new(0, ry)
2114 };
2115
2116 if scr_pos.0 < 0 {
2118 return Some(n_start_pos);
2119 }
2120
2121 let min_row = n_start_pos.y;
2122 let max_row = min(n_start_pos.y + 1, self.len_lines());
2123 for g in self
2124 .glyphs2(0, n_start_pos.x, min_row..max_row)
2125 .expect("valid-rows")
2126 {
2127 if g.contains_screen_x(scr_pos.0 as u16) {
2128 return Some(g.pos());
2129 }
2130 }
2131
2132 return Some(n_start_pos);
2134 } else {
2135 let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2136
2137 let n_start_pos = if scr_pos.1 == 0 {
2139 TextPosition::new(sub_row_offset, oy)
2140 } else {
2141 self.fill_cache(
2143 0,
2144 sub_row_offset,
2145 oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2146 )
2147 .expect("valid-rows");
2148
2149 'f: {
2150 let text_range = self.value.cache().line_break.borrow();
2151 let start_range = TextPosition::new(sub_row_offset, oy);
2152 let end_range = TextPosition::new(0, self.len_lines());
2153
2154 let mut nrows = scr_pos.1 - 1;
2155 let mut start_pos = TextPosition::new(sub_row_offset, oy);
2156 for (_break_pos, cache) in text_range.range(start_range..end_range) {
2157 if nrows == 0 {
2158 break 'f cache.start_pos;
2159 }
2160 start_pos = cache.start_pos;
2161 nrows -= 1;
2162 }
2163 start_pos
2164 }
2165 };
2166
2167 let min_row = n_start_pos.y;
2168 let max_row = min(n_start_pos.y + 1, self.len_lines());
2169 for g in self
2170 .glyphs2(0, n_start_pos.x, min_row..max_row)
2171 .expect("valid-rows")
2172 {
2173 if g.contains_screen_x(scr_pos.0) {
2174 return Some(g.pos());
2175 }
2176 }
2177
2178 return Some(n_start_pos);
2180 }
2181 }
2182 }
2183 }
2184
2185 pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
2187 let scr_pos = (
2188 scr_pos.0 as i16 - self.inner.x as i16,
2189 scr_pos.1 as i16 - self.inner.y as i16,
2190 );
2191 self.relative_screen_to_pos(scr_pos)
2192 }
2193
2194 #[allow(clippy::explicit_counter_loop)]
2204 pub fn pos_to_relative_screen(&self, pos: impl Into<TextPosition>) -> Option<(i16, i16)> {
2205 let pos = pos.into();
2206 match self.text_wrap {
2207 TextWrap::Shift => {
2208 let (ox, _, oy) = self.clean_offset();
2209
2210 if oy > self.len_lines() {
2211 return None;
2212 }
2213 if pos.y < oy {
2214 return None;
2215 }
2216 if pos.y > self.len_lines() {
2217 return None;
2218 }
2219 if pos.y - oy >= self.rendered.height as u32 {
2220 return None;
2221 }
2222
2223 let screen_y = (pos.y - oy) as u16;
2224
2225 let screen_x = 'f: {
2226 for g in self
2227 .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2228 .expect("valid-row")
2229 {
2230 if g.pos().x == pos.x {
2231 break 'f g.screen_pos().0;
2232 } else if g.line_break() {
2233 break 'f g.screen_pos().0;
2234 }
2235 }
2236 0
2238 };
2239 assert!(screen_x <= self.rendered.width);
2240
2241 Some((
2242 screen_x as i16 - self.dark_offset.0 as i16,
2243 screen_y as i16 - self.dark_offset.1 as i16,
2244 ))
2245 }
2246 TextWrap::Hard | TextWrap::Word(_) => {
2247 let (_, sub_row_offset, oy) = self.clean_offset();
2248
2249 if oy > self.len_lines() {
2250 return None;
2251 }
2252 if pos.y < oy {
2253 return None;
2254 }
2255 if pos.y > self.len_lines() {
2256 return None;
2257 }
2258
2259 let page = self.rendered.height as upos_type;
2260 let scr = (sub_row_offset, oy, page);
2261 self.stc_fill_screen_cache(scr);
2262 let (screen_y, start_pos) = if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2263 if pos_row >= page {
2264 return None;
2266 }
2267 let start_pos = self.stc_sub_row_offset(scr, pos_row);
2268 (pos_row, start_pos)
2269 } else {
2270 return None;
2272 };
2273
2274 let screen_x = 'f: {
2275 for g in self
2276 .glyphs2(
2277 0,
2278 start_pos.0,
2279 start_pos.1..min(start_pos.1 + 1, self.len_lines()),
2280 )
2281 .expect("valid-row")
2282 {
2283 if g.pos().x == pos.x {
2284 break 'f g.screen_pos().0;
2285 } else if g.line_break() {
2286 break 'f g.screen_pos().0;
2287 }
2288 }
2289 0
2291 };
2292 assert!(screen_x <= self.rendered.width);
2293
2294 let scr = (
2295 screen_x as i16 - self.dark_offset.0 as i16,
2296 screen_y as i16 - self.dark_offset.1 as i16,
2297 );
2298 Some(scr)
2299 }
2300 }
2301 }
2302
2303 #[inline]
2306 pub fn pos_to_screen(&self, pos: impl Into<TextPosition>) -> Option<(u16, u16)> {
2307 let scr_pos = self.pos_to_relative_screen(pos.into())?;
2308 if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
2309 Some((
2310 (scr_pos.0 + self.inner.x as i16) as u16,
2311 (scr_pos.1 + self.inner.y as i16) as u16,
2312 ))
2313 } else {
2314 None
2315 }
2316 }
2317
2318 pub fn pos_to_line_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
2320 let pos = pos.into();
2321 match self.text_wrap {
2322 TextWrap::Shift => {
2323 TextPosition::new(0, pos.y)
2325 }
2326 TextWrap::Hard | TextWrap::Word(_) => {
2327 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2328 .expect("valid-row");
2329
2330 let mut start_pos = TextPosition::new(0, pos.y);
2331 for (break_pos, _) in self.value.cache().line_break.borrow().range(
2332 TextPosition::new(0, pos.y)
2333 ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
2334 ) {
2335 if pos >= start_pos && &pos <= break_pos {
2336 break;
2337 }
2338 start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2339 }
2340
2341 start_pos
2342 }
2343 }
2344 }
2345
2346 pub fn pos_to_line_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
2348 let pos = pos.into();
2349
2350 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2351 .expect("valid-row");
2352
2353 let mut end_pos = TextPosition::new(0, pos.y);
2354 for (break_pos, _) in self
2355 .value
2356 .cache()
2357 .line_break
2358 .borrow()
2359 .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2360 {
2361 if pos >= end_pos && &pos <= break_pos {
2362 end_pos = *break_pos;
2363 break;
2364 }
2365 end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2366 }
2367
2368 end_pos
2369 }
2370
2371 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2377 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2378 return false;
2379 };
2380 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2381 self.set_move_col(Some(scr_cursor.0));
2382 }
2383 self.set_cursor(cursor, extend_selection)
2384 }
2385
2386 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2393 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2394 return false;
2395 };
2396
2397 let anchor = self.anchor();
2398 let cursor = if cursor < anchor {
2399 self.word_start(cursor)
2400 } else {
2401 self.word_end(cursor)
2402 };
2403
2404 if !self.is_word_boundary(anchor) {
2406 if cursor < anchor {
2407 self.set_cursor(self.word_end(anchor), false);
2408 } else {
2409 self.set_cursor(self.word_start(anchor), false);
2410 }
2411 }
2412
2413 self.set_cursor(cursor, extend_selection)
2414 }
2415}
2416
2417impl TextAreaState {
2418 pub fn vertical_max_offset(&self) -> upos_type {
2423 self.vscroll.max_offset() as upos_type
2424 }
2425
2426 pub fn vertical_offset(&self) -> upos_type {
2428 self.vscroll.offset() as upos_type
2429 }
2430
2431 pub fn vertical_page(&self) -> upos_type {
2433 self.vscroll.page_len() as upos_type
2434 }
2435
2436 pub fn vertical_scroll(&self) -> upos_type {
2438 self.vscroll.scroll_by() as upos_type
2439 }
2440
2441 pub fn horizontal_max_offset(&self) -> upos_type {
2450 self.hscroll.max_offset() as upos_type
2451 }
2452
2453 pub fn horizontal_offset(&self) -> upos_type {
2455 self.hscroll.offset() as upos_type
2456 }
2457
2458 pub fn horizontal_page(&self) -> upos_type {
2460 self.hscroll.page_len() as upos_type
2461 }
2462
2463 pub fn horizontal_scroll(&self) -> upos_type {
2465 self.hscroll.scroll_by() as upos_type
2466 }
2467
2468 pub fn set_vertical_offset(&mut self, row_offset: upos_type) -> bool {
2475 self.scroll_to_cursor.set(false);
2476 self.sub_row_offset = 0;
2477 self.vscroll.set_offset(row_offset as usize)
2478 }
2479
2480 pub fn set_horizontal_offset(&mut self, col_offset: upos_type) -> bool {
2489 self.scroll_to_cursor.set(false);
2490 self.hscroll.set_offset(col_offset as usize)
2491 }
2492
2493 pub fn scroll_cursor_to_visible(&mut self) {
2499 self.scroll_to_cursor.set(true);
2500 }
2501
2502 pub fn scroll_to_pos(&mut self, pos: impl Into<TextPosition>) -> bool {
2508 let old_offset = self.clean_offset();
2509
2510 let pos = pos.into();
2511
2512 'f: {
2513 match self.text_wrap {
2514 TextWrap::Shift => {
2515 let (ox, _, oy) = old_offset;
2516
2517 let height = self.rendered.height as upos_type;
2518 let width = self.rendered.width as upos_type;
2519 let width = if self.show_ctrl() || self.wrap_ctrl() {
2520 width.saturating_sub(1)
2521 } else {
2522 width
2523 };
2524
2525 let noy = if pos.y < oy.saturating_sub(height) {
2526 pos.y.saturating_sub(height * 4 / 10)
2527 } else if pos.y < oy {
2528 pos.y
2529 } else if pos.y >= oy + 2 * height {
2530 pos.y.saturating_sub(height * 6 / 10)
2531 } else if pos.y >= oy + height {
2532 pos.y.saturating_sub(height.saturating_sub(1))
2533 } else {
2534 oy
2535 };
2536
2537 let nox = if pos.x < ox {
2538 pos.x
2539 } else if pos.x >= ox + width {
2540 pos.x.saturating_sub(width) + 1
2541 } else {
2542 ox
2543 };
2544
2545 self.set_offset((nox, noy));
2546 self.set_sub_row_offset(0);
2547 }
2548 TextWrap::Hard | TextWrap::Word(_) => {
2549 let (_ox, sub_row_offset, oy) = old_offset;
2550 let page = self.rendered.height as upos_type;
2551
2552 let scr = (0, oy.saturating_sub(page), 3 * page);
2554 self.stc_fill_screen_cache(scr);
2555 if let Some(off_row) =
2556 self.stc_screen_row(scr, TextPosition::new(sub_row_offset, oy))
2557 {
2558 if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2559 if pos_row < off_row && pos_row >= off_row.saturating_sub(page) {
2560 let noff_row = pos_row;
2561 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2562 self.set_offset((0, noy));
2563 self.set_sub_row_offset(nsub_row_offset);
2564 break 'f;
2565 } else if pos_row >= off_row + page && pos_row < off_row + 2 * page {
2566 let noff_row = pos_row.saturating_sub(page.saturating_sub(1));
2567 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2568 self.set_offset((0, noy));
2569 self.set_sub_row_offset(nsub_row_offset);
2570 break 'f;
2571 } else if pos_row >= off_row && pos_row < off_row + page {
2572 break 'f;
2573 }
2574 }
2575 }
2576
2577 let alt_scr = (0, pos.y.saturating_sub(page), 3 * page);
2579 self.stc_fill_screen_cache(alt_scr);
2580 if let Some(alt_scr_row) = self.stc_screen_row(alt_scr, pos) {
2581 let noff_row = alt_scr_row.saturating_sub(page * 5 / 10);
2582 let (nsub_row_offset, noy) = self.stc_sub_row_offset(alt_scr, noff_row);
2583 self.set_offset((0, noy));
2584 self.set_sub_row_offset(nsub_row_offset);
2585 } else {
2586 self.set_offset((0, pos.y));
2587 self.set_sub_row_offset(0);
2588 }
2589 }
2590 }
2591 }
2592
2593 old_offset != self.clean_offset()
2594 }
2595
2596 pub fn scroll_to_row(&mut self, pos: upos_type) -> bool {
2605 self.scroll_to_cursor.set(false);
2606
2607 match self.text_wrap {
2608 TextWrap::Shift => self.vscroll.scroll_to_pos(pos as usize),
2609 TextWrap::Hard | TextWrap::Word(_) => self
2610 .vscroll
2611 .set_offset(self.vscroll.limited_offset(pos as usize)),
2612 }
2613 }
2614
2615 pub fn scroll_to_col(&mut self, pos: upos_type) -> bool {
2623 self.scroll_to_cursor.set(false);
2624 self.hscroll.set_offset(pos as usize)
2625 }
2626
2627 pub fn scroll_up(&mut self, delta: upos_type) -> bool {
2633 if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2634 self.sub_row_offset = pos.x;
2635 self.vscroll.set_offset(pos.y as usize);
2636 true
2637 } else {
2638 false
2639 }
2640 }
2641
2642 pub fn scroll_down(&mut self, delta: upos_type) -> bool {
2648 if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2649 self.sub_row_offset = pos.x;
2650 self.vscroll.set_offset(pos.y as usize);
2651 true
2652 } else {
2653 false
2654 }
2655 }
2656
2657 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
2669 self.hscroll
2670 .set_offset(self.hscroll.offset.saturating_add(delta as usize))
2671 }
2672
2673 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
2685 self.hscroll
2686 .set_offset(self.hscroll.offset.saturating_sub(delta as usize))
2687 }
2688}
2689
2690impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2691 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2692 fn tc(r: bool) -> TextOutcome {
2694 if r {
2695 TextOutcome::TextChanged
2696 } else {
2697 TextOutcome::Unchanged
2698 }
2699 }
2700
2701 let mut r =
2702 if self.is_focused() {
2703 match event {
2704 ct_event!(key press c)
2705 | ct_event!(key press SHIFT-c)
2706 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2707 ct_event!(keycode press Tab) => {
2708 tc(if !self.focus.gained() {
2710 self.insert_tab()
2711 } else {
2712 false
2713 })
2714 }
2715 ct_event!(keycode press SHIFT-BackTab) => {
2716 tc(if !self.focus.gained() {
2718 self.insert_backtab()
2719 } else {
2720 false
2721 })
2722 }
2723 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2724 ct_event!(keycode press Backspace)
2725 | ct_event!(keycode press SHIFT-Backspace) => tc(self.delete_prev_char()),
2726 ct_event!(keycode press Delete) | ct_event!(keycode press SHIFT-Delete) => {
2727 tc(self.delete_next_char())
2728 }
2729 ct_event!(keycode press CONTROL-Backspace)
2730 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2731 ct_event!(keycode press CONTROL-Delete)
2732 | ct_event!(keycode press ALT-Delete) => tc(self.delete_next_word()),
2733 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2734 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2735 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2736 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2737 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2738 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2739
2740 ct_event!(key release _)
2741 | ct_event!(key release SHIFT-_)
2742 | ct_event!(key release CONTROL_ALT-_)
2743 | ct_event!(keycode release Tab)
2744 | ct_event!(keycode release Enter)
2745 | ct_event!(keycode release Backspace)
2746 | ct_event!(keycode release Delete)
2747 | ct_event!(keycode release CONTROL-Backspace)
2748 | ct_event!(keycode release ALT-Backspace)
2749 | ct_event!(keycode release CONTROL-Delete)
2750 | ct_event!(key release CONTROL-'x')
2751 | ct_event!(key release CONTROL-'v')
2752 | ct_event!(key release CONTROL-'d')
2753 | ct_event!(key release CONTROL-'y')
2754 | ct_event!(key release CONTROL-'z')
2755 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2756 _ => TextOutcome::Continue,
2757 }
2758 } else {
2759 TextOutcome::Continue
2760 };
2761 if r == TextOutcome::Continue {
2762 r = self.handle(event, ReadOnly);
2763 }
2764 r
2765 }
2766}
2767
2768pub const MATCH_STYLE: usize = 100_001;
2770
2771impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
2772 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2773 let mut r = if self.is_focused() {
2774 match event {
2775 ct_event!(keycode press Left) => self.move_left(1, false).into(),
2776 ct_event!(keycode press Right) => self.move_right(1, false).into(),
2777 ct_event!(keycode press Up) => self.move_up(1, false).into(),
2778 ct_event!(keycode press Down) => self.move_down(1, false).into(),
2779 ct_event!(keycode press PageUp) => {
2780 self.move_up(self.vertical_page() as u16, false).into()
2781 }
2782 ct_event!(keycode press PageDown) => {
2783 self.move_down(self.vertical_page() as u16, false).into()
2784 }
2785 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
2786 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
2787 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
2788 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
2789 ct_event!(keycode press CONTROL-Up) => false.into(),
2790 ct_event!(keycode press CONTROL-Down) => false.into(),
2791 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
2792 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
2793 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
2794 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
2795
2796 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
2797 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
2798 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
2799 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
2800 ct_event!(keycode press ALT-PageUp) => {
2801 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
2802 }
2803 ct_event!(keycode press ALT-PageDown) => {
2804 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
2805 }
2806 ct_event!(keycode press ALT_SHIFT-PageUp) => {
2807 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
2808 }
2809 ct_event!(keycode press ALT_SHIFT-PageDown) => {
2810 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
2811 }
2812
2813 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
2814 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
2815 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
2816 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
2817 ct_event!(keycode press SHIFT-PageUp) => {
2818 self.move_up(self.vertical_page() as u16, true).into()
2819 }
2820 ct_event!(keycode press SHIFT-PageDown) => {
2821 self.move_down(self.vertical_page() as u16, true).into()
2822 }
2823 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
2824 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
2825 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
2826 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
2827 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
2828 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
2829 ct_event!(key press CONTROL-'a') => self.select_all().into(),
2830 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
2831
2832 ct_event!(keycode press F(3)) => self.move_to_next_match().into(),
2833 ct_event!(keycode press SHIFT-F(3)) => self.move_to_prev_match().into(),
2834
2835 ct_event!(keycode release Left)
2836 | ct_event!(keycode release Right)
2837 | ct_event!(keycode release Up)
2838 | ct_event!(keycode release Down)
2839 | ct_event!(keycode release PageUp)
2840 | ct_event!(keycode release PageDown)
2841 | ct_event!(keycode release Home)
2842 | ct_event!(keycode release End)
2843 | ct_event!(keycode release CONTROL-Left)
2844 | ct_event!(keycode release CONTROL-Right)
2845 | ct_event!(keycode release CONTROL-Up)
2846 | ct_event!(keycode release CONTROL-Down)
2847 | ct_event!(keycode release CONTROL-PageUp)
2848 | ct_event!(keycode release CONTROL-PageDown)
2849 | ct_event!(keycode release CONTROL-Home)
2850 | ct_event!(keycode release CONTROL-End)
2851 | ct_event!(keycode release ALT-Left)
2852 | ct_event!(keycode release ALT-Right)
2853 | ct_event!(keycode release ALT-Up)
2854 | ct_event!(keycode release ALT-Down)
2855 | ct_event!(keycode release ALT-PageUp)
2856 | ct_event!(keycode release ALT-PageDown)
2857 | ct_event!(keycode release ALT_SHIFT-PageUp)
2858 | ct_event!(keycode release ALT_SHIFT-PageDown)
2859 | ct_event!(keycode release SHIFT-Left)
2860 | ct_event!(keycode release SHIFT-Right)
2861 | ct_event!(keycode release SHIFT-Up)
2862 | ct_event!(keycode release SHIFT-Down)
2863 | ct_event!(keycode release SHIFT-PageUp)
2864 | ct_event!(keycode release SHIFT-PageDown)
2865 | ct_event!(keycode release SHIFT-Home)
2866 | ct_event!(keycode release SHIFT-End)
2867 | ct_event!(keycode release CONTROL_SHIFT-Left)
2868 | ct_event!(keycode release CONTROL_SHIFT-Right)
2869 | ct_event!(keycode release CONTROL_SHIFT-Home)
2870 | ct_event!(keycode release CONTROL_SHIFT-End)
2871 | ct_event!(key release CONTROL-'a')
2872 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2873 _ => TextOutcome::Continue,
2874 }
2875 } else {
2876 TextOutcome::Continue
2877 };
2878
2879 if r == TextOutcome::Continue {
2880 r = self.handle(event, MouseOnly);
2881 }
2882 r
2883 }
2884}
2885
2886impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
2887 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
2888 flow!(match event {
2889 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2890 let cx = m.column as i16 - self.inner.x as i16;
2891 let cy = m.row as i16 - self.inner.y as i16;
2892 self.set_screen_cursor((cx, cy), true).into()
2893 }
2894 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2895 let cx = m.column as i16 - self.inner.x as i16;
2896 let cy = m.row as i16 - self.inner.y as i16;
2897 self.set_screen_cursor_words((cx, cy), true).into()
2898 }
2899 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2900 if let Some(test) = self.screen_to_pos((m.column, m.row)) {
2901 let start = self.word_start(test);
2902 let end = self.word_end(test);
2903 self.set_selection(start, end).into()
2904 } else {
2905 TextOutcome::Unchanged
2906 }
2907 }
2908 ct_event!(mouse down Left for column,row) => {
2909 if self.inner.contains((*column, *row).into()) {
2910 let cx = (column - self.inner.x) as i16;
2911 let cy = (row - self.inner.y) as i16;
2912 self.set_screen_cursor((cx, cy), false).into()
2913 } else {
2914 TextOutcome::Continue
2915 }
2916 }
2917 ct_event!(mouse down SHIFT-Left for column,row) => {
2918 if self.inner.contains((*column, *row).into()) {
2919 let cx = (column - self.inner.x) as i16;
2920 let cy = (row - self.inner.y) as i16;
2921 self.set_screen_cursor((cx, cy), true).into()
2922 } else {
2923 TextOutcome::Continue
2924 }
2925 }
2926 ct_event!(mouse down CONTROL-Left for column,row) => {
2927 if self.inner.contains((*column, *row).into()) {
2928 let cx = (column - self.inner.x) as i16;
2929 let cy = (row - self.inner.y) as i16;
2930 self.set_screen_cursor((cx, cy), true).into()
2931 } else {
2932 TextOutcome::Continue
2933 }
2934 }
2935 ct_event!(mouse down ALT-Left for column,row) => {
2936 if self.inner.contains((*column, *row).into()) {
2937 let cx = (column - self.inner.x) as i16;
2938 let cy = (row - self.inner.y) as i16;
2939 self.set_screen_cursor_words((cx, cy), true).into()
2940 } else {
2941 TextOutcome::Continue
2942 }
2943 }
2944 _ => TextOutcome::Continue,
2945 });
2946
2947 let mut sas = ScrollAreaState::new()
2948 .area(self.inner)
2949 .h_scroll(&mut self.hscroll)
2950 .v_scroll(&mut self.vscroll);
2951 let r = match sas.handle(event, MouseOnly) {
2952 ScrollOutcome::Up(v) => self.scroll_up(v as upos_type),
2953 ScrollOutcome::Down(v) => self.scroll_down(v as upos_type),
2954 ScrollOutcome::Left(v) => self.scroll_left(v as upos_type),
2955 ScrollOutcome::Right(v) => self.scroll_right(v as upos_type),
2956 ScrollOutcome::VPos(v) => self.scroll_to_row(v as upos_type),
2957 ScrollOutcome::HPos(v) => self.scroll_to_col(v as upos_type),
2958 _ => false,
2959 };
2960 if r {
2961 return TextOutcome::Changed;
2962 }
2963
2964 TextOutcome::Continue
2965 }
2966}
2967
2968pub fn handle_events(
2972 state: &mut TextAreaState,
2973 focus: bool,
2974 event: &crossterm::event::Event,
2975) -> TextOutcome {
2976 state.focus.set(focus);
2977 state.handle(event, Regular)
2978}
2979
2980pub fn handle_readonly_events(
2984 state: &mut TextAreaState,
2985 focus: bool,
2986 event: &crossterm::event::Event,
2987) -> TextOutcome {
2988 state.focus.set(focus);
2989 state.handle(event, ReadOnly)
2990}
2991
2992pub fn handle_mouse_events(
2994 state: &mut TextAreaState,
2995 event: &crossterm::event::Event,
2996) -> TextOutcome {
2997 state.handle(event, MouseOnly)
2998}