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 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_core::buffer::Buffer;
24use ratatui_core::layout::{Rect, Size};
25use ratatui_core::style::Style;
26use ratatui_core::widgets::StatefulWidget;
27use ratatui_crossterm::crossterm::event::{Event, KeyModifiers};
28use ratatui_widgets::block::Block;
29use ropey::Rope;
30use std::borrow::Cow;
31use std::cell::Cell;
32use std::cmp::{max, min};
33use std::collections::HashMap;
34use std::ops::Range;
35use std::rc::Rc;
36
37pub mod text_area_op;
38
39#[derive(Debug, Default, Clone)]
79pub struct TextArea<'a> {
80 style: Style,
81 block: Option<Block<'a>>,
82 hscroll: Option<Scroll<'a>>,
83 h_max_offset: Option<upos_type>,
84 h_overscroll: Option<upos_type>,
85 vscroll: Option<Scroll<'a>>,
86
87 focus_style: Option<Style>,
88 select_style: Option<Style>,
89 cursor_style: Option<Style>,
90 text_style: HashMap<usize, Style>,
91
92 text_wrap: Option<TextWrap>,
93}
94
95#[derive(Debug)]
97pub struct TextAreaState {
98 pub area: Rect,
101 pub inner: Rect,
104 pub rendered: Size,
109 screen_cursor: Option<(u16, u16)>,
112
113 pub hscroll: ScrollState,
117 pub vscroll: ScrollState,
120 pub sub_row_offset: upos_type,
125 pub dark_offset: (u16, u16),
128 pub scroll_to_cursor: Rc<Cell<bool>>,
134
135 pub value: TextCore<TextRope>,
137
138 pub move_col: Option<i16>,
147 pub auto_indent: bool,
150 pub auto_quote: bool,
153 pub text_wrap: TextWrap,
156 pub newline: String,
159 pub tab_width: u32,
162 pub expand_tabs: bool,
165
166 pub focus: FocusFlag,
169
170 pub mouse: MouseFlags,
173
174 pub non_exhaustive: NonExhaustive,
175}
176
177impl Clone for TextAreaState {
178 fn clone(&self) -> Self {
179 Self {
180 area: self.area,
181 inner: self.inner,
182 rendered: self.rendered,
183 screen_cursor: self.screen_cursor,
184 hscroll: self.hscroll.clone(),
185 vscroll: self.vscroll.clone(),
186 sub_row_offset: self.sub_row_offset,
187 dark_offset: self.dark_offset,
188 scroll_to_cursor: Rc::new(Cell::new(self.scroll_to_cursor.get())),
189 value: self.value.clone(),
190 move_col: None,
191 auto_indent: self.auto_indent,
192 auto_quote: self.auto_quote,
193 text_wrap: self.text_wrap,
194 newline: self.newline.clone(),
195 tab_width: self.tab_width,
196 expand_tabs: self.expand_tabs,
197 focus: self.focus.new_instance(),
198 mouse: Default::default(),
199 non_exhaustive: NonExhaustive,
200 }
201 }
202}
203
204#[derive(Debug, Default, Clone, Copy)]
206#[non_exhaustive]
207pub enum TextWrap {
208 #[default]
210 Shift,
211 Hard,
213 Word(u16),
225}
226
227impl<'a> TextArea<'a> {
228 pub fn new() -> Self {
230 Self::default()
231 }
232
233 #[inline]
235 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
236 if let Some(styles) = styles {
237 self.styles(styles)
238 } else {
239 self
240 }
241 }
242
243 #[inline]
245 pub fn styles(mut self, styles: TextStyle) -> Self {
246 self.style = styles.style;
247 if styles.block.is_some() {
248 self.block = styles.block;
249 }
250 if let Some(border_style) = styles.border_style {
251 self.block = self.block.map(|v| v.border_style(border_style));
252 }
253 if let Some(title_style) = styles.title_style {
254 self.block = self.block.map(|v| v.title_style(title_style));
255 }
256 self.block = self.block.map(|v| v.style(self.style));
257
258 if let Some(styles) = styles.scroll {
259 self.hscroll = self.hscroll.map(|v| v.styles(styles.clone()));
260 self.vscroll = self.vscroll.map(|v| v.styles(styles));
261 }
262 if styles.focus.is_some() {
263 self.focus_style = styles.focus;
264 }
265 if styles.select.is_some() {
266 self.select_style = styles.select;
267 }
268 if styles.cursor.is_some() {
269 self.cursor_style = styles.cursor;
270 }
271
272 self
273 }
274
275 pub fn style(mut self, style: Style) -> Self {
277 self.style = style;
278 self.block = self.block.map(|v| v.style(style));
279 self
280 }
281
282 pub fn focus_style(mut self, style: Style) -> Self {
284 self.focus_style = Some(style);
285 self.block = self.block.map(|v| v.style(self.style));
286 self
287 }
288
289 pub fn select_style(mut self, style: Style) -> Self {
291 self.select_style = Some(style);
292 self
293 }
294
295 #[inline]
298 pub fn cursor_style(mut self, style: impl Into<Style>) -> Self {
299 self.cursor_style = Some(style.into());
300 self
301 }
302
303 pub fn text_style_idx(mut self, idx: usize, style: Style) -> Self {
308 self.text_style.insert(idx, style);
309 self
310 }
311
312 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
317 for (i, s) in styles.into_iter().enumerate() {
318 self.text_style.insert(i, s);
319 }
320 self
321 }
322
323 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
328 for (i, s) in styles.into_iter() {
329 self.text_style.insert(i, s.into());
330 }
331 self
332 }
333
334 #[inline]
336 pub fn block(mut self, block: Block<'a>) -> Self {
337 self.block = Some(block);
338 self
339 }
340
341 pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
343 self.hscroll = Some(scroll.clone().override_horizontal());
344 self.vscroll = Some(scroll.override_vertical());
345 self
346 }
347
348 pub fn hscroll(mut self, scroll: Scroll<'a>) -> Self {
350 self.hscroll = Some(scroll.override_horizontal());
351 self
352 }
353
354 pub fn text_wrap(mut self, wrap: TextWrap) -> Self {
356 self.text_wrap = Some(wrap);
357 self
358 }
359
360 pub fn set_horizontal_max_offset(mut self, offset: u32) -> Self {
377 self.h_max_offset = Some(offset);
378 self
379 }
380
381 pub fn set_horizontal_overscroll(mut self, overscroll: u32) -> Self {
388 self.h_overscroll = Some(overscroll);
389 self
390 }
391
392 pub fn vscroll(mut self, scroll: Scroll<'a>) -> Self {
394 self.vscroll = Some(scroll.override_vertical());
395 self
396 }
397
398 pub fn width(&self) -> u16 {
400 0
401 }
402
403 pub fn height(&self) -> u16 {
405 1
406 }
407}
408
409impl<'a> StatefulWidget for &TextArea<'a> {
410 type State = TextAreaState;
411
412 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
413 render_text_area(self, area, buf, state);
414 }
415}
416
417impl StatefulWidget for TextArea<'_> {
418 type State = TextAreaState;
419
420 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
421 render_text_area(&self, area, buf, state);
422 }
423}
424
425fn render_text_area(
426 widget: &TextArea<'_>,
427 area: Rect,
428 buf: &mut Buffer,
429 state: &mut TextAreaState,
430) {
431 state.area = area;
432 state.screen_cursor = None;
433 if let Some(text_wrap) = widget.text_wrap {
434 state.text_wrap = text_wrap;
435 }
436
437 let focused = state.is_focused();
438 let cursor_type = cursor_type();
439 let style = widget.style;
440 let focus_style = if let Some(focus_style) = widget.focus_style {
441 focus_style
442 } else {
443 style
444 };
445 let select_style = if let Some(select_style) = widget.select_style {
446 select_style
447 } else {
448 Style::default().black().on_yellow()
449 };
450 let (style, select_style) = if focused {
451 (
452 style.patch(focus_style),
453 style.patch(focus_style).patch(select_style),
454 )
455 } else {
456 (style, style.patch(select_style))
457 };
458 let cursor_style = if cursor_type == CursorType::RenderedCursor {
459 widget
460 .cursor_style
461 .unwrap_or_else(|| Style::default().white().on_red())
462 } else {
463 Style::default()
464 };
465
466 state.area = area;
468 state.screen_cursor = None;
469 state.inner = ScrollArea::new()
470 .block(widget.block.as_ref())
471 .h_scroll(widget.hscroll.as_ref())
472 .v_scroll(widget.vscroll.as_ref())
473 .inner(area, Some(&state.hscroll), Some(&state.vscroll));
474 state.rendered = state.inner.as_size();
475
476 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
477 state.hscroll.set_max_offset(0);
478 state.hscroll.set_overscroll_by(None);
479 } else {
480 if let Some(h_max_offset) = widget.h_max_offset {
481 state.hscroll.set_max_offset(h_max_offset as usize);
482 }
483 if let Some(h_overscroll) = widget.h_overscroll {
484 state.hscroll.set_overscroll_by(Some(h_overscroll as usize));
485 }
486 }
487 state.hscroll.set_page_len(state.inner.width as usize);
488
489 if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
490 state
491 .vscroll
492 .set_max_offset(state.len_lines().saturating_sub(1) as usize);
493 } else {
494 state.vscroll.set_max_offset(
495 state
496 .len_lines()
497 .saturating_sub(state.inner.height as upos_type) as usize,
498 );
499 }
500 state.vscroll.set_page_len(state.inner.height as usize);
501
502 if state.scroll_to_cursor.get() {
503 state.scroll_to_pos(state.cursor());
504 }
505
506 ScrollArea::new()
508 .block(widget.block.as_ref())
509 .h_scroll(widget.hscroll.as_ref())
510 .v_scroll(widget.vscroll.as_ref())
511 .style(style)
512 .render(
513 area,
514 buf,
515 &mut ScrollAreaState::new()
516 .h_scroll(&mut state.hscroll)
517 .v_scroll(&mut state.vscroll),
518 );
519
520 if state.inner.width == 0 || state.inner.height == 0 {
521 return;
523 }
524 if state.vscroll.offset() > state.value.len_lines() as usize {
525 return;
527 }
528
529 let (shift_left, sub_row_offset, start_row) = state.clean_offset();
530 let page_rows = start_row
531 ..min(
532 start_row + state.inner.height as upos_type,
533 state.value.len_lines(),
534 );
535 let page_bytes = state
536 .try_bytes_at_range(TextRange::new(
537 (sub_row_offset, page_rows.start),
538 (0, page_rows.end),
539 ))
540 .expect("valid_rows");
541 let selection = state.selection();
543 let cursor = state.cursor();
544 let mut styles = Vec::new();
545
546 let mut screen_pos = (0, 0);
547 for g in state
548 .glyphs2(shift_left, sub_row_offset, page_rows)
549 .expect("valid_offset")
550 {
551 screen_pos = g.screen_pos();
553
554 if screen_pos.1 >= state.inner.height {
555 break;
556 }
557
558 if g.screen_width() > 0 {
559 let mut style = style;
560 state
562 .value
563 .styles_at_page(g.text_bytes().start, page_bytes.clone(), &mut styles);
564 for style_nr in &styles {
565 if let Some(s) = widget.text_style.get(style_nr) {
566 style = style.patch(*s);
567 }
568 }
569 if cursor_type == CursorType::RenderedCursor {
570 if focused && selection.is_empty() && g.pos() == cursor {
571 style = cursor_style;
572 }
573 }
574 if selection.contains_pos(g.pos()) {
576 style = style.patch(select_style);
577 };
578
579 if let Some(cell) =
581 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
582 {
583 cell.set_symbol(g.glyph());
584 cell.set_style(style);
585 }
586 for d in 1..g.screen_width() {
588 if let Some(cell) = buf.cell_mut((
589 state.inner.x + screen_pos.0 + d,
590 state.inner.y + screen_pos.1,
591 )) {
592 cell.reset();
593 cell.set_style(style);
594 }
595 }
596 } else {
597 if cursor_type == CursorType::RenderedCursor {
598 if focused && selection.is_empty() && g.line_break() && g.pos() == cursor {
599 if let Some(cell) =
600 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
601 {
602 cell.set_symbol(" ");
603 cell.set_style(cursor_style);
604 }
605 }
606 }
607 }
608 }
609
610 if cursor_type == CursorType::RenderedCursor {
611 if focused
612 && selection.is_empty()
613 && cursor == TextPosition::new(0, state.len_lines().saturating_sub(1))
614 {
615 let yy = if state.is_empty() { 0 } else { 1 };
616 if let Some(cell) = buf.cell_mut((state.inner.x, state.inner.y + screen_pos.1 + yy)) {
617 cell.set_style(cursor_style);
618 }
619 }
620 } else if cursor_type == CursorType::TerminalCursor {
621 state.screen_cursor = state.pos_to_screen(state.cursor());
622 } else {
623 state.screen_cursor = None;
624 }
625}
626
627impl Default for TextAreaState {
628 fn default() -> Self {
629 #[cfg(windows)]
630 const LINE_ENDING: &str = "\r\n";
631
632 #[cfg(not(windows))]
633 const LINE_ENDING: &str = "\n";
634
635 let mut s = Self {
636 area: Default::default(),
637 inner: Default::default(),
638 rendered: Default::default(),
639 screen_cursor: Default::default(),
640 hscroll: Default::default(),
641 vscroll: Default::default(),
642 sub_row_offset: 0,
643 dark_offset: Default::default(),
644 scroll_to_cursor: Default::default(),
645 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
646 move_col: Default::default(),
647 auto_indent: true,
648 auto_quote: true,
649 text_wrap: TextWrap::Shift,
650 newline: LINE_ENDING.to_string(),
651 tab_width: 8,
652 expand_tabs: true,
653 focus: Default::default(),
654 mouse: Default::default(),
655 non_exhaustive: NonExhaustive,
656 };
657 s.hscroll.set_max_offset(255);
658 s.hscroll.set_overscroll_by(Some(16384));
659 s
660 }
661}
662
663impl HasFocus for TextAreaState {
664 fn build(&self, builder: &mut FocusBuilder) {
665 builder.leaf_widget(self);
666 }
667
668 fn focus(&self) -> FocusFlag {
669 self.focus.clone()
670 }
671
672 fn area(&self) -> Rect {
673 self.area
674 }
675
676 fn navigable(&self) -> Navigation {
677 Navigation::Reach
678 }
679}
680
681impl TextAreaState {
682 #[inline]
684 pub fn new() -> Self {
685 Self::default()
686 }
687
688 #[inline]
690 pub fn named(name: &str) -> Self {
691 Self {
692 focus: FocusFlag::new().with_name(name),
693 ..Default::default()
694 }
695 }
696
697 #[inline]
703 pub fn set_newline(&mut self, br: impl Into<String>) {
704 self.newline = br.into();
705 }
706
707 #[inline]
709 pub fn newline(&self) -> &str {
710 &self.newline
711 }
712
713 #[inline]
715 pub fn set_auto_indent(&mut self, indent: bool) {
716 self.auto_indent = indent;
717 }
718
719 #[inline]
721 pub fn set_auto_quote(&mut self, quote: bool) {
722 self.auto_quote = quote;
723 }
724
725 #[inline]
727 pub fn set_tab_width(&mut self, tabs: u32) {
728 self.tab_width = tabs;
729 }
730
731 #[inline]
733 pub fn tab_width(&self) -> u32 {
734 self.tab_width
735 }
736
737 #[inline]
739 pub fn set_expand_tabs(&mut self, expand: bool) {
740 self.expand_tabs = expand;
741 }
742
743 #[inline]
745 pub fn expand_tabs(&self) -> bool {
746 self.expand_tabs
747 }
748
749 #[inline]
751 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
752 self.value.set_glyph_ctrl(show_ctrl);
753 }
754
755 pub fn show_ctrl(&self) -> bool {
757 self.value.glyph_ctrl()
758 }
759
760 #[inline]
763 pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
764 self.value.set_wrap_ctrl(wrap_ctrl);
765 }
766
767 pub fn wrap_ctrl(&self) -> bool {
770 self.value.wrap_ctrl()
771 }
772
773 pub fn set_text_wrap(&mut self, text_wrap: TextWrap) {
786 self.text_wrap = text_wrap;
787 }
788
789 pub fn text_wrap(&self) -> TextWrap {
791 self.text_wrap
792 }
793
794 #[inline]
804 pub fn set_move_col(&mut self, col: Option<i16>) {
805 self.move_col = col;
806 }
807
808 #[inline]
810 pub fn move_col(&mut self) -> Option<i16> {
811 self.move_col
812 }
813}
814
815impl TextAreaState {
816 #[inline]
819 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
820 match clip {
821 None => self.value.set_clipboard(None),
822 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
823 }
824 }
825
826 #[inline]
829 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
830 self.value.clipboard()
831 }
832
833 #[inline]
835 pub fn copy_to_clip(&mut self) -> bool {
836 let Some(clip) = self.value.clipboard() else {
837 return false;
838 };
839
840 _ = clip.set_string(self.selected_text().as_ref());
841 false
842 }
843
844 #[inline]
846 pub fn cut_to_clip(&mut self) -> bool {
847 let Some(clip) = self.value.clipboard() else {
848 return false;
849 };
850
851 match clip.set_string(self.selected_text().as_ref()) {
852 Ok(_) => self.delete_range(self.selection()),
853 Err(_) => false,
854 }
855 }
856
857 #[inline]
859 pub fn paste_from_clip(&mut self) -> bool {
860 let Some(clip) = self.value.clipboard() else {
861 return false;
862 };
863
864 if let Ok(text) = clip.get_string() {
865 self.insert_str(text)
866 } else {
867 false
868 }
869 }
870}
871
872impl TextAreaState {
873 #[inline]
880 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
881 match undo {
882 None => self.value.set_undo_buffer(None),
883 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
884 }
885 }
886
887 #[inline]
889 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
890 self.value.undo_buffer()
891 }
892
893 #[inline]
895 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
896 self.value.undo_buffer_mut()
897 }
898
899 #[inline]
905 pub fn begin_undo_seq(&mut self) {
906 self.value.begin_undo_seq()
907 }
908
909 #[inline]
911 pub fn end_undo_seq(&mut self) {
912 self.value.end_undo_seq()
913 }
914
915 #[inline]
920 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
921 self.value.recent_replay_log()
922 }
923
924 #[inline]
928 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
929 self.value.replay_log(replay)
930 }
931
932 #[inline]
934 pub fn undo(&mut self) -> bool {
935 self.value.undo()
936 }
937
938 #[inline]
940 pub fn redo(&mut self) -> bool {
941 self.value.redo()
942 }
943}
944
945impl TextAreaState {
946 #[inline]
963 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
964 self.value.set_styles(styles);
965 }
966
967 #[inline]
978 pub fn set_range_styles(&mut self, styles: Vec<(TextRange, usize)>) -> Result<(), TextError> {
979 let mut mapped = Vec::with_capacity(styles.len());
980 for (r, s) in styles {
981 let rr = self.value.bytes_at_range(r)?;
982 mapped.push((rr, s));
983 }
984 self.value.set_styles(mapped);
985 Ok(())
986 }
987
988 #[inline]
993 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
994 self.value.add_style(range, style);
995 }
996
997 #[inline]
1001 pub fn add_range_style(
1002 &mut self,
1003 range: impl Into<TextRange>,
1004 style: usize,
1005 ) -> Result<(), TextError> {
1006 let r = self.value.bytes_at_range(range.into())?;
1007 self.value.add_style(r, style);
1008 Ok(())
1009 }
1010
1011 #[inline]
1013 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
1014 self.value.remove_style(range, style);
1015 }
1016
1017 #[inline]
1019 pub fn remove_style_fully(&mut self, style: usize) {
1020 self.value.remove_style_fully(style);
1021 }
1022
1023 #[inline]
1025 pub fn remove_range_style(
1026 &mut self,
1027 range: impl Into<TextRange>,
1028 style: usize,
1029 ) -> Result<(), TextError> {
1030 let r = self.value.bytes_at_range(range.into())?;
1031 self.value.remove_style(r, style);
1032 Ok(())
1033 }
1034
1035 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
1037 self.value.styles_in(range, buf)
1038 }
1039
1040 pub fn styles_in_match(
1042 &self,
1043 range: Range<usize>,
1044 style: usize,
1045 buf: &mut Vec<(Range<usize>, usize)>,
1046 ) {
1047 self.value.styles_in_match(range, style, buf);
1048 }
1049
1050 #[inline]
1052 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
1053 self.value.styles_at(byte_pos, buf)
1054 }
1055
1056 #[inline]
1059 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
1060 self.value.styles_at_match(byte_pos, style)
1061 }
1062
1063 #[inline]
1065 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
1066 self.value.styles()
1067 }
1068}
1069
1070impl TextAreaState {
1071 #[inline]
1073 pub fn offset(&self) -> (upos_type, upos_type) {
1074 (
1075 self.hscroll.offset() as upos_type,
1076 self.vscroll.offset() as upos_type,
1077 )
1078 }
1079
1080 #[inline]
1085 pub fn set_offset(&mut self, offset: (upos_type, upos_type)) -> bool {
1086 self.scroll_to_cursor.set(false);
1087 let c = self.hscroll.set_offset(offset.0 as usize);
1088 let r = self.vscroll.set_offset(offset.1 as usize);
1089 r || c
1090 }
1091
1092 pub fn set_sub_row_offset(&mut self, sub_row_offset: upos_type) -> bool {
1104 self.scroll_to_cursor.set(false);
1105 let old = self.sub_row_offset;
1106 self.sub_row_offset = sub_row_offset;
1107 sub_row_offset != old
1108 }
1109
1110 pub fn sub_row_offset(&self) -> upos_type {
1115 self.sub_row_offset
1116 }
1117
1118 fn clean_offset(&self) -> (upos_type, upos_type, upos_type) {
1123 let ox = self.hscroll.offset as upos_type;
1124 let mut oy = self.vscroll.offset as upos_type;
1125
1126 if oy >= self.len_lines() {
1128 oy = 0;
1129 }
1130
1131 match self.text_wrap {
1132 TextWrap::Shift => (ox, 0, oy),
1133 TextWrap::Hard | TextWrap::Word(_) => {
1134 if let Ok(max_col) = self.try_line_width(oy) {
1136 (0, min(self.sub_row_offset, max_col), oy)
1137 } else {
1138 (0, 0, oy)
1139 }
1140 }
1141 }
1142 }
1143
1144 #[inline]
1146 pub fn cursor(&self) -> TextPosition {
1147 self.value.cursor()
1148 }
1149
1150 #[inline]
1152 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
1153 self.scroll_cursor_to_visible();
1154 self.value.set_cursor(cursor.into(), extend_selection)
1155 }
1156
1157 #[inline]
1159 pub fn anchor(&self) -> TextPosition {
1160 self.value.anchor()
1161 }
1162
1163 #[inline]
1165 pub fn has_selection(&self) -> bool {
1166 self.value.has_selection()
1167 }
1168
1169 #[inline]
1171 pub fn selection(&self) -> TextRange {
1172 self.value.selection()
1173 }
1174
1175 #[inline]
1178 pub fn set_selection(
1179 &mut self,
1180 anchor: impl Into<TextPosition>,
1181 cursor: impl Into<TextPosition>,
1182 ) -> bool {
1183 self.scroll_cursor_to_visible();
1184 self.value.set_selection(anchor.into(), cursor.into())
1185 }
1186
1187 #[inline]
1190 pub fn select_all(&mut self) -> bool {
1191 self.scroll_cursor_to_visible();
1192 self.value.select_all()
1193 }
1194
1195 #[inline]
1197 pub fn selected_text(&self) -> Cow<'_, str> {
1198 self.value
1199 .str_slice(self.value.selection())
1200 .expect("valid_selection")
1201 }
1202}
1203
1204impl TextAreaState {
1205 #[inline]
1207 pub fn clear(&mut self) -> bool {
1208 if !self.is_empty() {
1209 self.value.clear();
1210 true
1211 } else {
1212 false
1213 }
1214 }
1215
1216 #[inline]
1220 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1221 self.scroll_to_cursor.set(false);
1222 self.vscroll.set_offset(0);
1223 self.hscroll.set_offset(0);
1224 self.set_sub_row_offset(0);
1225 self.set_move_col(None);
1226
1227 self.value.set_text(TextRope::new_text(s.as_ref()));
1228 }
1229
1230 #[inline]
1232 pub fn text(&self) -> String {
1233 self.value.text().string()
1234 }
1235
1236 #[inline]
1239 pub fn set_rope(&mut self, r: Rope) {
1240 self.scroll_to_cursor.set(false);
1241 self.vscroll.set_offset(0);
1242 self.hscroll.set_offset(0);
1243 self.set_sub_row_offset(0);
1244 self.set_move_col(None);
1245
1246 self.value.set_text(TextRope::new_rope(r));
1247 }
1248
1249 #[inline]
1251 pub fn rope(&self) -> &Rope {
1252 self.value.text().rope()
1253 }
1254
1255 #[inline]
1259 pub fn set_value<S: AsRef<str>>(&mut self, s: S) {
1260 self.set_text(s);
1261 }
1262
1263 #[inline]
1265 pub fn value(&self) -> String {
1266 self.text()
1267 }
1268
1269 #[inline]
1271 pub fn is_empty(&self) -> bool {
1272 self.value.is_empty()
1273 }
1274
1275 #[inline]
1279 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1280 self.value.str_slice_byte(range).expect("valid_range")
1281 }
1282
1283 #[inline]
1285 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1286 self.value.str_slice_byte(range)
1287 }
1288
1289 #[inline]
1293 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
1294 self.value.str_slice(range.into()).expect("valid_range")
1295 }
1296
1297 #[inline]
1299 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
1300 self.value.str_slice(range.into())
1301 }
1302
1303 #[inline]
1305 pub fn len_lines(&self) -> upos_type {
1306 self.value.len_lines()
1307 }
1308
1309 #[inline]
1311 pub fn len_bytes(&self) -> usize {
1312 self.value.len_bytes()
1313 }
1314
1315 #[inline]
1319 pub fn line_width(&self, row: upos_type) -> upos_type {
1320 self.try_line_width(row).expect("valid_row")
1321 }
1322
1323 #[inline]
1325 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1326 self.value.line_width(row)
1327 }
1328
1329 #[inline]
1334 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
1335 self.value.line_at(row).expect("valid_row")
1336 }
1337
1338 #[inline]
1341 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1342 self.value.line_at(row)
1343 }
1344
1345 #[inline]
1349 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
1350 self.value.lines_at(row).expect("valid_row")
1351 }
1352
1353 #[inline]
1355 pub fn try_lines_at(
1356 &self,
1357 row: upos_type,
1358 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1359 self.value.lines_at(row)
1360 }
1361
1362 #[inline]
1367 pub fn line_graphemes(&self, row: upos_type) -> <TextRope as TextStore>::GraphemeIter<'_> {
1368 self.value.line_graphemes(row).expect("valid_row")
1369 }
1370
1371 #[inline]
1374 pub fn try_line_graphemes(
1375 &self,
1376 row: upos_type,
1377 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1378 self.value.line_graphemes(row)
1379 }
1380
1381 #[inline]
1385 pub fn text_graphemes(
1386 &self,
1387 pos: impl Into<TextPosition>,
1388 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1389 self.value.text_graphemes(pos.into()).expect("valid_pos")
1390 }
1391
1392 #[inline]
1394 pub fn try_text_graphemes(
1395 &self,
1396 pos: impl Into<TextPosition>,
1397 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1398 self.value.text_graphemes(pos.into())
1399 }
1400
1401 #[inline]
1405 pub fn graphemes(
1406 &self,
1407 range: impl Into<TextRange>,
1408 pos: impl Into<TextPosition>,
1409 ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1410 self.value
1411 .graphemes(range.into(), pos.into())
1412 .expect("valid_args")
1413 }
1414
1415 #[inline]
1417 pub fn try_graphemes(
1418 &self,
1419 range: impl Into<TextRange>,
1420 pos: impl Into<TextPosition>,
1421 ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1422 self.value.graphemes(range.into(), pos.into())
1423 }
1424
1425 #[inline]
1430 pub fn byte_at(&self, pos: impl Into<TextPosition>) -> Range<usize> {
1431 self.value.byte_at(pos.into()).expect("valid_pos")
1432 }
1433
1434 #[inline]
1437 pub fn try_byte_at(&self, pos: impl Into<TextPosition>) -> Result<Range<usize>, TextError> {
1438 self.value.byte_at(pos.into())
1439 }
1440
1441 #[inline]
1445 pub fn bytes_at_range(&self, range: impl Into<TextRange>) -> Range<usize> {
1446 self.value
1447 .bytes_at_range(range.into())
1448 .expect("valid_range")
1449 }
1450
1451 #[inline]
1453 pub fn try_bytes_at_range(
1454 &self,
1455 range: impl Into<TextRange>,
1456 ) -> Result<Range<usize>, TextError> {
1457 self.value.bytes_at_range(range.into())
1458 }
1459
1460 #[inline]
1465 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1466 self.value.byte_pos(byte).expect("valid_pos")
1467 }
1468
1469 #[inline]
1472 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1473 self.value.byte_pos(byte)
1474 }
1475
1476 #[inline]
1480 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1481 self.value.byte_range(bytes).expect("valid_range")
1482 }
1483
1484 #[inline]
1486 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1487 self.value.byte_range(bytes)
1488 }
1489}
1490
1491impl TextAreaState {
1492 #[inline]
1498 pub fn insert_char(&mut self, c: char) -> bool {
1499 match c {
1500 '\n' => text_area_op::insert_newline(self),
1501 '\t' => text_area_op::insert_tab(self),
1502 c => text_area_op::insert_char(self, c),
1503 }
1504 }
1505
1506 #[inline]
1512 pub fn insert_tab(&mut self) -> bool {
1513 text_area_op::insert_tab(self)
1514 }
1515
1516 #[inline]
1521 pub fn insert_backtab(&mut self) -> bool {
1522 text_area_op::insert_backtab(self)
1523 }
1524
1525 #[inline]
1528 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1529 text_area_op::insert_str(self, t.as_ref())
1530 }
1531
1532 #[inline]
1537 pub fn insert_newline(&mut self) -> bool {
1538 text_area_op::insert_newline(self)
1539 }
1540
1541 #[inline]
1545 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1546 text_area_op::delete_range(self, range.into()).expect("valid_range")
1547 }
1548
1549 #[inline]
1551 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1552 text_area_op::delete_range(self, range.into())
1553 }
1554
1555 #[inline]
1558 pub fn duplicate_text(&mut self) -> bool {
1559 text_area_op::duplicate_text(self)
1560 }
1561
1562 #[inline]
1565 pub fn delete_line(&mut self) -> bool {
1566 text_area_op::delete_line(self)
1567 }
1568
1569 #[inline]
1572 pub fn delete_next_char(&mut self) -> bool {
1573 text_area_op::delete_next_char(self)
1574 }
1575
1576 #[inline]
1579 pub fn delete_prev_char(&mut self) -> bool {
1580 text_area_op::delete_prev_char(self)
1581 }
1582
1583 #[inline]
1588 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1589 core_op::next_word_start(&self.value, pos.into()).expect("valid_pos")
1590 }
1591
1592 #[inline]
1595 pub fn try_next_word_start(
1596 &self,
1597 pos: impl Into<TextPosition>,
1598 ) -> Result<TextPosition, TextError> {
1599 core_op::next_word_start(&self.value, pos.into())
1600 }
1601
1602 #[inline]
1607 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1608 core_op::next_word_end(&self.value, pos.into()).expect("valid_pos")
1609 }
1610
1611 #[inline]
1614 pub fn try_next_word_end(
1615 &self,
1616 pos: impl Into<TextPosition>,
1617 ) -> Result<TextPosition, TextError> {
1618 core_op::next_word_end(&self.value, pos.into())
1619 }
1620
1621 #[inline]
1629 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1630 core_op::prev_word_start(&self.value, pos.into()).expect("valid_pos")
1631 }
1632
1633 #[inline]
1639 pub fn try_prev_word_start(
1640 &self,
1641 pos: impl Into<TextPosition>,
1642 ) -> Result<TextPosition, TextError> {
1643 core_op::prev_word_start(&self.value, pos.into())
1644 }
1645
1646 #[inline]
1652 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1653 core_op::prev_word_end(&self.value, pos.into()).expect("valid_pos")
1654 }
1655
1656 #[inline]
1660 pub fn try_prev_word_end(
1661 &self,
1662 pos: impl Into<TextPosition>,
1663 ) -> Result<TextPosition, TextError> {
1664 core_op::prev_word_end(&self.value, pos.into())
1665 }
1666
1667 #[inline]
1671 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1672 core_op::is_word_boundary(&self.value, pos.into()).expect("valid_pos")
1673 }
1674
1675 #[inline]
1677 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1678 core_op::is_word_boundary(&self.value, pos.into())
1679 }
1680
1681 #[inline]
1686 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1687 core_op::word_start(&self.value, pos.into()).expect("valid_pos")
1688 }
1689
1690 #[inline]
1693 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1694 core_op::word_start(&self.value, pos.into())
1695 }
1696
1697 #[inline]
1702 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1703 core_op::word_end(&self.value, pos.into()).expect("valid_pos")
1704 }
1705
1706 #[inline]
1709 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1710 core_op::word_end(&self.value, pos.into())
1711 }
1712
1713 #[inline]
1718 pub fn delete_next_word(&mut self) -> bool {
1719 text_area_op::delete_next_word(self)
1720 }
1721
1722 #[inline]
1727 pub fn delete_prev_word(&mut self) -> bool {
1728 text_area_op::delete_prev_word(self)
1729 }
1730
1731 pub fn search(&mut self, re: &str) -> Result<bool, TextError> {
1743 match text_area_op::search(self, re, MATCH_STYLE) {
1744 Ok(r) => Ok(r),
1745 Err(_) => Err(TextError::InvalidSearch),
1746 }
1747 }
1748
1749 pub fn move_to_next_match(&mut self) -> bool {
1751 text_area_op::move_to_next_match(self, MATCH_STYLE)
1752 }
1753
1754 pub fn move_to_prev_match(&mut self) -> bool {
1756 text_area_op::move_to_prev_match(self, MATCH_STYLE)
1757 }
1758
1759 pub fn clear_search(&mut self) {
1761 text_area_op::clear_search(self, MATCH_STYLE)
1762 }
1763
1764 #[inline]
1767 pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1768 text_area_op::move_left(self, n, extend_selection)
1769 }
1770
1771 #[inline]
1774 pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1775 text_area_op::move_right(self, n, extend_selection)
1776 }
1777
1778 #[inline]
1781 pub fn move_up(&mut self, n: u16, extend_selection: bool) -> bool {
1782 text_area_op::move_up(self, n, extend_selection)
1783 }
1784
1785 #[inline]
1788 pub fn move_down(&mut self, n: u16, extend_selection: bool) -> bool {
1789 text_area_op::move_down(self, n, extend_selection)
1790 }
1791
1792 #[inline]
1796 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1797 text_area_op::move_to_line_start(self, extend_selection)
1798 }
1799
1800 #[inline]
1804 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1805 text_area_op::move_to_line_end(self, extend_selection)
1806 }
1807
1808 #[inline]
1810 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1811 text_area_op::move_to_start(self, extend_selection)
1812 }
1813
1814 #[inline]
1816 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1817 text_area_op::move_to_end(self, extend_selection)
1818 }
1819
1820 #[inline]
1822 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1823 text_area_op::move_to_screen_start(self, extend_selection)
1824 }
1825
1826 #[inline]
1828 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1829 text_area_op::move_to_screen_end(self, extend_selection)
1830 }
1831
1832 #[inline]
1834 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1835 text_area_op::move_to_next_word(self, extend_selection)
1836 }
1837
1838 #[inline]
1840 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1841 text_area_op::move_to_prev_word(self, extend_selection)
1842 }
1843}
1844
1845impl HasScreenCursor for TextAreaState {
1846 #[allow(clippy::question_mark)]
1848 fn screen_cursor(&self) -> Option<(u16, u16)> {
1849 if self.is_focused() {
1850 if self.has_selection() {
1851 None
1852 } else {
1853 let Some(scr_cursor) = self.screen_cursor else {
1854 return None;
1855 };
1856
1857 if !(scr_cursor.0 >= self.inner.x
1858 && scr_cursor.0 <= self.inner.right()
1859 && scr_cursor.1 >= self.inner.y
1860 && scr_cursor.1 < self.inner.bottom())
1861 {
1862 return None;
1863 }
1864 Some(scr_cursor)
1865 }
1866 } else {
1867 None
1868 }
1869 }
1870}
1871
1872impl RelocatableState for TextAreaState {
1873 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1874 self.area.relocate(shift, clip);
1876 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1877 self.inner.relocate(shift, clip);
1878 self.hscroll.relocate(shift, clip);
1879 self.vscroll.relocate(shift, clip);
1880 if let Some(screen_cursor) = self.screen_cursor {
1881 self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
1882 }
1883 }
1884}
1885
1886impl TextAreaState {
1887 fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
1889 match self.text_wrap {
1890 TextWrap::Shift => (
1891 TextWrap2::Shift,
1892 shift_left,
1893 shift_left + self.rendered.width as upos_type,
1894 shift_left + self.rendered.width as upos_type,
1895 ),
1896 TextWrap::Hard => (
1897 TextWrap2::Hard,
1898 0,
1899 self.rendered.width as upos_type,
1900 self.rendered.width as upos_type,
1901 ),
1902 TextWrap::Word(margin) => (
1903 TextWrap2::Word,
1904 0,
1905 self.rendered.width as upos_type,
1906 self.rendered.width.saturating_sub(margin) as upos_type,
1907 ),
1908 }
1909 }
1910
1911 fn fill_cache(
1914 &self,
1915 shift_left: upos_type,
1916 sub_row_offset: upos_type,
1917 rows: Range<upos_type>,
1918 ) -> Result<(), TextError> {
1919 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1920 self.value.fill_cache(
1921 self.rendered,
1922 sub_row_offset,
1923 rows,
1924 self.tab_width,
1925 text_wrap,
1926 self.wrap_ctrl() | self.show_ctrl(),
1927 left_margin,
1928 right_margin,
1929 word_margin,
1930 )
1931 }
1932
1933 fn glyphs2(
1935 &self,
1936 shift_left: upos_type,
1937 sub_row_offset: upos_type,
1938 rows: Range<upos_type>,
1939 ) -> Result<GlyphIter2<'_, <TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
1940 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1941 self.value.glyphs2(
1942 self.rendered,
1943 sub_row_offset,
1944 rows,
1945 self.tab_width,
1946 text_wrap,
1947 self.wrap_ctrl() | self.show_ctrl(),
1948 left_margin,
1949 right_margin,
1950 word_margin,
1951 )
1952 }
1953
1954 fn stc_fill_screen_cache(&self, scr: (upos_type, upos_type, upos_type)) {
1958 let y2 = scr.1 + scr.2;
1959 self.fill_cache(0, scr.0, scr.1..min(y2, self.len_lines()))
1960 .expect("valid-rows");
1961 }
1962
1963 fn stc_screen_row(
1970 &self,
1971 scr: (upos_type, upos_type, upos_type),
1972 pos: TextPosition,
1973 ) -> Option<upos_type> {
1974 if pos < TextPosition::new(scr.0, scr.1) {
1975 return None;
1976 }
1977
1978 let line_breaks = self.value.cache().line_break.borrow();
1979 let range_start = TextPosition::new(scr.0, scr.1);
1980 let y2 = scr.1 + scr.2;
1981 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
1982
1983 let mut start_pos = TextPosition::new(scr.0, scr.1);
1984 let mut scr_row = 0;
1985 for (_key, cache) in line_breaks.range(range_start..range_end) {
1986 if pos < cache.start_pos {
1987 return Some(scr_row);
1988 }
1989 scr_row += 1;
1990 start_pos = cache.start_pos;
1991 }
1992
1993 if pos == start_pos {
1995 return Some(scr_row);
1996 }
1997
1998 None
1999 }
2000
2001 fn stc_sub_row_offset(
2008 &self,
2009 scr: (upos_type, upos_type, upos_type),
2010 mut scr_row: upos_type,
2011 ) -> (upos_type, upos_type) {
2012 let line_breaks = self.value.cache().line_break.borrow();
2013 let range_start = TextPosition::new(scr.0, scr.1);
2014 let y2 = scr.1 + scr.2;
2015 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
2016
2017 let mut start_pos = (scr.0, scr.1);
2018 for (_key, cache) in line_breaks.range(range_start..range_end) {
2019 if scr_row == 0 {
2020 return start_pos;
2021 }
2022 scr_row -= 1;
2023 start_pos = (cache.start_pos.x, cache.start_pos.y);
2024 }
2025
2026 start_pos
2028 }
2029}
2030
2031impl TextAreaState {
2032 #[allow(clippy::needless_return)]
2034 pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
2035 let scr_pos = (
2036 scr_pos.0 + self.dark_offset.0 as i16,
2037 scr_pos.1 + self.dark_offset.1 as i16,
2038 );
2039
2040 match self.text_wrap {
2041 TextWrap::Shift => {
2042 let (ox, _, oy) = self.clean_offset();
2043
2044 if oy >= self.len_lines() {
2045 return None;
2046 }
2047
2048 if scr_pos.1 < 0 {
2049 return Some(TextPosition::new(
2051 0,
2052 oy.saturating_add_signed(scr_pos.1 as ipos_type),
2053 ));
2054 } else if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
2055 return Some(TextPosition::new(0, self.len_lines().saturating_sub(1)));
2057 }
2058
2059 let pos_y = oy + scr_pos.1 as upos_type;
2060
2061 if scr_pos.0 < 0 {
2062 return Some(TextPosition::new(
2063 ox.saturating_add_signed(scr_pos.0 as ipos_type),
2064 pos_y,
2065 ));
2066 } else if scr_pos.0 as u16 >= self.rendered.width {
2067 return Some(TextPosition::new(
2068 min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
2069 pos_y,
2070 ));
2071 } else {
2072 let mut start_pos = TextPosition::new(0, pos_y);
2073 for g in self
2074 .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
2075 .expect("valid-position")
2076 {
2077 if g.contains_screen_x(scr_pos.0 as u16) {
2078 return Some(TextPosition::new(g.pos().x, pos_y));
2079 }
2080 start_pos = g.pos();
2081 }
2082 Some(start_pos)
2083 }
2084 }
2085 TextWrap::Hard | TextWrap::Word(_) => {
2086 let (_, sub_row_offset, oy) = self.clean_offset();
2087
2088 if oy >= self.len_lines() {
2089 return None;
2090 }
2091
2092 if scr_pos.1 < 0 {
2093 let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2102
2103 self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2104
2105 let n_start_pos = 'f: {
2106 let line_break = self.value.cache().line_break.borrow();
2107 let start_range = TextPosition::new(0, ry);
2108 let end_range = TextPosition::new(sub_row_offset, oy);
2109
2110 let mut nrows = scr_pos.1.unsigned_abs();
2111 for (_break_pos, cache) in line_break.range(start_range..end_range).rev() {
2112 if nrows == 0 {
2113 break 'f cache.start_pos;
2114 }
2115 nrows -= 1;
2116 }
2117 TextPosition::new(0, ry)
2118 };
2119
2120 if scr_pos.0 < 0 {
2122 return Some(n_start_pos);
2123 }
2124
2125 let min_row = n_start_pos.y;
2126 let max_row = min(n_start_pos.y + 1, self.len_lines());
2127 for g in self
2128 .glyphs2(0, n_start_pos.x, min_row..max_row)
2129 .expect("valid-rows")
2130 {
2131 if g.contains_screen_x(scr_pos.0 as u16) {
2132 return Some(g.pos());
2133 }
2134 }
2135
2136 return Some(n_start_pos);
2138 } else {
2139 let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2140
2141 let n_start_pos = if scr_pos.1 == 0 {
2143 TextPosition::new(sub_row_offset, oy)
2144 } else {
2145 self.fill_cache(
2147 0,
2148 sub_row_offset,
2149 oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2150 )
2151 .expect("valid-rows");
2152
2153 'f: {
2154 let text_range = self.value.cache().line_break.borrow();
2155 let start_range = TextPosition::new(sub_row_offset, oy);
2156 let end_range = TextPosition::new(0, self.len_lines());
2157
2158 let mut nrows = scr_pos.1 - 1;
2159 let mut start_pos = TextPosition::new(sub_row_offset, oy);
2160 for (_break_pos, cache) in text_range.range(start_range..end_range) {
2161 if nrows == 0 {
2162 break 'f cache.start_pos;
2163 }
2164 start_pos = cache.start_pos;
2165 nrows -= 1;
2166 }
2167 start_pos
2168 }
2169 };
2170
2171 let min_row = n_start_pos.y;
2172 let max_row = min(n_start_pos.y + 1, self.len_lines());
2173 for g in self
2174 .glyphs2(0, n_start_pos.x, min_row..max_row)
2175 .expect("valid-rows")
2176 {
2177 if g.contains_screen_x(scr_pos.0) {
2178 return Some(g.pos());
2179 }
2180 }
2181
2182 return Some(n_start_pos);
2184 }
2185 }
2186 }
2187 }
2188
2189 pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
2191 let scr_pos = (
2192 scr_pos.0 as i16 - self.inner.x as i16,
2193 scr_pos.1 as i16 - self.inner.y as i16,
2194 );
2195 self.relative_screen_to_pos(scr_pos)
2196 }
2197
2198 #[allow(clippy::explicit_counter_loop)]
2208 pub fn pos_to_relative_screen(&self, pos: impl Into<TextPosition>) -> Option<(i16, i16)> {
2209 let pos = pos.into();
2210 match self.text_wrap {
2211 TextWrap::Shift => {
2212 let (ox, _, oy) = self.clean_offset();
2213
2214 if oy > self.len_lines() {
2215 return None;
2216 }
2217 if pos.y < oy {
2218 return None;
2219 }
2220 if pos.y > self.len_lines() {
2221 return None;
2222 }
2223 if pos.y - oy >= self.rendered.height as u32 {
2224 return None;
2225 }
2226
2227 let screen_y = (pos.y - oy) as u16;
2228
2229 let screen_x = 'f: {
2230 for g in self
2231 .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2232 .expect("valid-row")
2233 {
2234 if g.pos().x == pos.x {
2235 break 'f g.screen_pos().0;
2236 } else if g.line_break() {
2237 break 'f g.screen_pos().0;
2238 }
2239 }
2240 0
2242 };
2243 assert!(screen_x <= self.rendered.width);
2244
2245 Some((
2246 screen_x as i16 - self.dark_offset.0 as i16,
2247 screen_y as i16 - self.dark_offset.1 as i16,
2248 ))
2249 }
2250 TextWrap::Hard | TextWrap::Word(_) => {
2251 let (_, sub_row_offset, oy) = self.clean_offset();
2252
2253 if oy > self.len_lines() {
2254 return None;
2255 }
2256 if pos.y < oy {
2257 return None;
2258 }
2259 if pos.y > self.len_lines() {
2260 return None;
2261 }
2262
2263 let page = self.rendered.height as upos_type;
2264 let scr = (sub_row_offset, oy, page);
2265 self.stc_fill_screen_cache(scr);
2266 let (screen_y, start_pos) = if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2267 if pos_row >= page {
2268 return None;
2270 }
2271 let start_pos = self.stc_sub_row_offset(scr, pos_row);
2272 (pos_row, start_pos)
2273 } else {
2274 return None;
2276 };
2277
2278 let screen_x = 'f: {
2279 for g in self
2280 .glyphs2(
2281 0,
2282 start_pos.0,
2283 start_pos.1..min(start_pos.1 + 1, self.len_lines()),
2284 )
2285 .expect("valid-row")
2286 {
2287 if g.pos().x == pos.x {
2288 break 'f g.screen_pos().0;
2289 } else if g.line_break() {
2290 break 'f g.screen_pos().0;
2291 }
2292 }
2293 0
2295 };
2296 assert!(screen_x <= self.rendered.width);
2297
2298 let scr = (
2299 screen_x as i16 - self.dark_offset.0 as i16,
2300 screen_y as i16 - self.dark_offset.1 as i16,
2301 );
2302 Some(scr)
2303 }
2304 }
2305 }
2306
2307 #[inline]
2310 pub fn pos_to_screen(&self, pos: impl Into<TextPosition>) -> Option<(u16, u16)> {
2311 let scr_pos = self.pos_to_relative_screen(pos.into())?;
2312 if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
2313 Some((
2314 (scr_pos.0 + self.inner.x as i16) as u16,
2315 (scr_pos.1 + self.inner.y as i16) as u16,
2316 ))
2317 } else {
2318 None
2319 }
2320 }
2321
2322 pub fn pos_to_line_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
2324 let pos = pos.into();
2325 match self.text_wrap {
2326 TextWrap::Shift => {
2327 TextPosition::new(0, pos.y)
2329 }
2330 TextWrap::Hard | TextWrap::Word(_) => {
2331 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2332 .expect("valid-row");
2333
2334 let mut start_pos = TextPosition::new(0, pos.y);
2335 for (break_pos, _) in self.value.cache().line_break.borrow().range(
2336 TextPosition::new(0, pos.y)
2337 ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
2338 ) {
2339 if pos >= start_pos && &pos <= break_pos {
2340 break;
2341 }
2342 start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2343 }
2344
2345 start_pos
2346 }
2347 }
2348 }
2349
2350 pub fn pos_to_line_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
2352 let pos = pos.into();
2353
2354 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2355 .expect("valid-row");
2356
2357 let mut end_pos = TextPosition::new(0, pos.y);
2358 for (break_pos, _) in self
2359 .value
2360 .cache()
2361 .line_break
2362 .borrow()
2363 .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2364 {
2365 if pos >= end_pos && &pos <= break_pos {
2366 end_pos = *break_pos;
2367 break;
2368 }
2369 end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2370 }
2371
2372 end_pos
2373 }
2374
2375 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2381 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2382 return false;
2383 };
2384 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2385 self.set_move_col(Some(scr_cursor.0));
2386 }
2387 self.set_cursor(cursor, extend_selection)
2388 }
2389
2390 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2397 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2398 return false;
2399 };
2400
2401 let anchor = self.anchor();
2402 let cursor = if cursor < anchor {
2403 self.word_start(cursor)
2404 } else {
2405 self.word_end(cursor)
2406 };
2407
2408 if !self.is_word_boundary(anchor) {
2410 if cursor < anchor {
2411 self.set_cursor(self.word_end(anchor), false);
2412 } else {
2413 self.set_cursor(self.word_start(anchor), false);
2414 }
2415 }
2416
2417 self.set_cursor(cursor, extend_selection)
2418 }
2419}
2420
2421impl TextAreaState {
2422 pub fn vertical_max_offset(&self) -> upos_type {
2427 self.vscroll.max_offset() as upos_type
2428 }
2429
2430 pub fn vertical_offset(&self) -> upos_type {
2432 self.vscroll.offset() as upos_type
2433 }
2434
2435 pub fn vertical_page(&self) -> upos_type {
2437 self.vscroll.page_len() as upos_type
2438 }
2439
2440 pub fn vertical_scroll(&self) -> upos_type {
2442 self.vscroll.scroll_by() as upos_type
2443 }
2444
2445 pub fn horizontal_max_offset(&self) -> upos_type {
2454 self.hscroll.max_offset() as upos_type
2455 }
2456
2457 pub fn horizontal_offset(&self) -> upos_type {
2459 self.hscroll.offset() as upos_type
2460 }
2461
2462 pub fn horizontal_page(&self) -> upos_type {
2464 self.hscroll.page_len() as upos_type
2465 }
2466
2467 pub fn horizontal_scroll(&self) -> upos_type {
2469 self.hscroll.scroll_by() as upos_type
2470 }
2471
2472 pub fn set_vertical_offset(&mut self, row_offset: upos_type) -> bool {
2479 self.scroll_to_cursor.set(false);
2480 self.sub_row_offset = 0;
2481 self.vscroll.set_offset(row_offset as usize)
2482 }
2483
2484 pub fn set_horizontal_offset(&mut self, col_offset: upos_type) -> bool {
2493 self.scroll_to_cursor.set(false);
2494 self.hscroll.set_offset(col_offset as usize)
2495 }
2496
2497 pub fn scroll_cursor_to_visible(&mut self) {
2503 self.scroll_to_cursor.set(true);
2504 }
2505
2506 pub fn scroll_to_pos(&mut self, pos: impl Into<TextPosition>) -> bool {
2512 let old_offset = self.clean_offset();
2513
2514 let pos = pos.into();
2515
2516 'f: {
2517 match self.text_wrap {
2518 TextWrap::Shift => {
2519 let (ox, _, oy) = old_offset;
2520
2521 let height = self.rendered.height as upos_type;
2522 let width = self.rendered.width as upos_type;
2523 let width = if self.show_ctrl() || self.wrap_ctrl() {
2524 width.saturating_sub(1)
2525 } else {
2526 width
2527 };
2528
2529 let noy = if pos.y < oy.saturating_sub(height) {
2530 pos.y.saturating_sub(height * 4 / 10)
2531 } else if pos.y < oy {
2532 pos.y
2533 } else if pos.y >= oy + 2 * height {
2534 pos.y.saturating_sub(height * 6 / 10)
2535 } else if pos.y >= oy + height {
2536 pos.y.saturating_sub(height.saturating_sub(1))
2537 } else {
2538 oy
2539 };
2540
2541 let nox = if pos.x < ox {
2542 pos.x
2543 } else if pos.x >= ox + width {
2544 pos.x.saturating_sub(width) + 1
2545 } else {
2546 ox
2547 };
2548
2549 self.set_offset((nox, noy));
2550 self.set_sub_row_offset(0);
2551 }
2552 TextWrap::Hard | TextWrap::Word(_) => {
2553 let (_ox, sub_row_offset, oy) = old_offset;
2554 let page = self.rendered.height as upos_type;
2555
2556 let scr = (0, oy.saturating_sub(page), 3 * page);
2558 self.stc_fill_screen_cache(scr);
2559 if let Some(off_row) =
2560 self.stc_screen_row(scr, TextPosition::new(sub_row_offset, oy))
2561 {
2562 if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2563 if pos_row < off_row && pos_row >= off_row.saturating_sub(page) {
2564 let noff_row = pos_row;
2565 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2566 self.set_offset((0, noy));
2567 self.set_sub_row_offset(nsub_row_offset);
2568 break 'f;
2569 } else if pos_row >= off_row + page && pos_row < off_row + 2 * page {
2570 let noff_row = pos_row.saturating_sub(page.saturating_sub(1));
2571 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2572 self.set_offset((0, noy));
2573 self.set_sub_row_offset(nsub_row_offset);
2574 break 'f;
2575 } else if pos_row >= off_row && pos_row < off_row + page {
2576 break 'f;
2577 }
2578 }
2579 }
2580
2581 let alt_scr = (0, pos.y.saturating_sub(page), 3 * page);
2583 self.stc_fill_screen_cache(alt_scr);
2584 if let Some(alt_scr_row) = self.stc_screen_row(alt_scr, pos) {
2585 let noff_row = alt_scr_row.saturating_sub(page * 5 / 10);
2586 let (nsub_row_offset, noy) = self.stc_sub_row_offset(alt_scr, noff_row);
2587 self.set_offset((0, noy));
2588 self.set_sub_row_offset(nsub_row_offset);
2589 } else {
2590 self.set_offset((0, pos.y));
2591 self.set_sub_row_offset(0);
2592 }
2593 }
2594 }
2595 }
2596
2597 old_offset != self.clean_offset()
2598 }
2599
2600 pub fn scroll_to_row(&mut self, pos: upos_type) -> bool {
2609 self.scroll_to_cursor.set(false);
2610
2611 match self.text_wrap {
2612 TextWrap::Shift => self.vscroll.scroll_to_pos(pos as usize),
2613 TextWrap::Hard | TextWrap::Word(_) => self
2614 .vscroll
2615 .set_offset(self.vscroll.limited_offset(pos as usize)),
2616 }
2617 }
2618
2619 pub fn scroll_to_col(&mut self, pos: upos_type) -> bool {
2627 self.scroll_to_cursor.set(false);
2628 self.hscroll.set_offset(pos as usize)
2629 }
2630
2631 pub fn scroll_up(&mut self, delta: upos_type) -> bool {
2637 if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2638 self.sub_row_offset = pos.x;
2639 self.vscroll.set_offset(pos.y as usize);
2640 true
2641 } else {
2642 false
2643 }
2644 }
2645
2646 pub fn scroll_down(&mut self, delta: upos_type) -> bool {
2652 if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2653 self.sub_row_offset = pos.x;
2654 self.vscroll.set_offset(pos.y as usize);
2655 true
2656 } else {
2657 false
2658 }
2659 }
2660
2661 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
2673 self.hscroll
2674 .set_offset(self.hscroll.offset.saturating_add(delta as usize))
2675 }
2676
2677 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
2689 self.hscroll
2690 .set_offset(self.hscroll.offset.saturating_sub(delta as usize))
2691 }
2692}
2693
2694impl HandleEvent<Event, Regular, TextOutcome> for TextAreaState {
2695 fn handle(&mut self, event: &Event, _keymap: Regular) -> TextOutcome {
2696 fn tc(r: bool) -> TextOutcome {
2698 if r {
2699 TextOutcome::TextChanged
2700 } else {
2701 TextOutcome::Unchanged
2702 }
2703 }
2704
2705 let mut r =
2706 if self.is_focused() {
2707 match event {
2708 ct_event!(key press c)
2709 | ct_event!(key press SHIFT-c)
2710 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2711 ct_event!(keycode press Tab) => {
2712 tc(if !self.focus.gained() {
2714 self.insert_tab()
2715 } else {
2716 false
2717 })
2718 }
2719 ct_event!(keycode press SHIFT-BackTab) => {
2720 tc(if !self.focus.gained() {
2722 self.insert_backtab()
2723 } else {
2724 false
2725 })
2726 }
2727 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2728 ct_event!(keycode press Backspace)
2729 | ct_event!(keycode press SHIFT-Backspace) => tc(self.delete_prev_char()),
2730 ct_event!(keycode press Delete) | ct_event!(keycode press SHIFT-Delete) => {
2731 tc(self.delete_next_char())
2732 }
2733 ct_event!(keycode press CONTROL-Backspace)
2734 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2735 ct_event!(keycode press CONTROL-Delete)
2736 | ct_event!(keycode press ALT-Delete) => tc(self.delete_next_word()),
2737 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2738 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2739 ct_event!(paste v) => tc(self.insert_str(&v)),
2740 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2741 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2742 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2743 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2744
2745 ct_event!(key release _)
2746 | ct_event!(key release SHIFT-_)
2747 | ct_event!(key release CONTROL_ALT-_)
2748 | ct_event!(keycode release Tab)
2749 | ct_event!(keycode release Enter)
2750 | ct_event!(keycode release Backspace)
2751 | ct_event!(keycode release Delete)
2752 | ct_event!(keycode release CONTROL-Backspace)
2753 | ct_event!(keycode release ALT-Backspace)
2754 | ct_event!(keycode release CONTROL-Delete)
2755 | ct_event!(key release CONTROL-'x')
2756 | ct_event!(key release CONTROL-'v')
2757 | ct_event!(key release CONTROL-'d')
2758 | ct_event!(key release CONTROL-'y')
2759 | ct_event!(key release CONTROL-'z')
2760 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2761 _ => TextOutcome::Continue,
2762 }
2763 } else {
2764 TextOutcome::Continue
2765 };
2766 if r == TextOutcome::Continue {
2767 r = self.handle(event, ReadOnly);
2768 }
2769 r
2770 }
2771}
2772
2773pub const MATCH_STYLE: usize = 100_001;
2775
2776impl HandleEvent<Event, ReadOnly, TextOutcome> for TextAreaState {
2777 fn handle(&mut self, event: &Event, _keymap: ReadOnly) -> TextOutcome {
2778 let mut r = if self.is_focused() {
2779 match event {
2780 ct_event!(keycode press Left) => self.move_left(1, false).into(),
2781 ct_event!(keycode press Right) => self.move_right(1, false).into(),
2782 ct_event!(keycode press Up) => self.move_up(1, false).into(),
2783 ct_event!(keycode press Down) => self.move_down(1, false).into(),
2784 ct_event!(keycode press PageUp) => {
2785 self.move_up(self.vertical_page() as u16, false).into()
2786 }
2787 ct_event!(keycode press PageDown) => {
2788 self.move_down(self.vertical_page() as u16, false).into()
2789 }
2790 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
2791 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
2792 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
2793 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
2794 ct_event!(keycode press CONTROL-Up) => false.into(),
2795 ct_event!(keycode press CONTROL-Down) => false.into(),
2796 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
2797 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
2798 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
2799 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
2800
2801 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
2802 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
2803 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
2804 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
2805 ct_event!(keycode press ALT-PageUp) => {
2806 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
2807 }
2808 ct_event!(keycode press ALT-PageDown) => {
2809 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
2810 }
2811 ct_event!(keycode press ALT_SHIFT-PageUp) => {
2812 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
2813 }
2814 ct_event!(keycode press ALT_SHIFT-PageDown) => {
2815 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
2816 }
2817
2818 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
2819 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
2820 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
2821 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
2822 ct_event!(keycode press SHIFT-PageUp) => {
2823 self.move_up(self.vertical_page() as u16, true).into()
2824 }
2825 ct_event!(keycode press SHIFT-PageDown) => {
2826 self.move_down(self.vertical_page() as u16, true).into()
2827 }
2828 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
2829 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
2830 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
2831 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
2832 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
2833 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
2834 ct_event!(key press CONTROL-'a') => self.select_all().into(),
2835 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
2836
2837 ct_event!(keycode press F(3)) => self.move_to_next_match().into(),
2838 ct_event!(keycode press SHIFT-F(3)) => self.move_to_prev_match().into(),
2839
2840 ct_event!(keycode release Left)
2841 | ct_event!(keycode release Right)
2842 | ct_event!(keycode release Up)
2843 | ct_event!(keycode release Down)
2844 | ct_event!(keycode release PageUp)
2845 | ct_event!(keycode release PageDown)
2846 | ct_event!(keycode release Home)
2847 | ct_event!(keycode release End)
2848 | ct_event!(keycode release CONTROL-Left)
2849 | ct_event!(keycode release CONTROL-Right)
2850 | ct_event!(keycode release CONTROL-Up)
2851 | ct_event!(keycode release CONTROL-Down)
2852 | ct_event!(keycode release CONTROL-PageUp)
2853 | ct_event!(keycode release CONTROL-PageDown)
2854 | ct_event!(keycode release CONTROL-Home)
2855 | ct_event!(keycode release CONTROL-End)
2856 | ct_event!(keycode release ALT-Left)
2857 | ct_event!(keycode release ALT-Right)
2858 | ct_event!(keycode release ALT-Up)
2859 | ct_event!(keycode release ALT-Down)
2860 | ct_event!(keycode release ALT-PageUp)
2861 | ct_event!(keycode release ALT-PageDown)
2862 | ct_event!(keycode release ALT_SHIFT-PageUp)
2863 | ct_event!(keycode release ALT_SHIFT-PageDown)
2864 | ct_event!(keycode release SHIFT-Left)
2865 | ct_event!(keycode release SHIFT-Right)
2866 | ct_event!(keycode release SHIFT-Up)
2867 | ct_event!(keycode release SHIFT-Down)
2868 | ct_event!(keycode release SHIFT-PageUp)
2869 | ct_event!(keycode release SHIFT-PageDown)
2870 | ct_event!(keycode release SHIFT-Home)
2871 | ct_event!(keycode release SHIFT-End)
2872 | ct_event!(keycode release CONTROL_SHIFT-Left)
2873 | ct_event!(keycode release CONTROL_SHIFT-Right)
2874 | ct_event!(keycode release CONTROL_SHIFT-Home)
2875 | ct_event!(keycode release CONTROL_SHIFT-End)
2876 | ct_event!(key release CONTROL-'a')
2877 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2878 _ => TextOutcome::Continue,
2879 }
2880 } else {
2881 TextOutcome::Continue
2882 };
2883
2884 if r == TextOutcome::Continue {
2885 r = self.handle(event, MouseOnly);
2886 }
2887 r
2888 }
2889}
2890
2891impl HandleEvent<Event, MouseOnly, TextOutcome> for TextAreaState {
2892 fn handle(&mut self, event: &Event, _keymap: MouseOnly) -> TextOutcome {
2893 if self.is_obscured() {
2894 return TextOutcome::Continue;
2895 }
2896
2897 flow!(match event {
2898 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2899 let cx = m.column as i16 - self.inner.x as i16;
2900 let cy = m.row as i16 - self.inner.y as i16;
2901 self.set_screen_cursor((cx, cy), true).into()
2902 }
2903 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2904 let cx = m.column as i16 - self.inner.x as i16;
2905 let cy = m.row as i16 - self.inner.y as i16;
2906 self.set_screen_cursor_words((cx, cy), true).into()
2907 }
2908 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2909 if let Some(test) = self.screen_to_pos((m.column, m.row)) {
2910 let start = self.word_start(test);
2911 let end = self.word_end(test);
2912 self.set_selection(start, end).into()
2913 } else {
2914 TextOutcome::Unchanged
2915 }
2916 }
2917 ct_event!(mouse down 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), false).into()
2922 } else {
2923 TextOutcome::Continue
2924 }
2925 }
2926 ct_event!(mouse down SHIFT-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 CONTROL-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((cx, cy), true).into()
2940 } else {
2941 TextOutcome::Continue
2942 }
2943 }
2944 ct_event!(mouse down ALT-Left for column,row) => {
2945 if self.inner.contains((*column, *row).into()) {
2946 let cx = (column - self.inner.x) as i16;
2947 let cy = (row - self.inner.y) as i16;
2948 self.set_screen_cursor_words((cx, cy), true).into()
2949 } else {
2950 TextOutcome::Continue
2951 }
2952 }
2953 _ => TextOutcome::Continue,
2954 });
2955
2956 let mut sas = ScrollAreaState::new()
2957 .area(self.inner)
2958 .h_scroll(&mut self.hscroll)
2959 .v_scroll(&mut self.vscroll);
2960 let r = match sas.handle(event, MouseOnly) {
2961 ScrollOutcome::Up(v) => self.scroll_up(v as upos_type),
2962 ScrollOutcome::Down(v) => self.scroll_down(v as upos_type),
2963 ScrollOutcome::Left(v) => self.scroll_left(v as upos_type),
2964 ScrollOutcome::Right(v) => self.scroll_right(v as upos_type),
2965 ScrollOutcome::VPos(v) => self.scroll_to_row(v as upos_type),
2966 ScrollOutcome::HPos(v) => self.scroll_to_col(v as upos_type),
2967 _ => false,
2968 };
2969 if r {
2970 return TextOutcome::Changed;
2971 }
2972
2973 TextOutcome::Continue
2974 }
2975}
2976
2977pub fn handle_events(state: &mut TextAreaState, focus: bool, event: &Event) -> TextOutcome {
2981 state.focus.set(focus);
2982 state.handle(event, Regular)
2983}
2984
2985pub fn handle_readonly_events(
2989 state: &mut TextAreaState,
2990 focus: bool,
2991 event: &Event,
2992) -> TextOutcome {
2993 state.focus.set(focus);
2994 state.handle(event, ReadOnly)
2995}
2996
2997pub fn handle_mouse_events(state: &mut TextAreaState, event: &Event) -> TextOutcome {
2999 state.handle(event, MouseOnly)
3000}