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