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> {
1742 match text_area_op::search(self, re, MATCH_STYLE) {
1743 Ok(r) => Ok(r),
1744 Err(_) => Err(TextError::InvalidSearch),
1745 }
1746 }
1747
1748 pub fn move_to_next_match(&mut self) -> bool {
1750 text_area_op::move_to_next_match(self, MATCH_STYLE)
1751 }
1752
1753 pub fn move_to_prev_match(&mut self) -> bool {
1755 text_area_op::move_to_prev_match(self, MATCH_STYLE)
1756 }
1757
1758 pub fn clear_search(&mut self) {
1760 text_area_op::clear_search(self, MATCH_STYLE)
1761 }
1762
1763 #[inline]
1766 pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1767 text_area_op::move_left(self, n, extend_selection)
1768 }
1769
1770 #[inline]
1773 pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1774 text_area_op::move_right(self, n, extend_selection)
1775 }
1776
1777 #[inline]
1780 pub fn move_up(&mut self, n: u16, extend_selection: bool) -> bool {
1781 text_area_op::move_up(self, n, extend_selection)
1782 }
1783
1784 #[inline]
1787 pub fn move_down(&mut self, n: u16, extend_selection: bool) -> bool {
1788 text_area_op::move_down(self, n, extend_selection)
1789 }
1790
1791 #[inline]
1795 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1796 text_area_op::move_to_line_start(self, extend_selection)
1797 }
1798
1799 #[inline]
1803 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1804 text_area_op::move_to_line_end(self, extend_selection)
1805 }
1806
1807 #[inline]
1809 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1810 text_area_op::move_to_start(self, extend_selection)
1811 }
1812
1813 #[inline]
1815 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1816 text_area_op::move_to_end(self, extend_selection)
1817 }
1818
1819 #[inline]
1821 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1822 text_area_op::move_to_screen_start(self, extend_selection)
1823 }
1824
1825 #[inline]
1827 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1828 text_area_op::move_to_screen_end(self, extend_selection)
1829 }
1830
1831 #[inline]
1833 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1834 text_area_op::move_to_next_word(self, extend_selection)
1835 }
1836
1837 #[inline]
1839 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1840 text_area_op::move_to_prev_word(self, extend_selection)
1841 }
1842}
1843
1844impl HasScreenCursor for TextAreaState {
1845 #[allow(clippy::question_mark)]
1847 fn screen_cursor(&self) -> Option<(u16, u16)> {
1848 if self.is_focused() {
1849 if self.has_selection() {
1850 None
1851 } else {
1852 let Some(scr_cursor) = self.screen_cursor else {
1853 return None;
1854 };
1855
1856 if !(scr_cursor.0 >= self.inner.x
1857 && scr_cursor.0 <= self.inner.right()
1858 && scr_cursor.1 >= self.inner.y
1859 && scr_cursor.1 < self.inner.bottom())
1860 {
1861 return None;
1862 }
1863 Some(scr_cursor)
1864 }
1865 } else {
1866 None
1867 }
1868 }
1869}
1870
1871impl RelocatableState for TextAreaState {
1872 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1873 self.area.relocate(shift, clip);
1875 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1876 self.inner.relocate(shift, clip);
1877 self.hscroll.relocate(shift, clip);
1878 self.vscroll.relocate(shift, clip);
1879 if let Some(screen_cursor) = self.screen_cursor {
1880 self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
1881 }
1882 }
1883}
1884
1885impl TextAreaState {
1886 fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
1888 match self.text_wrap {
1889 TextWrap::Shift => (
1890 TextWrap2::Shift,
1891 shift_left,
1892 shift_left + self.rendered.width as upos_type,
1893 shift_left + self.rendered.width as upos_type,
1894 ),
1895 TextWrap::Hard => (
1896 TextWrap2::Hard,
1897 0,
1898 self.rendered.width as upos_type,
1899 self.rendered.width as upos_type,
1900 ),
1901 TextWrap::Word(margin) => (
1902 TextWrap2::Word,
1903 0,
1904 self.rendered.width as upos_type,
1905 self.rendered.width.saturating_sub(margin) as upos_type,
1906 ),
1907 }
1908 }
1909
1910 fn fill_cache(
1913 &self,
1914 shift_left: upos_type,
1915 sub_row_offset: upos_type,
1916 rows: Range<upos_type>,
1917 ) -> Result<(), TextError> {
1918 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1919 self.value.fill_cache(
1920 self.rendered,
1921 sub_row_offset,
1922 rows,
1923 self.tab_width,
1924 text_wrap,
1925 self.wrap_ctrl() | self.show_ctrl(),
1926 left_margin,
1927 right_margin,
1928 word_margin,
1929 )
1930 }
1931
1932 fn glyphs2(
1934 &self,
1935 shift_left: upos_type,
1936 sub_row_offset: upos_type,
1937 rows: Range<upos_type>,
1938 ) -> Result<GlyphIter2<'_, <TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
1939 let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1940 self.value.glyphs2(
1941 self.rendered,
1942 sub_row_offset,
1943 rows,
1944 self.tab_width,
1945 text_wrap,
1946 self.wrap_ctrl() | self.show_ctrl(),
1947 left_margin,
1948 right_margin,
1949 word_margin,
1950 )
1951 }
1952
1953 fn stc_fill_screen_cache(&self, scr: (upos_type, upos_type, upos_type)) {
1957 let y2 = scr.1 + scr.2;
1958 self.fill_cache(0, scr.0, scr.1..min(y2, self.len_lines()))
1959 .expect("valid-rows");
1960 }
1961
1962 fn stc_screen_row(
1969 &self,
1970 scr: (upos_type, upos_type, upos_type),
1971 pos: TextPosition,
1972 ) -> Option<upos_type> {
1973 if pos < TextPosition::new(scr.0, scr.1) {
1974 return None;
1975 }
1976
1977 let line_breaks = self.value.cache().line_break.borrow();
1978 let range_start = TextPosition::new(scr.0, scr.1);
1979 let y2 = scr.1 + scr.2;
1980 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
1981
1982 let mut start_pos = TextPosition::new(scr.0, scr.1);
1983 let mut scr_row = 0;
1984 for (_key, cache) in line_breaks.range(range_start..range_end) {
1985 if pos < cache.start_pos {
1986 return Some(scr_row);
1987 }
1988 scr_row += 1;
1989 start_pos = cache.start_pos;
1990 }
1991
1992 if pos == start_pos {
1994 return Some(scr_row);
1995 }
1996
1997 None
1998 }
1999
2000 fn stc_sub_row_offset(
2007 &self,
2008 scr: (upos_type, upos_type, upos_type),
2009 mut scr_row: upos_type,
2010 ) -> (upos_type, upos_type) {
2011 let line_breaks = self.value.cache().line_break.borrow();
2012 let range_start = TextPosition::new(scr.0, scr.1);
2013 let y2 = scr.1 + scr.2;
2014 let range_end = TextPosition::new(0, min(y2, self.len_lines()));
2015
2016 let mut start_pos = (scr.0, scr.1);
2017 for (_key, cache) in line_breaks.range(range_start..range_end) {
2018 if scr_row == 0 {
2019 return start_pos;
2020 }
2021 scr_row -= 1;
2022 start_pos = (cache.start_pos.x, cache.start_pos.y);
2023 }
2024
2025 start_pos
2027 }
2028}
2029
2030impl TextAreaState {
2031 #[allow(clippy::needless_return)]
2033 pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
2034 let scr_pos = (
2035 scr_pos.0 + self.dark_offset.0 as i16,
2036 scr_pos.1 + self.dark_offset.1 as i16,
2037 );
2038
2039 match self.text_wrap {
2040 TextWrap::Shift => {
2041 let (ox, _, oy) = self.clean_offset();
2042
2043 if oy >= self.len_lines() {
2044 return None;
2045 }
2046
2047 if scr_pos.1 < 0 {
2048 return Some(TextPosition::new(
2050 0,
2051 oy.saturating_add_signed(scr_pos.1 as ipos_type),
2052 ));
2053 } else if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
2054 return Some(TextPosition::new(0, self.len_lines().saturating_sub(1)));
2056 }
2057
2058 let pos_y = oy + scr_pos.1 as upos_type;
2059
2060 if scr_pos.0 < 0 {
2061 return Some(TextPosition::new(
2062 ox.saturating_add_signed(scr_pos.0 as ipos_type),
2063 pos_y,
2064 ));
2065 } else if scr_pos.0 as u16 >= self.rendered.width {
2066 return Some(TextPosition::new(
2067 min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
2068 pos_y,
2069 ));
2070 } else {
2071 let mut start_pos = TextPosition::new(0, pos_y);
2072 for g in self
2073 .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
2074 .expect("valid-position")
2075 {
2076 if g.contains_screen_x(scr_pos.0 as u16) {
2077 return Some(TextPosition::new(g.pos().x, pos_y));
2078 }
2079 start_pos = g.pos();
2080 }
2081 Some(start_pos)
2082 }
2083 }
2084 TextWrap::Hard | TextWrap::Word(_) => {
2085 let (_, sub_row_offset, oy) = self.clean_offset();
2086
2087 if oy >= self.len_lines() {
2088 return None;
2089 }
2090
2091 if scr_pos.1 < 0 {
2092 let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2101
2102 self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2103
2104 let n_start_pos = 'f: {
2105 let line_break = self.value.cache().line_break.borrow();
2106 let start_range = TextPosition::new(0, ry);
2107 let end_range = TextPosition::new(sub_row_offset, oy);
2108
2109 let mut nrows = scr_pos.1.unsigned_abs();
2110 for (_break_pos, cache) in line_break.range(start_range..end_range).rev() {
2111 if nrows == 0 {
2112 break 'f cache.start_pos;
2113 }
2114 nrows -= 1;
2115 }
2116 TextPosition::new(0, ry)
2117 };
2118
2119 if scr_pos.0 < 0 {
2121 return Some(n_start_pos);
2122 }
2123
2124 let min_row = n_start_pos.y;
2125 let max_row = min(n_start_pos.y + 1, self.len_lines());
2126 for g in self
2127 .glyphs2(0, n_start_pos.x, min_row..max_row)
2128 .expect("valid-rows")
2129 {
2130 if g.contains_screen_x(scr_pos.0 as u16) {
2131 return Some(g.pos());
2132 }
2133 }
2134
2135 return Some(n_start_pos);
2137 } else {
2138 let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2139
2140 let n_start_pos = if scr_pos.1 == 0 {
2142 TextPosition::new(sub_row_offset, oy)
2143 } else {
2144 self.fill_cache(
2146 0,
2147 sub_row_offset,
2148 oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2149 )
2150 .expect("valid-rows");
2151
2152 'f: {
2153 let text_range = self.value.cache().line_break.borrow();
2154 let start_range = TextPosition::new(sub_row_offset, oy);
2155 let end_range = TextPosition::new(0, self.len_lines());
2156
2157 let mut nrows = scr_pos.1 - 1;
2158 let mut start_pos = TextPosition::new(sub_row_offset, oy);
2159 for (_break_pos, cache) in text_range.range(start_range..end_range) {
2160 if nrows == 0 {
2161 break 'f cache.start_pos;
2162 }
2163 start_pos = cache.start_pos;
2164 nrows -= 1;
2165 }
2166 start_pos
2167 }
2168 };
2169
2170 let min_row = n_start_pos.y;
2171 let max_row = min(n_start_pos.y + 1, self.len_lines());
2172 for g in self
2173 .glyphs2(0, n_start_pos.x, min_row..max_row)
2174 .expect("valid-rows")
2175 {
2176 if g.contains_screen_x(scr_pos.0) {
2177 return Some(g.pos());
2178 }
2179 }
2180
2181 return Some(n_start_pos);
2183 }
2184 }
2185 }
2186 }
2187
2188 pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
2190 let scr_pos = (
2191 scr_pos.0 as i16 - self.inner.x as i16,
2192 scr_pos.1 as i16 - self.inner.y as i16,
2193 );
2194 self.relative_screen_to_pos(scr_pos)
2195 }
2196
2197 #[allow(clippy::explicit_counter_loop)]
2207 pub fn pos_to_relative_screen(&self, pos: impl Into<TextPosition>) -> Option<(i16, i16)> {
2208 let pos = pos.into();
2209 match self.text_wrap {
2210 TextWrap::Shift => {
2211 let (ox, _, oy) = self.clean_offset();
2212
2213 if oy > self.len_lines() {
2214 return None;
2215 }
2216 if pos.y < oy {
2217 return None;
2218 }
2219 if pos.y > self.len_lines() {
2220 return None;
2221 }
2222 if pos.y - oy >= self.rendered.height as u32 {
2223 return None;
2224 }
2225
2226 let screen_y = (pos.y - oy) as u16;
2227
2228 let screen_x = 'f: {
2229 for g in self
2230 .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2231 .expect("valid-row")
2232 {
2233 if g.pos().x == pos.x {
2234 break 'f g.screen_pos().0;
2235 } else if g.line_break() {
2236 break 'f g.screen_pos().0;
2237 }
2238 }
2239 0
2241 };
2242 assert!(screen_x <= self.rendered.width);
2243
2244 Some((
2245 screen_x as i16 - self.dark_offset.0 as i16,
2246 screen_y as i16 - self.dark_offset.1 as i16,
2247 ))
2248 }
2249 TextWrap::Hard | TextWrap::Word(_) => {
2250 let (_, sub_row_offset, oy) = self.clean_offset();
2251
2252 if oy > self.len_lines() {
2253 return None;
2254 }
2255 if pos.y < oy {
2256 return None;
2257 }
2258 if pos.y > self.len_lines() {
2259 return None;
2260 }
2261
2262 let page = self.rendered.height as upos_type;
2263 let scr = (sub_row_offset, oy, page);
2264 self.stc_fill_screen_cache(scr);
2265 let (screen_y, start_pos) = if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2266 if pos_row >= page {
2267 return None;
2269 }
2270 let start_pos = self.stc_sub_row_offset(scr, pos_row);
2271 (pos_row, start_pos)
2272 } else {
2273 return None;
2275 };
2276
2277 let screen_x = 'f: {
2278 for g in self
2279 .glyphs2(
2280 0,
2281 start_pos.0,
2282 start_pos.1..min(start_pos.1 + 1, self.len_lines()),
2283 )
2284 .expect("valid-row")
2285 {
2286 if g.pos().x == pos.x {
2287 break 'f g.screen_pos().0;
2288 } else if g.line_break() {
2289 break 'f g.screen_pos().0;
2290 }
2291 }
2292 0
2294 };
2295 assert!(screen_x <= self.rendered.width);
2296
2297 let scr = (
2298 screen_x as i16 - self.dark_offset.0 as i16,
2299 screen_y as i16 - self.dark_offset.1 as i16,
2300 );
2301 Some(scr)
2302 }
2303 }
2304 }
2305
2306 #[inline]
2309 pub fn pos_to_screen(&self, pos: impl Into<TextPosition>) -> Option<(u16, u16)> {
2310 let scr_pos = self.pos_to_relative_screen(pos.into())?;
2311 if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
2312 Some((
2313 (scr_pos.0 + self.inner.x as i16) as u16,
2314 (scr_pos.1 + self.inner.y as i16) as u16,
2315 ))
2316 } else {
2317 None
2318 }
2319 }
2320
2321 pub fn pos_to_line_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
2323 let pos = pos.into();
2324 match self.text_wrap {
2325 TextWrap::Shift => {
2326 TextPosition::new(0, pos.y)
2328 }
2329 TextWrap::Hard | TextWrap::Word(_) => {
2330 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2331 .expect("valid-row");
2332
2333 let mut start_pos = TextPosition::new(0, pos.y);
2334 for (break_pos, _) in self.value.cache().line_break.borrow().range(
2335 TextPosition::new(0, pos.y)
2336 ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
2337 ) {
2338 if pos >= start_pos && &pos <= break_pos {
2339 break;
2340 }
2341 start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2342 }
2343
2344 start_pos
2345 }
2346 }
2347 }
2348
2349 pub fn pos_to_line_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
2351 let pos = pos.into();
2352
2353 self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2354 .expect("valid-row");
2355
2356 let mut end_pos = TextPosition::new(0, pos.y);
2357 for (break_pos, _) in self
2358 .value
2359 .cache()
2360 .line_break
2361 .borrow()
2362 .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2363 {
2364 if pos >= end_pos && &pos <= break_pos {
2365 end_pos = *break_pos;
2366 break;
2367 }
2368 end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2369 }
2370
2371 end_pos
2372 }
2373
2374 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2380 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2381 return false;
2382 };
2383 if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2384 self.set_move_col(Some(scr_cursor.0));
2385 }
2386 self.set_cursor(cursor, extend_selection)
2387 }
2388
2389 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2396 let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2397 return false;
2398 };
2399
2400 let anchor = self.anchor();
2401 let cursor = if cursor < anchor {
2402 self.word_start(cursor)
2403 } else {
2404 self.word_end(cursor)
2405 };
2406
2407 if !self.is_word_boundary(anchor) {
2409 if cursor < anchor {
2410 self.set_cursor(self.word_end(anchor), false);
2411 } else {
2412 self.set_cursor(self.word_start(anchor), false);
2413 }
2414 }
2415
2416 self.set_cursor(cursor, extend_selection)
2417 }
2418}
2419
2420impl TextAreaState {
2421 pub fn vertical_max_offset(&self) -> upos_type {
2426 self.vscroll.max_offset() as upos_type
2427 }
2428
2429 pub fn vertical_offset(&self) -> upos_type {
2431 self.vscroll.offset() as upos_type
2432 }
2433
2434 pub fn vertical_page(&self) -> upos_type {
2436 self.vscroll.page_len() as upos_type
2437 }
2438
2439 pub fn vertical_scroll(&self) -> upos_type {
2441 self.vscroll.scroll_by() as upos_type
2442 }
2443
2444 pub fn horizontal_max_offset(&self) -> upos_type {
2453 self.hscroll.max_offset() as upos_type
2454 }
2455
2456 pub fn horizontal_offset(&self) -> upos_type {
2458 self.hscroll.offset() as upos_type
2459 }
2460
2461 pub fn horizontal_page(&self) -> upos_type {
2463 self.hscroll.page_len() as upos_type
2464 }
2465
2466 pub fn horizontal_scroll(&self) -> upos_type {
2468 self.hscroll.scroll_by() as upos_type
2469 }
2470
2471 pub fn set_vertical_offset(&mut self, row_offset: upos_type) -> bool {
2478 self.scroll_to_cursor.set(false);
2479 self.sub_row_offset = 0;
2480 self.vscroll.set_offset(row_offset as usize)
2481 }
2482
2483 pub fn set_horizontal_offset(&mut self, col_offset: upos_type) -> bool {
2492 self.scroll_to_cursor.set(false);
2493 self.hscroll.set_offset(col_offset as usize)
2494 }
2495
2496 pub fn scroll_cursor_to_visible(&mut self) {
2502 self.scroll_to_cursor.set(true);
2503 }
2504
2505 pub fn scroll_to_pos(&mut self, pos: impl Into<TextPosition>) -> bool {
2511 let old_offset = self.clean_offset();
2512
2513 let pos = pos.into();
2514
2515 'f: {
2516 match self.text_wrap {
2517 TextWrap::Shift => {
2518 let (ox, _, oy) = old_offset;
2519
2520 let height = self.rendered.height as upos_type;
2521 let width = self.rendered.width as upos_type;
2522 let width = if self.show_ctrl() || self.wrap_ctrl() {
2523 width.saturating_sub(1)
2524 } else {
2525 width
2526 };
2527
2528 let noy = if pos.y < oy.saturating_sub(height) {
2529 pos.y.saturating_sub(height * 4 / 10)
2530 } else if pos.y < oy {
2531 pos.y
2532 } else if pos.y >= oy + 2 * height {
2533 pos.y.saturating_sub(height * 6 / 10)
2534 } else if pos.y >= oy + height {
2535 pos.y.saturating_sub(height.saturating_sub(1))
2536 } else {
2537 oy
2538 };
2539
2540 let nox = if pos.x < ox {
2541 pos.x
2542 } else if pos.x >= ox + width {
2543 pos.x.saturating_sub(width) + 1
2544 } else {
2545 ox
2546 };
2547
2548 self.set_offset((nox, noy));
2549 self.set_sub_row_offset(0);
2550 }
2551 TextWrap::Hard | TextWrap::Word(_) => {
2552 let (_ox, sub_row_offset, oy) = old_offset;
2553 let page = self.rendered.height as upos_type;
2554
2555 let scr = (0, oy.saturating_sub(page), 3 * page);
2557 self.stc_fill_screen_cache(scr);
2558 if let Some(off_row) =
2559 self.stc_screen_row(scr, TextPosition::new(sub_row_offset, oy))
2560 {
2561 if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2562 if pos_row < off_row && pos_row >= off_row.saturating_sub(page) {
2563 let noff_row = pos_row;
2564 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2565 self.set_offset((0, noy));
2566 self.set_sub_row_offset(nsub_row_offset);
2567 break 'f;
2568 } else if pos_row >= off_row + page && pos_row < off_row + 2 * page {
2569 let noff_row = pos_row.saturating_sub(page.saturating_sub(1));
2570 let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2571 self.set_offset((0, noy));
2572 self.set_sub_row_offset(nsub_row_offset);
2573 break 'f;
2574 } else if pos_row >= off_row && pos_row < off_row + page {
2575 break 'f;
2576 }
2577 }
2578 }
2579
2580 let alt_scr = (0, pos.y.saturating_sub(page), 3 * page);
2582 self.stc_fill_screen_cache(alt_scr);
2583 if let Some(alt_scr_row) = self.stc_screen_row(alt_scr, pos) {
2584 let noff_row = alt_scr_row.saturating_sub(page * 5 / 10);
2585 let (nsub_row_offset, noy) = self.stc_sub_row_offset(alt_scr, noff_row);
2586 self.set_offset((0, noy));
2587 self.set_sub_row_offset(nsub_row_offset);
2588 } else {
2589 self.set_offset((0, pos.y));
2590 self.set_sub_row_offset(0);
2591 }
2592 }
2593 }
2594 }
2595
2596 old_offset != self.clean_offset()
2597 }
2598
2599 pub fn scroll_to_row(&mut self, pos: upos_type) -> bool {
2608 self.scroll_to_cursor.set(false);
2609
2610 match self.text_wrap {
2611 TextWrap::Shift => self.vscroll.scroll_to_pos(pos as usize),
2612 TextWrap::Hard | TextWrap::Word(_) => self
2613 .vscroll
2614 .set_offset(self.vscroll.limited_offset(pos as usize)),
2615 }
2616 }
2617
2618 pub fn scroll_to_col(&mut self, pos: upos_type) -> bool {
2626 self.scroll_to_cursor.set(false);
2627 self.hscroll.set_offset(pos as usize)
2628 }
2629
2630 pub fn scroll_up(&mut self, delta: upos_type) -> bool {
2636 if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2637 self.sub_row_offset = pos.x;
2638 self.vscroll.set_offset(pos.y as usize);
2639 true
2640 } else {
2641 false
2642 }
2643 }
2644
2645 pub fn scroll_down(&mut self, delta: upos_type) -> bool {
2651 if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2652 self.sub_row_offset = pos.x;
2653 self.vscroll.set_offset(pos.y as usize);
2654 true
2655 } else {
2656 false
2657 }
2658 }
2659
2660 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
2672 self.hscroll
2673 .set_offset(self.hscroll.offset.saturating_add(delta as usize))
2674 }
2675
2676 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
2688 self.hscroll
2689 .set_offset(self.hscroll.offset.saturating_sub(delta as usize))
2690 }
2691}
2692
2693impl HandleEvent<Event, Regular, TextOutcome> for TextAreaState {
2694 fn handle(&mut self, event: &Event, _keymap: Regular) -> TextOutcome {
2695 fn tc(r: bool) -> TextOutcome {
2697 if r {
2698 TextOutcome::TextChanged
2699 } else {
2700 TextOutcome::Unchanged
2701 }
2702 }
2703
2704 let mut r =
2705 if self.is_focused() {
2706 match event {
2707 ct_event!(key press c)
2708 | ct_event!(key press SHIFT-c)
2709 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2710 ct_event!(keycode press Tab) => {
2711 tc(if !self.focus.gained() {
2713 self.insert_tab()
2714 } else {
2715 false
2716 })
2717 }
2718 ct_event!(keycode press SHIFT-BackTab) => {
2719 tc(if !self.focus.gained() {
2721 self.insert_backtab()
2722 } else {
2723 false
2724 })
2725 }
2726 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2727 ct_event!(keycode press Backspace)
2728 | ct_event!(keycode press SHIFT-Backspace) => tc(self.delete_prev_char()),
2729 ct_event!(keycode press Delete) | ct_event!(keycode press SHIFT-Delete) => {
2730 tc(self.delete_next_char())
2731 }
2732 ct_event!(keycode press CONTROL-Backspace)
2733 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2734 ct_event!(keycode press CONTROL-Delete)
2735 | ct_event!(keycode press ALT-Delete) => tc(self.delete_next_word()),
2736 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2737 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2738 ct_event!(paste v) => tc(self.insert_str(&v)),
2739 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2740 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2741 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2742 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2743
2744 ct_event!(key release _)
2745 | ct_event!(key release SHIFT-_)
2746 | ct_event!(key release CONTROL_ALT-_)
2747 | ct_event!(keycode release Tab)
2748 | ct_event!(keycode release Enter)
2749 | ct_event!(keycode release Backspace)
2750 | ct_event!(keycode release Delete)
2751 | ct_event!(keycode release CONTROL-Backspace)
2752 | ct_event!(keycode release ALT-Backspace)
2753 | ct_event!(keycode release CONTROL-Delete)
2754 | ct_event!(key release CONTROL-'x')
2755 | ct_event!(key release CONTROL-'v')
2756 | ct_event!(key release CONTROL-'d')
2757 | ct_event!(key release CONTROL-'y')
2758 | ct_event!(key release CONTROL-'z')
2759 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2760 _ => TextOutcome::Continue,
2761 }
2762 } else {
2763 TextOutcome::Continue
2764 };
2765 if r == TextOutcome::Continue {
2766 r = self.handle(event, ReadOnly);
2767 }
2768 r
2769 }
2770}
2771
2772pub const MATCH_STYLE: usize = 100_001;
2774
2775impl HandleEvent<Event, ReadOnly, TextOutcome> for TextAreaState {
2776 fn handle(&mut self, event: &Event, _keymap: ReadOnly) -> TextOutcome {
2777 let mut r = if self.is_focused() {
2778 match event {
2779 ct_event!(keycode press Left) => self.move_left(1, false).into(),
2780 ct_event!(keycode press Right) => self.move_right(1, false).into(),
2781 ct_event!(keycode press Up) => self.move_up(1, false).into(),
2782 ct_event!(keycode press Down) => self.move_down(1, false).into(),
2783 ct_event!(keycode press PageUp) => {
2784 self.move_up(self.vertical_page() as u16, false).into()
2785 }
2786 ct_event!(keycode press PageDown) => {
2787 self.move_down(self.vertical_page() as u16, false).into()
2788 }
2789 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
2790 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
2791 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
2792 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
2793 ct_event!(keycode press CONTROL-Up) => false.into(),
2794 ct_event!(keycode press CONTROL-Down) => false.into(),
2795 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
2796 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
2797 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
2798 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
2799
2800 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
2801 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
2802 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
2803 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
2804 ct_event!(keycode press ALT-PageUp) => {
2805 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
2806 }
2807 ct_event!(keycode press ALT-PageDown) => {
2808 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
2809 }
2810 ct_event!(keycode press ALT_SHIFT-PageUp) => {
2811 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
2812 }
2813 ct_event!(keycode press ALT_SHIFT-PageDown) => {
2814 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
2815 }
2816
2817 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
2818 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
2819 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
2820 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
2821 ct_event!(keycode press SHIFT-PageUp) => {
2822 self.move_up(self.vertical_page() as u16, true).into()
2823 }
2824 ct_event!(keycode press SHIFT-PageDown) => {
2825 self.move_down(self.vertical_page() as u16, true).into()
2826 }
2827 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
2828 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
2829 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
2830 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
2831 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
2832 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
2833 ct_event!(key press CONTROL-'a') => self.select_all().into(),
2834 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
2835
2836 ct_event!(keycode press F(3)) => self.move_to_next_match().into(),
2837 ct_event!(keycode press SHIFT-F(3)) => self.move_to_prev_match().into(),
2838
2839 ct_event!(keycode release Left)
2840 | ct_event!(keycode release Right)
2841 | ct_event!(keycode release Up)
2842 | ct_event!(keycode release Down)
2843 | ct_event!(keycode release PageUp)
2844 | ct_event!(keycode release PageDown)
2845 | ct_event!(keycode release Home)
2846 | ct_event!(keycode release End)
2847 | ct_event!(keycode release CONTROL-Left)
2848 | ct_event!(keycode release CONTROL-Right)
2849 | ct_event!(keycode release CONTROL-Up)
2850 | ct_event!(keycode release CONTROL-Down)
2851 | ct_event!(keycode release CONTROL-PageUp)
2852 | ct_event!(keycode release CONTROL-PageDown)
2853 | ct_event!(keycode release CONTROL-Home)
2854 | ct_event!(keycode release CONTROL-End)
2855 | ct_event!(keycode release ALT-Left)
2856 | ct_event!(keycode release ALT-Right)
2857 | ct_event!(keycode release ALT-Up)
2858 | ct_event!(keycode release ALT-Down)
2859 | ct_event!(keycode release ALT-PageUp)
2860 | ct_event!(keycode release ALT-PageDown)
2861 | ct_event!(keycode release ALT_SHIFT-PageUp)
2862 | ct_event!(keycode release ALT_SHIFT-PageDown)
2863 | ct_event!(keycode release SHIFT-Left)
2864 | ct_event!(keycode release SHIFT-Right)
2865 | ct_event!(keycode release SHIFT-Up)
2866 | ct_event!(keycode release SHIFT-Down)
2867 | ct_event!(keycode release SHIFT-PageUp)
2868 | ct_event!(keycode release SHIFT-PageDown)
2869 | ct_event!(keycode release SHIFT-Home)
2870 | ct_event!(keycode release SHIFT-End)
2871 | ct_event!(keycode release CONTROL_SHIFT-Left)
2872 | ct_event!(keycode release CONTROL_SHIFT-Right)
2873 | ct_event!(keycode release CONTROL_SHIFT-Home)
2874 | ct_event!(keycode release CONTROL_SHIFT-End)
2875 | ct_event!(key release CONTROL-'a')
2876 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2877 _ => TextOutcome::Continue,
2878 }
2879 } else {
2880 TextOutcome::Continue
2881 };
2882
2883 if r == TextOutcome::Continue {
2884 r = self.handle(event, MouseOnly);
2885 }
2886 r
2887 }
2888}
2889
2890impl HandleEvent<Event, MouseOnly, TextOutcome> for TextAreaState {
2891 fn handle(&mut self, event: &Event, _keymap: MouseOnly) -> TextOutcome {
2892 if self.is_obscured() {
2893 return TextOutcome::Continue;
2894 }
2895
2896 flow!(match event {
2897 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2898 let cx = m.column as i16 - self.inner.x as i16;
2899 let cy = m.row as i16 - self.inner.y as i16;
2900 self.set_screen_cursor((cx, cy), true).into()
2901 }
2902 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2903 let cx = m.column as i16 - self.inner.x as i16;
2904 let cy = m.row as i16 - self.inner.y as i16;
2905 self.set_screen_cursor_words((cx, cy), true).into()
2906 }
2907 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2908 if let Some(test) = self.screen_to_pos((m.column, m.row)) {
2909 let start = self.word_start(test);
2910 let end = self.word_end(test);
2911 self.set_selection(start, end).into()
2912 } else {
2913 TextOutcome::Unchanged
2914 }
2915 }
2916 ct_event!(mouse down Left for column,row) => {
2917 if self.inner.contains((*column, *row).into()) {
2918 let cx = (column - self.inner.x) as i16;
2919 let cy = (row - self.inner.y) as i16;
2920 self.set_screen_cursor((cx, cy), false).into()
2921 } else {
2922 TextOutcome::Continue
2923 }
2924 }
2925 ct_event!(mouse down SHIFT-Left for column,row) => {
2926 if self.inner.contains((*column, *row).into()) {
2927 let cx = (column - self.inner.x) as i16;
2928 let cy = (row - self.inner.y) as i16;
2929 self.set_screen_cursor((cx, cy), true).into()
2930 } else {
2931 TextOutcome::Continue
2932 }
2933 }
2934 ct_event!(mouse down CONTROL-Left for column,row) => {
2935 if self.inner.contains((*column, *row).into()) {
2936 let cx = (column - self.inner.x) as i16;
2937 let cy = (row - self.inner.y) as i16;
2938 self.set_screen_cursor((cx, cy), true).into()
2939 } else {
2940 TextOutcome::Continue
2941 }
2942 }
2943 ct_event!(mouse down ALT-Left for column,row) => {
2944 if self.inner.contains((*column, *row).into()) {
2945 let cx = (column - self.inner.x) as i16;
2946 let cy = (row - self.inner.y) as i16;
2947 self.set_screen_cursor_words((cx, cy), true).into()
2948 } else {
2949 TextOutcome::Continue
2950 }
2951 }
2952 _ => TextOutcome::Continue,
2953 });
2954
2955 let mut sas = ScrollAreaState::new()
2956 .area(self.inner)
2957 .h_scroll(&mut self.hscroll)
2958 .v_scroll(&mut self.vscroll);
2959 let r = match sas.handle(event, MouseOnly) {
2960 ScrollOutcome::Up(v) => self.scroll_up(v as upos_type),
2961 ScrollOutcome::Down(v) => self.scroll_down(v as upos_type),
2962 ScrollOutcome::Left(v) => self.scroll_left(v as upos_type),
2963 ScrollOutcome::Right(v) => self.scroll_right(v as upos_type),
2964 ScrollOutcome::VPos(v) => self.scroll_to_row(v as upos_type),
2965 ScrollOutcome::HPos(v) => self.scroll_to_col(v as upos_type),
2966 _ => false,
2967 };
2968 if r {
2969 return TextOutcome::Changed;
2970 }
2971
2972 TextOutcome::Continue
2973 }
2974}
2975
2976pub fn handle_events(state: &mut TextAreaState, focus: bool, event: &Event) -> TextOutcome {
2980 state.focus.set(focus);
2981 state.handle(event, Regular)
2982}
2983
2984pub fn handle_readonly_events(
2988 state: &mut TextAreaState,
2989 focus: bool,
2990 event: &Event,
2991) -> TextOutcome {
2992 state.focus.set(focus);
2993 state.handle(event, ReadOnly)
2994}
2995
2996pub fn handle_mouse_events(state: &mut TextAreaState, event: &Event) -> TextOutcome {
2998 state.handle(event, MouseOnly)
2999}