1use crate::_private::NonExhaustive;
7use crate::clipboard::{global_clipboard, Clipboard};
8use crate::event::{ReadOnly, TextOutcome};
9use crate::grapheme::{Glyph, Grapheme};
10use crate::text_core::TextCore;
11use crate::text_store::text_rope::TextRope;
12use crate::text_store::TextStore;
13use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
14use crate::{
15 ipos_type, upos_type, Cursor, HasScreenCursor, TextError, TextPosition, TextRange, TextStyle,
16};
17use crossterm::event::KeyModifiers;
18use rat_event::util::MouseFlags;
19use rat_event::{ct_event, flow, HandleEvent, MouseOnly, Regular};
20use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
21use rat_reloc::{relocate_area, relocate_dark_offset, RelocatableState};
22use rat_scrolled::event::ScrollOutcome;
23use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState};
24use ratatui::buffer::Buffer;
25use ratatui::layout::Rect;
26use ratatui::style::{Style, Stylize};
27use ratatui::widgets::{Block, StatefulWidget};
28use ropey::Rope;
29use std::borrow::Cow;
30use std::cmp::{max, min};
31use std::collections::HashMap;
32use std::ops::Range;
33
34#[derive(Debug, Default, Clone)]
73pub struct TextArea<'a> {
74 block: Option<Block<'a>>,
75 hscroll: Option<Scroll<'a>>,
76 h_max_offset: Option<usize>,
77 h_overscroll: Option<usize>,
78 vscroll: Option<Scroll<'a>>,
79
80 style: Style,
81 focus_style: Option<Style>,
82 select_style: Option<Style>,
83 text_style: HashMap<usize, Style>,
84}
85
86#[derive(Debug)]
88pub struct TextAreaState {
89 pub area: Rect,
92 pub inner: Rect,
95
96 pub hscroll: ScrollState,
99 pub vscroll: ScrollState,
102 pub dark_offset: (u16, u16),
105 pub scroll_to_cursor: bool,
111
112 pub value: TextCore<TextRope>,
114
115 pub move_col: Option<upos_type>,
117 pub auto_indent: bool,
119 pub auto_quote: bool,
121
122 pub focus: FocusFlag,
124
125 pub mouse: MouseFlags,
128
129 pub non_exhaustive: NonExhaustive,
130}
131
132impl Clone for TextAreaState {
133 fn clone(&self) -> Self {
134 Self {
135 focus: FocusFlag::named(self.focus.name()),
136 area: self.area,
137 inner: self.inner,
138 value: self.value.clone(),
139 hscroll: self.hscroll.clone(),
140 vscroll: self.vscroll.clone(),
141 move_col: None,
142 auto_indent: self.auto_indent,
143 auto_quote: self.auto_quote,
144 mouse: Default::default(),
145 dark_offset: self.dark_offset,
146 scroll_to_cursor: self.scroll_to_cursor,
147 non_exhaustive: NonExhaustive,
148 }
149 }
150}
151
152impl<'a> TextArea<'a> {
153 pub fn new() -> Self {
155 Self::default()
156 }
157
158 #[inline]
160 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
161 if let Some(styles) = styles {
162 self.styles(styles)
163 } else {
164 self
165 }
166 }
167
168 #[inline]
170 pub fn styles(mut self, styles: TextStyle) -> Self {
171 self.style = styles.style;
172 if styles.focus.is_some() {
173 self.focus_style = styles.focus;
174 }
175 if styles.select.is_some() {
176 self.select_style = styles.select;
177 }
178 if let Some(border_style) = styles.border_style {
179 self.block = self.block.map(|v| v.border_style(border_style));
180 }
181 self.block = self.block.map(|v| v.style(self.style));
182 if styles.block.is_some() {
183 self.block = styles.block;
184 }
185 if let Some(styles) = styles.scroll {
186 self.hscroll = self.hscroll.map(|v| v.styles(styles.clone()));
187 self.vscroll = self.vscroll.map(|v| v.styles(styles));
188 }
189 self
190 }
191
192 pub fn style(mut self, style: Style) -> Self {
194 self.style = style;
195 self
196 }
197
198 pub fn focus_style(mut self, style: Style) -> Self {
200 self.focus_style = Some(style);
201 self.block = self.block.map(|v| v.style(self.style));
202 self
203 }
204
205 pub fn select_style(mut self, style: Style) -> Self {
207 self.select_style = Some(style);
208 self
209 }
210
211 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
216 for (i, s) in styles.into_iter().enumerate() {
217 self.text_style.insert(i, s);
218 }
219 self
220 }
221
222 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
227 for (i, s) in styles.into_iter() {
228 self.text_style.insert(i, s.into());
229 }
230 self
231 }
232
233 #[inline]
235 pub fn block(mut self, block: Block<'a>) -> Self {
236 self.block = Some(block);
237 self
238 }
239
240 pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
242 self.hscroll = Some(scroll.clone().override_horizontal());
243 self.vscroll = Some(scroll.override_vertical());
244 self
245 }
246
247 pub fn hscroll(mut self, scroll: Scroll<'a>) -> Self {
249 self.hscroll = Some(scroll.override_horizontal());
250 self
251 }
252
253 pub fn set_horizontal_max_offset(mut self, offset: usize) -> Self {
261 self.h_max_offset = Some(offset);
262 self
263 }
264
265 pub fn set_horizontal_overscroll(mut self, overscroll: usize) -> Self {
270 self.h_overscroll = Some(overscroll);
271 self
272 }
273
274 pub fn vscroll(mut self, scroll: Scroll<'a>) -> Self {
276 self.vscroll = Some(scroll.override_vertical());
277 self
278 }
279}
280
281impl<'a> StatefulWidget for &TextArea<'a> {
282 type State = TextAreaState;
283
284 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
285 render_text_area(self, area, buf, state);
286 }
287}
288
289impl StatefulWidget for TextArea<'_> {
290 type State = TextAreaState;
291
292 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
293 render_text_area(&self, area, buf, state);
294 }
295}
296
297fn render_text_area(
298 widget: &TextArea<'_>,
299 area: Rect,
300 buf: &mut Buffer,
301 state: &mut TextAreaState,
302) {
303 state.area = area;
304
305 let style = widget.style;
306 let select_style = if let Some(select_style) = widget.select_style {
307 if select_style.fg.is_none() && select_style.bg.is_none() {
309 select_style
310 } else {
311 style.patch(select_style)
312 }
313 } else {
314 Style::default().black().on_yellow()
315 };
316
317 let sa = ScrollArea::new()
318 .block(widget.block.as_ref())
319 .h_scroll(widget.hscroll.as_ref())
320 .v_scroll(widget.vscroll.as_ref())
321 .style(style);
322
323 state.inner = sa.inner(area, Some(&state.hscroll), Some(&state.vscroll));
324
325 if let Some(h_max_offset) = widget.h_max_offset {
326 state.hscroll.set_max_offset(h_max_offset);
327 }
328 if let Some(h_overscroll) = widget.h_overscroll {
329 state.hscroll.set_overscroll_by(Some(h_overscroll));
330 }
331 state.hscroll.set_page_len(state.inner.width as usize);
332 state.vscroll.set_max_offset(
333 state
334 .len_lines()
335 .saturating_sub(state.inner.height as upos_type) as usize,
336 );
337 state.vscroll.set_page_len(state.inner.height as usize);
338
339 if state.scroll_to_cursor {
340 let cursor = state.cursor();
341 let (ox, oy) = state.offset();
342 let (ox, oy) = (ox as upos_type, oy as upos_type);
343 let mut noy = if cursor.y < oy {
344 cursor.y
345 } else if cursor.y >= oy + (state.inner.height + state.dark_offset.1) as upos_type {
346 cursor
347 .y
348 .saturating_sub((state.inner.height + state.dark_offset.1) as upos_type)
349 } else {
350 oy
351 };
352 if cursor.y == noy + (state.inner.height + state.dark_offset.1) as upos_type {
355 noy = noy.saturating_add(1);
356 }
357
358 let mut nox = if cursor.x < ox {
359 cursor.x
360 } else if cursor.x >= ox + (state.inner.width + state.dark_offset.0) as upos_type {
361 cursor
362 .x
363 .saturating_sub((state.inner.width + state.dark_offset.0) as upos_type)
364 } else {
365 ox
366 };
367 if cursor.x == nox + (state.inner.width + state.dark_offset.0) as upos_type {
370 nox = nox.saturating_add(1);
371 }
372
373 state.set_offset((nox as usize, noy as usize));
374 }
375
376 let inner = state.inner;
377
378 sa.render(
380 area,
381 buf,
382 &mut ScrollAreaState::new()
383 .h_scroll(&mut state.hscroll)
384 .v_scroll(&mut state.vscroll),
385 );
386
387 if inner.width == 0 || inner.height == 0 {
388 return;
390 }
391
392 if state.vscroll.offset() > state.value.len_lines() as usize {
393 return;
394 }
395
396 let (ox, oy) = state.offset();
397 let page_rows = (oy as upos_type)
398 ..min(
399 oy as upos_type + inner.height as upos_type,
400 state.value.len_lines(),
401 );
402 let page_bytes = state
403 .try_bytes_at_range(TextRange::new((0, page_rows.start), (0, page_rows.end)))
404 .expect("valid_rows");
405 let selection = state.selection();
406 let mut styles = Vec::new();
407
408 let glyph_iter = state
409 .value
410 .glyphs(page_rows.clone(), ox as u16, inner.width)
411 .expect("valid_offset");
412
413 for g in glyph_iter {
414 if g.screen_width() > 0 {
415 let mut style = style;
416 styles.clear();
418 state
419 .value
420 .styles_at_page(page_bytes.clone(), g.text_bytes().start, &mut styles);
421 for style_nr in &styles {
422 if let Some(s) = widget.text_style.get(style_nr) {
423 style = style.patch(*s);
424 }
425 }
426 if selection.contains_pos(g.pos()) {
428 style = style.patch(select_style);
429 };
430
431 let screen_pos = g.screen_pos();
433
434 if let Some(cell) = buf.cell_mut((inner.x + screen_pos.0, inner.y + screen_pos.1)) {
436 cell.set_symbol(g.glyph());
437 cell.set_style(style);
438 }
439 for d in 1..g.screen_width() {
441 if let Some(cell) =
442 buf.cell_mut((inner.x + screen_pos.0 + d, inner.y + screen_pos.1))
443 {
444 cell.reset();
445 cell.set_style(style);
446 }
447 }
448 }
449 }
450}
451
452impl Default for TextAreaState {
453 fn default() -> Self {
454 let mut s = Self {
455 focus: Default::default(),
456 area: Default::default(),
457 inner: Default::default(),
458 mouse: Default::default(),
459 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
460 hscroll: Default::default(),
461 non_exhaustive: NonExhaustive,
462 vscroll: Default::default(),
463 move_col: Default::default(),
464 auto_indent: true,
465 auto_quote: true,
466 dark_offset: Default::default(),
467 scroll_to_cursor: Default::default(),
468 };
469 s.hscroll.set_max_offset(255);
470 s.hscroll.set_overscroll_by(Some(16384));
471 s
472 }
473}
474
475impl HasFocus for TextAreaState {
476 fn build(&self, builder: &mut FocusBuilder) {
477 builder.leaf_widget(self);
478 }
479
480 fn focus(&self) -> FocusFlag {
481 self.focus.clone()
482 }
483
484 fn area(&self) -> Rect {
485 self.area
486 }
487
488 fn navigable(&self) -> Navigation {
489 Navigation::Reach
490 }
491}
492
493impl TextAreaState {
494 #[inline]
496 pub fn new() -> Self {
497 Self::default()
498 }
499
500 #[inline]
502 pub fn named(name: &str) -> Self {
503 Self {
504 focus: FocusFlag::named(name),
505 ..Default::default()
506 }
507 }
508
509 #[inline]
515 pub fn set_newline(&mut self, br: impl Into<String>) {
516 self.value.set_newline(br.into());
517 }
518
519 #[inline]
521 pub fn newline(&self) -> &str {
522 self.value.newline()
523 }
524
525 #[inline]
527 pub fn set_auto_indent(&mut self, indent: bool) {
528 self.auto_indent = indent;
529 }
530
531 #[inline]
533 pub fn set_auto_quote(&mut self, quote: bool) {
534 self.auto_quote = quote;
535 }
536
537 #[inline]
539 pub fn set_tab_width(&mut self, tabs: u16) {
540 self.value.set_tab_width(tabs);
541 }
542
543 #[inline]
545 pub fn tab_width(&self) -> u16 {
546 self.value.tab_width()
547 }
548
549 #[inline]
551 pub fn set_expand_tabs(&mut self, expand: bool) {
552 self.value.set_expand_tabs(expand);
553 }
554
555 #[inline]
557 pub fn expand_tabs(&self) -> bool {
558 self.value.expand_tabs()
559 }
560
561 #[inline]
563 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
564 self.value.set_glyph_ctrl(show_ctrl);
565 }
566
567 pub fn show_ctrl(&self) -> bool {
569 self.value.glyph_ctrl()
570 }
571
572 #[inline]
579 pub fn set_move_col(&mut self, col: Option<upos_type>) {
580 self.move_col = col;
581 }
582
583 #[inline]
585 pub fn move_col(&mut self) -> Option<upos_type> {
586 self.move_col
587 }
588}
589
590impl TextAreaState {
591 #[inline]
594 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
595 match clip {
596 None => self.value.set_clipboard(None),
597 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
598 }
599 }
600
601 #[inline]
604 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
605 self.value.clipboard()
606 }
607
608 #[inline]
610 pub fn copy_to_clip(&mut self) -> bool {
611 let Some(clip) = self.value.clipboard() else {
612 return false;
613 };
614
615 _ = clip.set_string(self.selected_text().as_ref());
616 false
617 }
618
619 #[inline]
621 pub fn cut_to_clip(&mut self) -> bool {
622 let Some(clip) = self.value.clipboard() else {
623 return false;
624 };
625
626 match clip.set_string(self.selected_text().as_ref()) {
627 Ok(_) => self.delete_range(self.selection()),
628 Err(_) => false,
629 }
630 }
631
632 #[inline]
634 pub fn paste_from_clip(&mut self) -> bool {
635 let Some(clip) = self.value.clipboard() else {
636 return false;
637 };
638
639 if let Ok(text) = clip.get_string() {
640 self.insert_str(text)
641 } else {
642 false
643 }
644 }
645}
646
647impl TextAreaState {
648 #[inline]
650 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
651 match undo {
652 None => self.value.set_undo_buffer(None),
653 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
654 }
655 }
656
657 #[inline]
659 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
660 self.value.undo_buffer()
661 }
662
663 #[inline]
665 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
666 self.value.undo_buffer_mut()
667 }
668
669 #[inline]
671 pub fn begin_undo_seq(&mut self) {
672 self.value.begin_undo_seq()
673 }
674
675 #[inline]
677 pub fn end_undo_seq(&mut self) {
678 self.value.end_undo_seq()
679 }
680
681 #[inline]
683 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
684 self.value.recent_replay_log()
685 }
686
687 #[inline]
689 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
690 self.value.replay_log(replay)
691 }
692
693 #[inline]
695 pub fn undo(&mut self) -> bool {
696 self.value.undo()
697 }
698
699 #[inline]
701 pub fn redo(&mut self) -> bool {
702 self.value.redo()
703 }
704}
705
706impl TextAreaState {
707 #[inline]
718 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
719 self.value.set_styles(styles);
720 }
721
722 #[inline]
727 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
728 self.value.add_style(range, style);
729 }
730
731 #[inline]
735 pub fn add_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
736 let r = self.value.bytes_at_range(range)?;
737 self.value.add_style(r, style);
738 Ok(())
739 }
740
741 #[inline]
743 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
744 self.value.remove_style(range, style);
745 }
746
747 #[inline]
749 pub fn remove_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
750 let r = self.value.bytes_at_range(range)?;
751 self.value.remove_style(r, style);
752 Ok(())
753 }
754
755 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
757 self.value.styles_in(range, buf)
758 }
759
760 #[inline]
762 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
763 self.value.styles_at(byte_pos, buf)
764 }
765
766 #[inline]
769 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
770 self.value.style_match(byte_pos, style)
771 }
772
773 #[inline]
775 pub fn styles(&self) -> impl Iterator<Item = (Range<usize>, usize)> + '_ {
776 self.value.styles().expect("styles")
777 }
778}
779
780impl TextAreaState {
781 #[inline]
783 pub fn offset(&self) -> (usize, usize) {
784 (self.hscroll.offset(), self.vscroll.offset())
785 }
786
787 #[inline]
789 pub fn set_offset(&mut self, offset: (usize, usize)) -> bool {
790 self.scroll_to_cursor = false;
791 let c = self.hscroll.set_offset(offset.0);
792 let r = self.vscroll.set_offset(offset.1);
793 r || c
794 }
795
796 #[inline]
798 pub fn cursor(&self) -> TextPosition {
799 self.value.cursor()
800 }
801
802 #[inline]
804 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
805 self.scroll_cursor_to_visible();
806 self.value.set_cursor(cursor.into(), extend_selection)
807 }
808
809 #[inline]
811 pub fn anchor(&self) -> TextPosition {
812 self.value.anchor()
813 }
814
815 #[inline]
817 pub fn has_selection(&self) -> bool {
818 self.value.has_selection()
819 }
820
821 #[inline]
823 pub fn selection(&self) -> TextRange {
824 self.value.selection()
825 }
826
827 #[inline]
830 pub fn set_selection(
831 &mut self,
832 anchor: impl Into<TextPosition>,
833 cursor: impl Into<TextPosition>,
834 ) -> bool {
835 self.scroll_cursor_to_visible();
836 self.value.set_selection(anchor.into(), cursor.into())
837 }
838
839 #[inline]
842 pub fn select_all(&mut self) -> bool {
843 self.scroll_cursor_to_visible();
844 self.value.select_all()
845 }
846
847 #[inline]
849 pub fn selected_text(&self) -> Cow<'_, str> {
850 self.value
851 .str_slice(self.value.selection())
852 .expect("valid_selection")
853 }
854}
855
856impl TextAreaState {
857 #[inline]
859 pub fn is_empty(&self) -> bool {
860 self.value.is_empty()
861 }
862
863 #[inline]
865 pub fn rope(&self) -> &Rope {
866 self.value.text().rope()
867 }
868
869 #[inline]
871 pub fn text(&self) -> String {
872 self.value.text().string()
873 }
874
875 #[inline]
877 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
878 self.value.str_slice_byte(range).expect("valid_range")
879 }
880
881 #[inline]
883 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
884 self.value.str_slice_byte(range)
885 }
886
887 #[inline]
889 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
890 self.value.str_slice(range.into()).expect("valid_range")
891 }
892
893 #[inline]
895 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
896 self.value.str_slice(range.into())
897 }
898
899 #[inline]
901 pub fn len_lines(&self) -> upos_type {
902 self.value.len_lines()
903 }
904
905 #[inline]
907 pub fn line_width(&self, row: upos_type) -> upos_type {
908 self.value.line_width(row).expect("valid_row")
909 }
910
911 #[inline]
913 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
914 self.value.line_width(row)
915 }
916
917 #[inline]
920 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
921 self.value.line_at(row).expect("valid_row")
922 }
923
924 #[inline]
927 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
928 self.value.line_at(row)
929 }
930
931 #[inline]
933 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
934 self.value.lines_at(row).expect("valid_row")
935 }
936
937 #[inline]
939 pub fn try_lines_at(
940 &self,
941 row: upos_type,
942 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
943 self.value.lines_at(row)
944 }
945
946 #[inline]
949 pub fn glyphs(
950 &self,
951 rows: Range<upos_type>,
952 screen_offset: u16,
953 screen_width: u16,
954 ) -> impl Iterator<Item = Glyph<'_>> {
955 self.value
956 .glyphs(rows, screen_offset, screen_width)
957 .expect("valid_rows")
958 }
959
960 #[inline]
963 pub fn try_glyphs(
964 &self,
965 rows: Range<upos_type>,
966 screen_offset: u16,
967 screen_width: u16,
968 ) -> Result<impl Iterator<Item = Glyph<'_>>, TextError> {
969 self.value.glyphs(rows, screen_offset, screen_width)
970 }
971
972 #[inline]
975 pub fn line_graphemes(&self, row: upos_type) -> impl Iterator<Item = Grapheme<'_>> {
976 self.value.line_graphemes(row).expect("valid_row")
977 }
978
979 #[inline]
982 pub fn try_line_graphemes(
983 &self,
984 row: upos_type,
985 ) -> Result<impl Iterator<Item = Grapheme<'_>>, TextError> {
986 self.value.line_graphemes(row)
987 }
988
989 #[inline]
991 pub fn text_graphemes(&self, pos: TextPosition) -> impl Cursor<Item = Grapheme<'_>> {
992 self.value.text_graphemes(pos).expect("valid_pos")
993 }
994
995 #[inline]
997 pub fn try_text_graphemes(
998 &self,
999 pos: TextPosition,
1000 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1001 self.value.text_graphemes(pos)
1002 }
1003
1004 #[inline]
1006 pub fn graphemes(
1007 &self,
1008 range: TextRange,
1009 pos: TextPosition,
1010 ) -> impl Cursor<Item = Grapheme<'_>> {
1011 self.value.graphemes(range, pos).expect("valid_args")
1012 }
1013
1014 #[inline]
1016 pub fn try_graphemes(
1017 &self,
1018 range: TextRange,
1019 pos: TextPosition,
1020 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1021 self.value.graphemes(range, pos)
1022 }
1023
1024 #[inline]
1027 pub fn byte_at(&self, pos: TextPosition) -> Range<usize> {
1028 self.value.byte_at(pos).expect("valid_pos")
1029 }
1030
1031 #[inline]
1034 pub fn try_byte_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError> {
1035 self.value.byte_at(pos)
1036 }
1037
1038 #[inline]
1040 pub fn try_bytes_at_range(&self, range: TextRange) -> Result<Range<usize>, TextError> {
1041 self.value.bytes_at_range(range)
1042 }
1043
1044 #[inline]
1046 pub fn bytes_at_range(&self, range: TextRange) -> Range<usize> {
1047 self.value.bytes_at_range(range).expect("valid_range")
1048 }
1049
1050 #[inline]
1053 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1054 self.value.byte_pos(byte).expect("valid_pos")
1055 }
1056
1057 #[inline]
1060 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1061 self.value.byte_pos(byte)
1062 }
1063
1064 #[inline]
1066 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1067 self.value.byte_range(bytes).expect("valid_range")
1068 }
1069
1070 #[inline]
1072 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1073 self.value.byte_range(bytes)
1074 }
1075}
1076
1077impl TextAreaState {
1078 #[inline]
1080 pub fn clear(&mut self) -> bool {
1081 if !self.is_empty() {
1082 self.value.clear();
1083 true
1084 } else {
1085 false
1086 }
1087 }
1088
1089 #[inline]
1092 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1093 self.scroll_to_cursor = false;
1094 self.vscroll.set_offset(0);
1095 self.hscroll.set_offset(0);
1096
1097 self.value.set_text(TextRope::new_text(s.as_ref()));
1098 }
1099
1100 #[inline]
1103 pub fn set_rope(&mut self, r: Rope) {
1104 self.scroll_to_cursor = false;
1105 self.vscroll.set_offset(0);
1106 self.hscroll.set_offset(0);
1107
1108 self.value.set_text(TextRope::new_rope(r));
1109 }
1110
1111 pub fn insert_char(&mut self, c: char) -> bool {
1118 let mut insert = true;
1119 if self.has_selection() {
1120 if self.auto_quote
1121 && (c == '\''
1122 || c == '"'
1123 || c == '`'
1124 || c == '<'
1125 || c == '['
1126 || c == '('
1127 || c == '{')
1128 {
1129 self.value
1130 .insert_quotes(self.selection(), c)
1131 .expect("valid_selection");
1132 insert = false;
1133 } else {
1134 self.value
1135 .remove_str_range(self.selection())
1136 .expect("valid_selection");
1137 }
1138 }
1139
1140 if insert {
1141 if c == '\n' {
1142 self.value
1143 .insert_newline(self.cursor())
1144 .expect("valid_cursor");
1145 } else if c == '\t' {
1146 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1147 } else {
1148 self.value
1149 .insert_char(self.cursor(), c)
1150 .expect("valid_cursor");
1151 }
1152 }
1153
1154 self.scroll_cursor_to_visible();
1155
1156 true
1157 }
1158
1159 pub fn insert_tab(&mut self) -> bool {
1165 if self.has_selection() {
1166 if self.auto_indent {
1167 let sel = self.selection();
1168 let indent = " ".repeat(self.tab_width() as usize);
1169
1170 self.value.begin_undo_seq();
1171 for r in sel.start.y..=sel.end.y {
1172 self.value
1173 .insert_str(TextPosition::new(0, r), &indent)
1174 .expect("valid_row");
1175 }
1176 self.value.end_undo_seq();
1177
1178 true
1179 } else {
1180 false
1181 }
1182 } else {
1183 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1184 self.scroll_cursor_to_visible();
1185
1186 true
1187 }
1188 }
1189
1190 pub fn insert_backtab(&mut self) -> bool {
1195 let sel = self.selection();
1196
1197 self.value.begin_undo_seq();
1198 for r in sel.start.y..=sel.end.y {
1199 let mut idx = 0;
1200 let g_it = self
1201 .value
1202 .graphemes(TextRange::new((0, r), (0, r + 1)), TextPosition::new(0, r))
1203 .expect("valid_range")
1204 .take(self.tab_width() as usize);
1205 for g in g_it {
1206 if g != " " && g != "\t" {
1207 break;
1208 }
1209 idx += 1;
1210 }
1211
1212 self.value
1213 .remove_str_range(TextRange::new((0, r), (idx, r)))
1214 .expect("valid_range");
1215 }
1216 self.value.end_undo_seq();
1217
1218 true
1219 }
1220
1221 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1224 let t = t.as_ref();
1225 if self.has_selection() {
1226 self.value
1227 .remove_str_range(self.selection())
1228 .expect("valid_selection");
1229 }
1230 self.value
1231 .insert_str(self.cursor(), t)
1232 .expect("valid_cursor");
1233 self.scroll_cursor_to_visible();
1234 true
1235 }
1236
1237 pub fn insert_newline(&mut self) -> bool {
1242 if self.has_selection() {
1243 self.value
1244 .remove_str_range(self.selection())
1245 .expect("valid_selection");
1246 }
1247 self.value
1248 .insert_newline(self.cursor())
1249 .expect("valid_cursor");
1250
1251 if self.auto_indent {
1253 let cursor = self.cursor();
1254 if cursor.y > 0 {
1255 let mut blanks = String::new();
1256 for g in self.line_graphemes(cursor.y - 1) {
1257 if g == " " || g == "\t" {
1258 blanks.push_str(g.grapheme());
1259 } else {
1260 break;
1261 }
1262 }
1263 if !blanks.is_empty() {
1264 self.value
1265 .insert_str(cursor, &blanks)
1266 .expect("valid_cursor");
1267 }
1268 }
1269 }
1270
1271 self.scroll_cursor_to_visible();
1272 true
1273 }
1274
1275 #[inline]
1277 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1278 self.try_delete_range(range).expect("valid_range")
1279 }
1280
1281 #[inline]
1283 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1284 let range = range.into();
1285 if !range.is_empty() {
1286 self.value.remove_str_range(range)?;
1287 self.scroll_cursor_to_visible();
1288 Ok(true)
1289 } else {
1290 Ok(false)
1291 }
1292 }
1293}
1294
1295impl TextAreaState {
1296 pub fn duplicate_text(&mut self) -> bool {
1299 if self.has_selection() {
1300 let sel_range = self.selection();
1301 if !sel_range.is_empty() {
1302 let v = self.str_slice(sel_range).to_string();
1303 self.value
1304 .insert_str(sel_range.end, &v)
1305 .expect("valid_selection");
1306 true
1307 } else {
1308 false
1309 }
1310 } else {
1311 let pos = self.cursor();
1312 let row_range = TextRange::new((0, pos.y), (0, pos.y + 1));
1313 let v = self.str_slice(row_range).to_string();
1314 self.value
1315 .insert_str(row_range.start, &v)
1316 .expect("valid_cursor");
1317 true
1318 }
1319 }
1320
1321 pub fn delete_line(&mut self) -> bool {
1324 let pos = self.cursor();
1325 if pos.y + 1 < self.len_lines() {
1326 self.delete_range(TextRange::new((0, pos.y), (0, pos.y + 1)))
1327 } else {
1328 let width = self.line_width(pos.y);
1329 self.delete_range(TextRange::new((0, pos.y), (width, pos.y)))
1330 }
1331 }
1332
1333 pub fn delete_next_char(&mut self) -> bool {
1336 if self.has_selection() {
1337 self.delete_range(self.selection())
1338 } else {
1339 let r = self
1340 .value
1341 .remove_next_char(self.cursor())
1342 .expect("valid_cursor");
1343 self.scroll_cursor_to_visible();
1344 r
1345 }
1346 }
1347
1348 pub fn delete_prev_char(&mut self) -> bool {
1351 if self.has_selection() {
1352 self.delete_range(self.selection())
1353 } else {
1354 let r = self
1355 .value
1356 .remove_prev_char(self.cursor())
1357 .expect("valid_cursor");
1358 self.scroll_cursor_to_visible();
1359 r
1360 }
1361 }
1362
1363 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1366 self.value.next_word_start(pos.into()).expect("valid_pos")
1367 }
1368
1369 pub fn try_next_word_start(
1372 &self,
1373 pos: impl Into<TextPosition>,
1374 ) -> Result<TextPosition, TextError> {
1375 self.value.next_word_start(pos.into())
1376 }
1377
1378 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1381 self.value.next_word_end(pos.into()).expect("valid_pos")
1382 }
1383
1384 pub fn try_next_word_end(
1387 &self,
1388 pos: impl Into<TextPosition>,
1389 ) -> Result<TextPosition, TextError> {
1390 self.value.next_word_end(pos.into())
1391 }
1392
1393 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1399 self.value.prev_word_start(pos.into()).expect("valid_pos")
1400 }
1401
1402 pub fn try_prev_word_start(
1408 &self,
1409 pos: impl Into<TextPosition>,
1410 ) -> Result<TextPosition, TextError> {
1411 self.value.prev_word_start(pos.into())
1412 }
1413
1414 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1418 self.value.prev_word_end(pos.into()).expect("valid_pos")
1419 }
1420
1421 pub fn try_prev_word_end(
1425 &self,
1426 pos: impl Into<TextPosition>,
1427 ) -> Result<TextPosition, TextError> {
1428 self.value.prev_word_end(pos.into())
1429 }
1430
1431 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1433 self.value.is_word_boundary(pos.into()).expect("valid_pos")
1434 }
1435
1436 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1438 self.value.is_word_boundary(pos.into())
1439 }
1440
1441 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1444 self.value.word_start(pos.into()).expect("valid_pos")
1445 }
1446
1447 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1450 self.value.word_start(pos.into())
1451 }
1452
1453 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1456 self.value.word_end(pos.into()).expect("valid_pos")
1457 }
1458
1459 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1462 self.value.word_end(pos.into())
1463 }
1464
1465 pub fn delete_next_word(&mut self) -> bool {
1468 if self.has_selection() {
1469 self.delete_range(self.selection())
1470 } else {
1471 let cursor = self.cursor();
1472
1473 let start = self.next_word_start(cursor);
1474 if start != cursor {
1475 self.delete_range(cursor..start)
1476 } else {
1477 let end = self.next_word_end(cursor);
1478 self.delete_range(cursor..end)
1479 }
1480 }
1481 }
1482
1483 pub fn delete_prev_word(&mut self) -> bool {
1486 if self.has_selection() {
1487 self.delete_range(self.selection())
1488 } else {
1489 let cursor = self.cursor();
1490
1491 let till_line_start = if cursor.x != 0 {
1493 self.graphemes(TextRange::new((0, cursor.y), cursor), cursor)
1494 .rev_cursor()
1495 .all(|v| v.is_whitespace())
1496 } else {
1497 false
1498 };
1499
1500 if till_line_start {
1501 self.delete_range(TextRange::new((0, cursor.y), cursor))
1502 } else {
1503 let end = self.prev_word_end(cursor);
1504 if end != cursor {
1505 self.delete_range(end..cursor)
1506 } else {
1507 let start = self.prev_word_start(cursor);
1508 self.delete_range(start..cursor)
1509 }
1510 }
1511 }
1512 }
1513
1514 pub fn move_left(&mut self, n: upos_type, extend_selection: bool) -> bool {
1517 let mut cursor = self.cursor();
1518
1519 if cursor.x == 0 {
1520 if cursor.y > 0 {
1521 cursor.y = cursor.y.saturating_sub(1);
1522 cursor.x = self.line_width(cursor.y);
1523 }
1524 } else {
1525 cursor.x = cursor.x.saturating_sub(n);
1526 }
1527
1528 self.set_move_col(Some(cursor.x));
1529 self.set_cursor(cursor, extend_selection)
1530 }
1531
1532 pub fn move_right(&mut self, n: upos_type, extend_selection: bool) -> bool {
1535 let mut cursor = self.cursor();
1536
1537 let c_line_width = self.line_width(cursor.y);
1538 if cursor.x == c_line_width {
1539 if cursor.y + 1 < self.len_lines() {
1540 cursor.y += 1;
1541 cursor.x = 0;
1542 }
1543 } else {
1544 cursor.x = min(cursor.x + n, c_line_width)
1545 }
1546
1547 self.set_move_col(Some(cursor.x));
1548 self.set_cursor(cursor, extend_selection)
1549 }
1550
1551 pub fn move_up(&mut self, n: upos_type, extend_selection: bool) -> bool {
1554 let mut cursor = self.cursor();
1555
1556 cursor.y = cursor.y.saturating_sub(n);
1557 let c_line_width = self.line_width(cursor.y);
1558 if let Some(move_col) = self.move_col() {
1559 cursor.x = min(move_col, c_line_width);
1560 } else {
1561 cursor.x = min(cursor.x, c_line_width);
1562 }
1563
1564 self.set_cursor(cursor, extend_selection)
1565 }
1566
1567 pub fn move_down(&mut self, n: upos_type, extend_selection: bool) -> bool {
1570 let mut cursor = self.cursor();
1571
1572 cursor.y = min(cursor.y + n, self.len_lines() - 1);
1573 let c_line_width = self.line_width(cursor.y);
1574 if let Some(move_col) = self.move_col() {
1575 cursor.x = min(move_col, c_line_width);
1576 } else {
1577 cursor.x = min(cursor.x, c_line_width);
1578 }
1579
1580 self.set_cursor(cursor, extend_selection)
1581 }
1582
1583 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1587 let mut cursor = self.cursor();
1588
1589 cursor.x = 'f: {
1590 for (idx, g) in self.line_graphemes(cursor.y).enumerate() {
1591 if g != " " && g != "\t" {
1592 if cursor.x != idx as upos_type {
1593 break 'f idx as upos_type;
1594 } else {
1595 break 'f 0;
1596 }
1597 }
1598 }
1599 0
1600 };
1601
1602 self.set_move_col(Some(cursor.x));
1603 self.set_cursor(cursor, extend_selection)
1604 }
1605
1606 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1610 let mut cursor = self.cursor();
1611
1612 cursor.x = self.line_width(cursor.y);
1613
1614 self.set_move_col(Some(cursor.x));
1615 self.set_cursor(cursor, extend_selection)
1616 }
1617
1618 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1620 let cursor = TextPosition::new(0, 0);
1621
1622 self.set_cursor(cursor, extend_selection)
1623 }
1624
1625 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1627 let len = self.len_lines();
1628
1629 let cursor = TextPosition::new(0, len - 1);
1630
1631 self.set_cursor(cursor, extend_selection)
1632 }
1633
1634 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1636 let (ox, oy) = self.offset();
1637
1638 let cursor = TextPosition::new(ox as upos_type, oy as upos_type);
1639
1640 self.set_cursor(cursor, extend_selection)
1641 }
1642
1643 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1645 let (ox, oy) = self.offset();
1646 let (ox, oy) = (ox as upos_type, oy as upos_type);
1647 let len = self.len_lines();
1648
1649 let cursor =
1650 TextPosition::new(ox, min(oy + self.vertical_page() as upos_type - 1, len - 1));
1651
1652 self.set_cursor(cursor, extend_selection)
1653 }
1654
1655 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1657 let cursor = self.cursor();
1658
1659 let word = self.next_word_end(cursor);
1660
1661 self.set_cursor(word, extend_selection)
1662 }
1663
1664 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1666 let cursor = self.cursor();
1667
1668 let word = self.prev_word_start(cursor);
1669
1670 self.set_cursor(word, extend_selection)
1671 }
1672}
1673
1674impl HasScreenCursor for TextAreaState {
1675 fn screen_cursor(&self) -> Option<(u16, u16)> {
1677 if self.is_focused() {
1678 if self.has_selection() {
1679 None
1680 } else {
1681 let cursor = self.cursor();
1682 let (ox, oy) = self.offset();
1683 let (ox, oy) = (ox as upos_type, oy as upos_type);
1684
1685 if cursor.y < oy {
1686 None
1687 } else if cursor.y >= oy + (self.inner.height + self.dark_offset.1) as upos_type {
1688 None
1689 } else {
1690 if cursor.x < ox {
1691 None
1692 } else if cursor.x > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1693 None
1694 } else {
1695 let sy = self.row_to_screen(cursor);
1696 let sx = self.col_to_screen(cursor);
1697
1698 if let Some((sx, sy)) = sx.iter().zip(sy.iter()).next() {
1699 Some((self.inner.x + *sx, self.inner.y + *sy))
1700 } else {
1701 None
1702 }
1703 }
1704 }
1705 }
1706 } else {
1707 None
1708 }
1709 }
1710}
1711
1712impl RelocatableState for TextAreaState {
1713 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1714 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1716 self.area = relocate_area(self.area, shift, clip);
1717 self.inner = relocate_area(self.inner, shift, clip);
1718 }
1719}
1720
1721impl TextAreaState {
1722 pub fn screen_to_row(&self, scy: i16) -> upos_type {
1725 let (_, oy) = self.offset();
1726 let oy = oy as upos_type + self.dark_offset.1 as upos_type;
1727
1728 if scy < 0 {
1729 oy.saturating_sub((scy as ipos_type).unsigned_abs())
1730 } else if scy as u16 >= (self.inner.height + self.dark_offset.1) {
1731 min(oy + scy as upos_type, self.len_lines().saturating_sub(1))
1732 } else {
1733 let scy = oy + scy as upos_type;
1734 let len = self.len_lines();
1735 if scy < len {
1736 scy
1737 } else {
1738 len.saturating_sub(1)
1739 }
1740 }
1741 }
1742
1743 pub fn screen_to_col(&self, row: upos_type, scx: i16) -> upos_type {
1750 self.try_screen_to_col(row, scx).expect("valid_row")
1751 }
1752
1753 pub fn try_screen_to_col(&self, row: upos_type, scx: i16) -> Result<upos_type, TextError> {
1760 let (ox, _) = self.offset();
1761
1762 let ox = ox as upos_type + self.dark_offset.0 as upos_type;
1763
1764 if scx < 0 {
1765 Ok(ox.saturating_sub((scx as ipos_type).unsigned_abs()))
1766 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
1767 Ok(min(ox + scx as upos_type, self.line_width(row)))
1768 } else {
1769 let scx = scx as u16;
1770
1771 let line = self.try_glyphs(
1772 row..row + 1,
1773 ox as u16,
1774 self.inner.width + self.dark_offset.0,
1775 )?;
1776
1777 let mut col = ox;
1778 for g in line {
1779 if scx < g.screen_pos().0 + g.screen_width() {
1780 break;
1781 }
1782 col = g.pos().x + 1;
1783 }
1784 Ok(col)
1785 }
1786 }
1787
1788 pub fn row_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
1791 let pos = pos.into();
1792 let (_, oy) = self.offset();
1793
1794 if pos.y < oy as upos_type {
1795 return None;
1796 }
1797
1798 let screen_y = pos.y - oy as upos_type;
1799
1800 if screen_y >= self.dark_offset.1 as upos_type {
1801 Some(screen_y as u16 - self.dark_offset.1)
1802 } else {
1803 None
1804 }
1805 }
1806
1807 pub fn col_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
1810 self.try_col_to_screen(pos).expect("valid_pos")
1811 }
1812
1813 pub fn try_col_to_screen(
1816 &self,
1817 pos: impl Into<TextPosition>,
1818 ) -> Result<Option<u16>, TextError> {
1819 let pos = pos.into();
1820 let (ox, _) = self.offset();
1821
1822 if pos.x < ox as upos_type {
1823 return Ok(None);
1824 }
1825
1826 let line = self.try_glyphs(
1827 pos.y..pos.y + 1,
1828 ox as u16,
1829 self.inner.width + self.dark_offset.0,
1830 )?;
1831 let mut screen_x = 0;
1832 for g in line {
1833 if g.pos().x == pos.x {
1834 break;
1835 }
1836 screen_x = g.screen_pos().0 + g.screen_width();
1837 }
1838
1839 if screen_x >= self.dark_offset.0 {
1840 Ok(Some(screen_x - self.dark_offset.0))
1841 } else {
1842 Ok(None)
1843 }
1844 }
1845
1846 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
1852 let (scx, scy) = (cursor.0, cursor.1);
1853
1854 let cy = self.screen_to_row(scy);
1855 let cx = self.screen_to_col(cy, scx);
1856
1857 self.set_cursor(TextPosition::new(cx, cy), extend_selection)
1858 }
1859
1860 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
1867 let (scx, scy) = (cursor.0, cursor.1);
1868 let anchor = self.anchor();
1869
1870 let cy = self.screen_to_row(scy);
1871 let cx = self.screen_to_col(cy, scx);
1872 let cursor = TextPosition::new(cx, cy);
1873
1874 let cursor = if cursor < anchor {
1875 self.word_start(cursor)
1876 } else {
1877 self.word_end(cursor)
1878 };
1879
1880 if !self.is_word_boundary(anchor) {
1882 if cursor < anchor {
1883 self.set_cursor(self.word_end(anchor), false);
1884 } else {
1885 self.set_cursor(self.word_start(anchor), false);
1886 }
1887 }
1888
1889 self.set_cursor(cursor, extend_selection)
1890 }
1891}
1892
1893impl TextAreaState {
1894 pub fn vertical_max_offset(&self) -> usize {
1899 self.vscroll.max_offset()
1900 }
1901
1902 pub fn vertical_offset(&self) -> usize {
1904 self.vscroll.offset()
1905 }
1906
1907 pub fn vertical_page(&self) -> usize {
1909 self.vscroll.page_len()
1910 }
1911
1912 pub fn vertical_scroll(&self) -> usize {
1914 self.vscroll.scroll_by()
1915 }
1916
1917 pub fn horizontal_max_offset(&self) -> usize {
1921 self.hscroll.max_offset()
1922 }
1923
1924 pub fn horizontal_offset(&self) -> usize {
1926 self.hscroll.offset()
1927 }
1928
1929 pub fn horizontal_page(&self) -> usize {
1931 self.hscroll.page_len()
1932 }
1933
1934 pub fn horizontal_scroll(&self) -> usize {
1936 self.hscroll.scroll_by()
1937 }
1938
1939 #[allow(unused_assignments)]
1946 pub fn set_vertical_offset(&mut self, row_offset: usize) -> bool {
1947 self.scroll_to_cursor = false;
1948 self.vscroll.set_offset(row_offset)
1949 }
1950
1951 #[allow(unused_assignments)]
1958 pub fn set_horizontal_offset(&mut self, col_offset: usize) -> bool {
1959 self.scroll_to_cursor = false;
1960 self.hscroll.set_offset(col_offset)
1961 }
1962
1963 pub fn scroll_to_row(&mut self, pos: usize) -> bool {
1965 self.scroll_to_cursor = false;
1966 self.vscroll.set_offset(pos)
1967 }
1968
1969 pub fn scroll_to_col(&mut self, pos: usize) -> bool {
1971 self.scroll_to_cursor = false;
1972 self.hscroll.set_offset(pos)
1973 }
1974
1975 pub fn scroll_up(&mut self, delta: usize) -> bool {
1977 self.vscroll.scroll_up(delta)
1978 }
1979
1980 pub fn scroll_down(&mut self, delta: usize) -> bool {
1982 self.vscroll.scroll_down(delta)
1983 }
1984
1985 pub fn scroll_left(&mut self, delta: usize) -> bool {
1987 self.hscroll.scroll_left(delta)
1988 }
1989
1990 pub fn scroll_right(&mut self, delta: usize) -> bool {
1992 self.hscroll.scroll_right(delta)
1993 }
1994}
1995
1996impl TextAreaState {
1997 pub fn scroll_cursor_to_visible(&mut self) {
2000 self.scroll_to_cursor = true;
2001 }
2002}
2003
2004impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2005 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2006 fn tc(r: bool) -> TextOutcome {
2008 if r {
2009 TextOutcome::TextChanged
2010 } else {
2011 TextOutcome::Unchanged
2012 }
2013 }
2014
2015 let mut r = if self.is_focused() {
2016 match event {
2017 ct_event!(key press c)
2018 | ct_event!(key press SHIFT-c)
2019 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2020 ct_event!(keycode press Tab) => {
2021 tc(if !self.focus.gained() {
2023 self.insert_tab()
2024 } else {
2025 false
2026 })
2027 }
2028 ct_event!(keycode press SHIFT-BackTab) => {
2029 tc(if !self.focus.gained() {
2031 self.insert_backtab()
2032 } else {
2033 false
2034 })
2035 }
2036 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2037 ct_event!(keycode press Backspace) => tc(self.delete_prev_char()),
2038 ct_event!(keycode press Delete) => tc(self.delete_next_char()),
2039 ct_event!(keycode press CONTROL-Backspace)
2040 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2041 ct_event!(keycode press CONTROL-Delete) | ct_event!(keycode press ALT-Delete) => {
2042 tc(self.delete_next_word())
2043 }
2044 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2045 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2046 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2047 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2048 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2049 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2050
2051 ct_event!(key release _)
2052 | ct_event!(key release SHIFT-_)
2053 | ct_event!(key release CONTROL_ALT-_)
2054 | ct_event!(keycode release Tab)
2055 | ct_event!(keycode release Enter)
2056 | ct_event!(keycode release Backspace)
2057 | ct_event!(keycode release Delete)
2058 | ct_event!(keycode release CONTROL-Backspace)
2059 | ct_event!(keycode release ALT-Backspace)
2060 | ct_event!(keycode release CONTROL-Delete)
2061 | ct_event!(key release CONTROL-'x')
2062 | ct_event!(key release CONTROL-'v')
2063 | ct_event!(key release CONTROL-'d')
2064 | ct_event!(key release CONTROL-'y')
2065 | ct_event!(key release CONTROL-'z')
2066 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2067 _ => TextOutcome::Continue,
2068 }
2069 } else {
2070 TextOutcome::Continue
2071 };
2072 if r == TextOutcome::Continue {
2073 r = self.handle(event, ReadOnly);
2074 }
2075 r
2076 }
2077}
2078
2079impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
2080 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2081 let mut r = if self.is_focused() {
2082 match event {
2083 ct_event!(keycode press Left) => self.move_left(1, false).into(),
2084 ct_event!(keycode press Right) => self.move_right(1, false).into(),
2085 ct_event!(keycode press Up) => self.move_up(1, false).into(),
2086 ct_event!(keycode press Down) => self.move_down(1, false).into(),
2087 ct_event!(keycode press PageUp) => self
2088 .move_up(self.vertical_page() as upos_type, false)
2089 .into(),
2090 ct_event!(keycode press PageDown) => self
2091 .move_down(self.vertical_page() as upos_type, false)
2092 .into(),
2093 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
2094 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
2095 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
2096 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
2097 ct_event!(keycode press CONTROL-Up) => false.into(),
2098 ct_event!(keycode press CONTROL-Down) => false.into(),
2099 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
2100 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
2101 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
2102 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
2103
2104 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
2105 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
2106 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
2107 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
2108 ct_event!(keycode press ALT-PageUp) => {
2109 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
2110 }
2111 ct_event!(keycode press ALT-PageDown) => {
2112 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
2113 }
2114 ct_event!(keycode press ALT_SHIFT-PageUp) => {
2115 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
2116 }
2117 ct_event!(keycode press ALT_SHIFT-PageDown) => {
2118 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
2119 }
2120
2121 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
2122 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
2123 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
2124 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
2125 ct_event!(keycode press SHIFT-PageUp) => {
2126 self.move_up(self.vertical_page() as upos_type, true).into()
2127 }
2128 ct_event!(keycode press SHIFT-PageDown) => self
2129 .move_down(self.vertical_page() as upos_type, true)
2130 .into(),
2131 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
2132 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
2133 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
2134 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
2135 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
2136 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
2137 ct_event!(key press CONTROL-'a') => self.select_all().into(),
2138 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
2139
2140 ct_event!(keycode release Left)
2141 | ct_event!(keycode release Right)
2142 | ct_event!(keycode release Up)
2143 | ct_event!(keycode release Down)
2144 | ct_event!(keycode release PageUp)
2145 | ct_event!(keycode release PageDown)
2146 | ct_event!(keycode release Home)
2147 | ct_event!(keycode release End)
2148 | ct_event!(keycode release CONTROL-Left)
2149 | ct_event!(keycode release CONTROL-Right)
2150 | ct_event!(keycode release CONTROL-Up)
2151 | ct_event!(keycode release CONTROL-Down)
2152 | ct_event!(keycode release CONTROL-PageUp)
2153 | ct_event!(keycode release CONTROL-PageDown)
2154 | ct_event!(keycode release CONTROL-Home)
2155 | ct_event!(keycode release CONTROL-End)
2156 | ct_event!(keycode release ALT-Left)
2157 | ct_event!(keycode release ALT-Right)
2158 | ct_event!(keycode release ALT-Up)
2159 | ct_event!(keycode release ALT-Down)
2160 | ct_event!(keycode release ALT-PageUp)
2161 | ct_event!(keycode release ALT-PageDown)
2162 | ct_event!(keycode release ALT_SHIFT-PageUp)
2163 | ct_event!(keycode release ALT_SHIFT-PageDown)
2164 | ct_event!(keycode release SHIFT-Left)
2165 | ct_event!(keycode release SHIFT-Right)
2166 | ct_event!(keycode release SHIFT-Up)
2167 | ct_event!(keycode release SHIFT-Down)
2168 | ct_event!(keycode release SHIFT-PageUp)
2169 | ct_event!(keycode release SHIFT-PageDown)
2170 | ct_event!(keycode release SHIFT-Home)
2171 | ct_event!(keycode release SHIFT-End)
2172 | ct_event!(keycode release CONTROL_SHIFT-Left)
2173 | ct_event!(keycode release CONTROL_SHIFT-Right)
2174 | ct_event!(keycode release CONTROL_SHIFT-Home)
2175 | ct_event!(keycode release CONTROL_SHIFT-End)
2176 | ct_event!(key release CONTROL-'a')
2177 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2178 _ => TextOutcome::Continue,
2179 }
2180 } else {
2181 TextOutcome::Continue
2182 };
2183
2184 if r == TextOutcome::Continue {
2185 r = self.handle(event, MouseOnly);
2186 }
2187 r
2188 }
2189}
2190
2191impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
2192 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
2193 flow!(match event {
2194 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2195 let cx = m.column as i16 - self.inner.x as i16;
2196 let cy = m.row as i16 - self.inner.y as i16;
2197 self.set_screen_cursor((cx, cy), true).into()
2198 }
2199 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2200 let cx = m.column as i16 - self.inner.x as i16;
2201 let cy = m.row as i16 - self.inner.y as i16;
2202 self.set_screen_cursor_words((cx, cy), true).into()
2203 }
2204 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2205 let ty = self.screen_to_row(m.row as i16 - self.inner.y as i16);
2206 let tx = self.screen_to_col(ty, m.column as i16 - self.inner.x as i16);
2207 let test = TextPosition::new(tx, ty);
2208 let start = self.word_start(test);
2209 let end = self.word_end(test);
2210 self.set_selection(start, end).into()
2211 }
2212 ct_event!(mouse down Left for column,row) => {
2213 if self.inner.contains((*column, *row).into()) {
2214 let cx = (column - self.inner.x) as i16;
2215 let cy = (row - self.inner.y) as i16;
2216 self.set_screen_cursor((cx, cy), false).into()
2217 } else {
2218 TextOutcome::Continue
2219 }
2220 }
2221 ct_event!(mouse down SHIFT-Left for column,row) => {
2222 if self.inner.contains((*column, *row).into()) {
2223 let cx = (column - self.inner.x) as i16;
2224 let cy = (row - self.inner.y) as i16;
2225 self.set_screen_cursor((cx, cy), true).into()
2226 } else {
2227 TextOutcome::Continue
2228 }
2229 }
2230 ct_event!(mouse down CONTROL-Left for column,row) => {
2231 if self.inner.contains((*column, *row).into()) {
2232 let cx = (column - self.inner.x) as i16;
2233 let cy = (row - self.inner.y) as i16;
2234 self.set_screen_cursor((cx, cy), true).into()
2235 } else {
2236 TextOutcome::Continue
2237 }
2238 }
2239 ct_event!(mouse down ALT-Left for column,row) => {
2240 if self.inner.contains((*column, *row).into()) {
2241 let cx = (column - self.inner.x) as i16;
2242 let cy = (row - self.inner.y) as i16;
2243 self.set_screen_cursor_words((cx, cy), true).into()
2244 } else {
2245 TextOutcome::Continue
2246 }
2247 }
2248 _ => TextOutcome::Continue,
2249 });
2250
2251 let mut sas = ScrollAreaState::new()
2252 .area(self.inner)
2253 .h_scroll(&mut self.hscroll)
2254 .v_scroll(&mut self.vscroll);
2255 let r = match sas.handle(event, MouseOnly) {
2256 ScrollOutcome::Up(v) => self.scroll_up(v),
2257 ScrollOutcome::Down(v) => self.scroll_down(v),
2258 ScrollOutcome::Left(v) => self.scroll_left(v),
2259 ScrollOutcome::Right(v) => self.scroll_right(v),
2260 ScrollOutcome::VPos(v) => self.set_vertical_offset(v),
2261 ScrollOutcome::HPos(v) => self.set_horizontal_offset(v),
2262 _ => false,
2263 };
2264 if r {
2265 return TextOutcome::Changed;
2266 }
2267
2268 TextOutcome::Continue
2269 }
2270}
2271
2272pub fn handle_events(
2276 state: &mut TextAreaState,
2277 focus: bool,
2278 event: &crossterm::event::Event,
2279) -> TextOutcome {
2280 state.focus.set(focus);
2281 state.handle(event, Regular)
2282}
2283
2284pub fn handle_readonly_events(
2288 state: &mut TextAreaState,
2289 focus: bool,
2290 event: &crossterm::event::Event,
2291) -> TextOutcome {
2292 state.focus.set(focus);
2293 state.handle(event, ReadOnly)
2294}
2295
2296pub fn handle_mouse_events(
2298 state: &mut TextAreaState,
2299 event: &crossterm::event::Event,
2300) -> TextOutcome {
2301 state.handle(event, MouseOnly)
2302}