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};
27#[cfg(feature = "unstable-widget-ref")]
28use ratatui::widgets::StatefulWidgetRef;
29use ratatui::widgets::{Block, StatefulWidget};
30use ropey::Rope;
31use std::borrow::Cow;
32use std::cmp::{max, min};
33use std::collections::HashMap;
34use std::ops::Range;
35
36#[derive(Debug, Default, Clone)]
75pub struct TextArea<'a> {
76 block: Option<Block<'a>>,
77 hscroll: Option<Scroll<'a>>,
78 h_max_offset: Option<usize>,
79 h_overscroll: Option<usize>,
80 vscroll: Option<Scroll<'a>>,
81
82 style: Style,
83 focus_style: Option<Style>,
84 select_style: Option<Style>,
85 text_style: HashMap<usize, Style>,
86}
87
88#[derive(Debug)]
90pub struct TextAreaState {
91 pub area: Rect,
94 pub inner: Rect,
97
98 pub hscroll: ScrollState,
101 pub vscroll: ScrollState,
104 pub dark_offset: (u16, u16),
107
108 pub value: TextCore<TextRope>,
110
111 pub move_col: Option<upos_type>,
113 pub auto_indent: bool,
115 pub auto_quote: bool,
117
118 pub focus: FocusFlag,
120
121 pub mouse: MouseFlags,
124
125 pub non_exhaustive: NonExhaustive,
126}
127
128impl Clone for TextAreaState {
129 fn clone(&self) -> Self {
130 Self {
131 focus: FocusFlag::named(self.focus.name()),
132 area: self.area,
133 inner: self.inner,
134 value: self.value.clone(),
135 hscroll: self.hscroll.clone(),
136 vscroll: self.vscroll.clone(),
137 move_col: None,
138 auto_indent: self.auto_indent,
139 auto_quote: self.auto_quote,
140 mouse: Default::default(),
141 non_exhaustive: NonExhaustive,
142 dark_offset: (0, 0),
143 }
144 }
145}
146
147impl<'a> TextArea<'a> {
148 pub fn new() -> Self {
150 Self::default()
151 }
152
153 #[inline]
155 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
156 if let Some(styles) = styles {
157 self.styles(styles)
158 } else {
159 self
160 }
161 }
162
163 #[inline]
165 pub fn styles(mut self, styles: TextStyle) -> Self {
166 self.style = styles.style;
167 if styles.focus.is_some() {
168 self.focus_style = styles.focus;
169 }
170 if styles.select.is_some() {
171 self.select_style = styles.select;
172 }
173 if let Some(border_style) = styles.border_style {
174 self.block = self.block.map(|v| v.border_style(border_style));
175 }
176 self.block = self.block.map(|v| v.style(self.style));
177 if styles.block.is_some() {
178 self.block = styles.block;
179 }
180 if let Some(styles) = styles.scroll {
181 self.hscroll = self.hscroll.map(|v| v.styles(styles.clone()));
182 self.vscroll = self.vscroll.map(|v| v.styles(styles));
183 }
184 self
185 }
186
187 pub fn style(mut self, style: Style) -> Self {
189 self.style = style;
190 self
191 }
192
193 pub fn focus_style(mut self, style: Style) -> Self {
195 self.focus_style = Some(style);
196 self.block = self.block.map(|v| v.style(self.style));
197 self
198 }
199
200 pub fn select_style(mut self, style: Style) -> Self {
202 self.select_style = Some(style);
203 self
204 }
205
206 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
211 for (i, s) in styles.into_iter().enumerate() {
212 self.text_style.insert(i, s);
213 }
214 self
215 }
216
217 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
222 for (i, s) in styles.into_iter() {
223 self.text_style.insert(i, s.into());
224 }
225 self
226 }
227
228 #[inline]
230 pub fn block(mut self, block: Block<'a>) -> Self {
231 self.block = Some(block);
232 self
233 }
234
235 pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
237 self.hscroll = Some(scroll.clone().override_horizontal());
238 self.vscroll = Some(scroll.override_vertical());
239 self
240 }
241
242 pub fn hscroll(mut self, scroll: Scroll<'a>) -> Self {
244 self.hscroll = Some(scroll.override_horizontal());
245 self
246 }
247
248 pub fn set_horizontal_max_offset(mut self, offset: usize) -> Self {
256 self.h_max_offset = Some(offset);
257 self
258 }
259
260 pub fn set_horizontal_overscroll(mut self, overscroll: usize) -> Self {
265 self.h_overscroll = Some(overscroll);
266 self
267 }
268
269 pub fn vscroll(mut self, scroll: Scroll<'a>) -> Self {
271 self.vscroll = Some(scroll.override_vertical());
272 self
273 }
274}
275
276#[cfg(feature = "unstable-widget-ref")]
277impl<'a> StatefulWidgetRef for TextArea<'a> {
278 type State = TextAreaState;
279
280 fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
281 render_text_area(self, area, buf, state);
282 }
283}
284
285impl StatefulWidget for TextArea<'_> {
286 type State = TextAreaState;
287
288 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
289 render_text_area(&self, area, buf, state);
290 }
291}
292
293fn render_text_area(
294 widget: &TextArea<'_>,
295 area: Rect,
296 buf: &mut Buffer,
297 state: &mut TextAreaState,
298) {
299 state.area = area;
300
301 let style = widget.style;
302 let select_style = if let Some(select_style) = widget.select_style {
303 if select_style.fg.is_none() && select_style.bg.is_none() {
305 select_style
306 } else {
307 style.patch(select_style)
308 }
309 } else {
310 Style::default().black().on_yellow()
311 };
312
313 let sa = ScrollArea::new()
314 .block(widget.block.as_ref())
315 .h_scroll(widget.hscroll.as_ref())
316 .v_scroll(widget.vscroll.as_ref())
317 .style(style);
318
319 state.inner = sa.inner(area, Some(&state.hscroll), Some(&state.vscroll));
320
321 if let Some(h_max_offset) = widget.h_max_offset {
322 state.hscroll.set_max_offset(h_max_offset);
323 }
324 if let Some(h_overscroll) = widget.h_overscroll {
325 state.hscroll.set_overscroll_by(Some(h_overscroll));
326 }
327 state.hscroll.set_page_len(state.inner.width as usize);
328 state.vscroll.set_max_offset(
329 state
330 .len_lines()
331 .saturating_sub(state.inner.height as upos_type) as usize,
332 );
333 state.vscroll.set_page_len(state.inner.height as usize);
334
335 let inner = state.inner;
336
337 sa.render(
339 area,
340 buf,
341 &mut ScrollAreaState::new()
342 .h_scroll(&mut state.hscroll)
343 .v_scroll(&mut state.vscroll),
344 );
345
346 if inner.width == 0 || inner.height == 0 {
347 return;
349 }
350
351 if state.vscroll.offset() > state.value.len_lines() as usize {
352 return;
353 }
354
355 let (ox, oy) = state.offset();
356 let page_rows = (oy as upos_type)
357 ..min(
358 oy as upos_type + inner.height as upos_type,
359 state.value.len_lines(),
360 );
361 let page_bytes = state
362 .try_bytes_at_range(TextRange::new((0, page_rows.start), (0, page_rows.end)))
363 .expect("valid_rows");
364 let selection = state.selection();
365 let mut styles = Vec::new();
366
367 let glyph_iter = state
368 .value
369 .glyphs(page_rows.clone(), ox as u16, inner.width)
370 .expect("valid_offset");
371
372 for g in glyph_iter {
373 if g.screen_width() > 0 {
374 let mut style = style;
375 styles.clear();
377 state
378 .value
379 .styles_at_page(page_bytes.clone(), g.text_bytes().start, &mut styles);
380 for style_nr in &styles {
381 if let Some(s) = widget.text_style.get(style_nr) {
382 style = style.patch(*s);
383 }
384 }
385 if selection.contains_pos(g.pos()) {
387 style = style.patch(select_style);
388 };
389
390 let screen_pos = g.screen_pos();
392
393 if let Some(cell) = buf.cell_mut((inner.x + screen_pos.0, inner.y + screen_pos.1)) {
395 cell.set_symbol(g.glyph());
396 cell.set_style(style);
397 }
398 for d in 1..g.screen_width() {
400 if let Some(cell) =
401 buf.cell_mut((inner.x + screen_pos.0 + d, inner.y + screen_pos.1))
402 {
403 cell.reset();
404 cell.set_style(style);
405 }
406 }
407 }
408 }
409}
410
411impl Default for TextAreaState {
412 fn default() -> Self {
413 let mut s = Self {
414 focus: Default::default(),
415 area: Default::default(),
416 inner: Default::default(),
417 mouse: Default::default(),
418 value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
419 hscroll: Default::default(),
420 non_exhaustive: NonExhaustive,
421 vscroll: Default::default(),
422 move_col: None,
423 auto_indent: true,
424 auto_quote: true,
425 dark_offset: (0, 0),
426 };
427 s.hscroll.set_max_offset(255);
428 s.hscroll.set_overscroll_by(Some(16384));
429 s
430 }
431}
432
433impl HasFocus for TextAreaState {
434 fn build(&self, builder: &mut FocusBuilder) {
435 builder.leaf_widget(self);
436 }
437
438 fn focus(&self) -> FocusFlag {
439 self.focus.clone()
440 }
441
442 fn area(&self) -> Rect {
443 self.area
444 }
445
446 fn navigable(&self) -> Navigation {
447 Navigation::Reach
448 }
449}
450
451impl TextAreaState {
452 #[inline]
454 pub fn new() -> Self {
455 Self::default()
456 }
457
458 #[inline]
460 pub fn named(name: &str) -> Self {
461 Self {
462 focus: FocusFlag::named(name),
463 ..Default::default()
464 }
465 }
466
467 #[inline]
473 pub fn set_newline(&mut self, br: impl Into<String>) {
474 self.value.set_newline(br.into());
475 }
476
477 #[inline]
479 pub fn newline(&self) -> &str {
480 self.value.newline()
481 }
482
483 #[inline]
485 pub fn set_auto_indent(&mut self, indent: bool) {
486 self.auto_indent = indent;
487 }
488
489 #[inline]
491 pub fn set_auto_quote(&mut self, quote: bool) {
492 self.auto_quote = quote;
493 }
494
495 #[inline]
497 pub fn set_tab_width(&mut self, tabs: u16) {
498 self.value.set_tab_width(tabs);
499 }
500
501 #[inline]
503 pub fn tab_width(&self) -> u16 {
504 self.value.tab_width()
505 }
506
507 #[inline]
509 pub fn set_expand_tabs(&mut self, expand: bool) {
510 self.value.set_expand_tabs(expand);
511 }
512
513 #[inline]
515 pub fn expand_tabs(&self) -> bool {
516 self.value.expand_tabs()
517 }
518
519 #[inline]
521 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
522 self.value.set_glyph_ctrl(show_ctrl);
523 }
524
525 pub fn show_ctrl(&self) -> bool {
527 self.value.glyph_ctrl()
528 }
529
530 #[inline]
537 pub fn set_move_col(&mut self, col: Option<upos_type>) {
538 self.move_col = col;
539 }
540
541 #[inline]
543 pub fn move_col(&mut self) -> Option<upos_type> {
544 self.move_col
545 }
546}
547
548impl TextAreaState {
549 #[inline]
552 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
553 match clip {
554 None => self.value.set_clipboard(None),
555 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
556 }
557 }
558
559 #[inline]
562 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
563 self.value.clipboard()
564 }
565
566 #[inline]
568 pub fn copy_to_clip(&mut self) -> bool {
569 let Some(clip) = self.value.clipboard() else {
570 return false;
571 };
572
573 _ = clip.set_string(self.selected_text().as_ref());
574 false
575 }
576
577 #[inline]
579 pub fn cut_to_clip(&mut self) -> bool {
580 let Some(clip) = self.value.clipboard() else {
581 return false;
582 };
583
584 match clip.set_string(self.selected_text().as_ref()) {
585 Ok(_) => self.delete_range(self.selection()),
586 Err(_) => false,
587 }
588 }
589
590 #[inline]
592 pub fn paste_from_clip(&mut self) -> bool {
593 let Some(clip) = self.value.clipboard() else {
594 return false;
595 };
596
597 if let Ok(text) = clip.get_string() {
598 self.insert_str(text)
599 } else {
600 false
601 }
602 }
603}
604
605impl TextAreaState {
606 #[inline]
608 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
609 match undo {
610 None => self.value.set_undo_buffer(None),
611 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
612 }
613 }
614
615 #[inline]
617 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
618 self.value.undo_buffer()
619 }
620
621 #[inline]
623 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
624 self.value.undo_buffer_mut()
625 }
626
627 #[inline]
629 pub fn begin_undo_seq(&mut self) {
630 self.value.begin_undo_seq()
631 }
632
633 #[inline]
635 pub fn end_undo_seq(&mut self) {
636 self.value.end_undo_seq()
637 }
638
639 #[inline]
641 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
642 self.value.recent_replay_log()
643 }
644
645 #[inline]
647 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
648 self.value.replay_log(replay)
649 }
650
651 #[inline]
653 pub fn undo(&mut self) -> bool {
654 self.value.undo()
655 }
656
657 #[inline]
659 pub fn redo(&mut self) -> bool {
660 self.value.redo()
661 }
662}
663
664impl TextAreaState {
665 #[inline]
676 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
677 self.value.set_styles(styles);
678 }
679
680 #[inline]
685 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
686 self.value.add_style(range, style);
687 }
688
689 #[inline]
693 pub fn add_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
694 let r = self.value.bytes_at_range(range)?;
695 self.value.add_style(r, style);
696 Ok(())
697 }
698
699 #[inline]
701 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
702 self.value.remove_style(range, style);
703 }
704
705 #[inline]
707 pub fn remove_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
708 let r = self.value.bytes_at_range(range)?;
709 self.value.remove_style(r, style);
710 Ok(())
711 }
712
713 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
715 self.value.styles_in(range, buf)
716 }
717
718 #[inline]
720 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
721 self.value.styles_at(byte_pos, buf)
722 }
723
724 #[inline]
727 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
728 self.value.style_match(byte_pos, style)
729 }
730
731 #[inline]
733 pub fn styles(&self) -> impl Iterator<Item = (Range<usize>, usize)> + '_ {
734 self.value.styles().expect("styles")
735 }
736}
737
738impl TextAreaState {
739 #[inline]
741 pub fn offset(&self) -> (usize, usize) {
742 (self.hscroll.offset(), self.vscroll.offset())
743 }
744
745 #[inline]
747 pub fn set_offset(&mut self, offset: (usize, usize)) -> bool {
748 let c = self.hscroll.set_offset(offset.0);
749 let r = self.vscroll.set_offset(offset.1);
750 r || c
751 }
752
753 #[inline]
755 pub fn cursor(&self) -> TextPosition {
756 self.value.cursor()
757 }
758
759 #[inline]
763 pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
764 self.value.set_cursor(cursor.into(), extend_selection)
765 }
766
767 #[inline]
769 pub fn anchor(&self) -> TextPosition {
770 self.value.anchor()
771 }
772
773 #[inline]
775 pub fn has_selection(&self) -> bool {
776 self.value.has_selection()
777 }
778
779 #[inline]
781 pub fn selection(&self) -> TextRange {
782 self.value.selection()
783 }
784
785 #[inline]
787 pub fn set_selection(
788 &mut self,
789 anchor: impl Into<TextPosition>,
790 cursor: impl Into<TextPosition>,
791 ) -> bool {
792 self.value.set_selection(anchor.into(), cursor.into())
793 }
794
795 #[inline]
797 pub fn select_all(&mut self) -> bool {
798 self.value.select_all()
799 }
800
801 #[inline]
803 pub fn selected_text(&self) -> Cow<'_, str> {
804 self.value
805 .str_slice(self.value.selection())
806 .expect("valid_selection")
807 }
808}
809
810impl TextAreaState {
811 #[inline]
813 pub fn is_empty(&self) -> bool {
814 self.value.is_empty()
815 }
816
817 #[inline]
819 pub fn rope(&self) -> &Rope {
820 self.value.text().rope()
821 }
822
823 #[inline]
825 pub fn text(&self) -> String {
826 self.value.text().string()
827 }
828
829 #[inline]
831 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
832 self.value.str_slice_byte(range).expect("valid_range")
833 }
834
835 #[inline]
837 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
838 self.value.str_slice_byte(range)
839 }
840
841 #[inline]
843 pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
844 self.value.str_slice(range.into()).expect("valid_range")
845 }
846
847 #[inline]
849 pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
850 self.value.str_slice(range.into())
851 }
852
853 #[inline]
855 pub fn len_lines(&self) -> upos_type {
856 self.value.len_lines()
857 }
858
859 #[inline]
861 pub fn line_width(&self, row: upos_type) -> upos_type {
862 self.value.line_width(row).expect("valid_row")
863 }
864
865 #[inline]
867 pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
868 self.value.line_width(row)
869 }
870
871 #[inline]
874 pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
875 self.value.line_at(row).expect("valid_row")
876 }
877
878 #[inline]
881 pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
882 self.value.line_at(row)
883 }
884
885 #[inline]
887 pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
888 self.value.lines_at(row).expect("valid_row")
889 }
890
891 #[inline]
893 pub fn try_lines_at(
894 &self,
895 row: upos_type,
896 ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
897 self.value.lines_at(row)
898 }
899
900 #[inline]
903 pub fn glyphs(
904 &self,
905 rows: Range<upos_type>,
906 screen_offset: u16,
907 screen_width: u16,
908 ) -> impl Iterator<Item = Glyph<'_>> {
909 self.value
910 .glyphs(rows, screen_offset, screen_width)
911 .expect("valid_rows")
912 }
913
914 #[inline]
917 pub fn try_glyphs(
918 &self,
919 rows: Range<upos_type>,
920 screen_offset: u16,
921 screen_width: u16,
922 ) -> Result<impl Iterator<Item = Glyph<'_>>, TextError> {
923 self.value.glyphs(rows, screen_offset, screen_width)
924 }
925
926 #[inline]
929 pub fn line_graphemes(&self, row: upos_type) -> impl Iterator<Item = Grapheme<'_>> {
930 self.value.line_graphemes(row).expect("valid_row")
931 }
932
933 #[inline]
936 pub fn try_line_graphemes(
937 &self,
938 row: upos_type,
939 ) -> Result<impl Iterator<Item = Grapheme<'_>>, TextError> {
940 self.value.line_graphemes(row)
941 }
942
943 #[inline]
945 pub fn text_graphemes(&self, pos: TextPosition) -> impl Cursor<Item = Grapheme<'_>> {
946 self.value.text_graphemes(pos).expect("valid_pos")
947 }
948
949 #[inline]
951 pub fn try_text_graphemes(
952 &self,
953 pos: TextPosition,
954 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
955 self.value.text_graphemes(pos)
956 }
957
958 #[inline]
960 pub fn graphemes(
961 &self,
962 range: TextRange,
963 pos: TextPosition,
964 ) -> impl Cursor<Item = Grapheme<'_>> {
965 self.value.graphemes(range, pos).expect("valid_args")
966 }
967
968 #[inline]
970 pub fn try_graphemes(
971 &self,
972 range: TextRange,
973 pos: TextPosition,
974 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
975 self.value.graphemes(range, pos)
976 }
977
978 #[inline]
981 pub fn byte_at(&self, pos: TextPosition) -> Range<usize> {
982 self.value.byte_at(pos).expect("valid_pos")
983 }
984
985 #[inline]
988 pub fn try_byte_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError> {
989 self.value.byte_at(pos)
990 }
991
992 #[inline]
994 pub fn try_bytes_at_range(&self, range: TextRange) -> Result<Range<usize>, TextError> {
995 self.value.bytes_at_range(range)
996 }
997
998 #[inline]
1000 pub fn bytes_at_range(&self, range: TextRange) -> Range<usize> {
1001 self.value.bytes_at_range(range).expect("valid_range")
1002 }
1003
1004 #[inline]
1007 pub fn byte_pos(&self, byte: usize) -> TextPosition {
1008 self.value.byte_pos(byte).expect("valid_pos")
1009 }
1010
1011 #[inline]
1014 pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1015 self.value.byte_pos(byte)
1016 }
1017
1018 #[inline]
1020 pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1021 self.value.byte_range(bytes).expect("valid_range")
1022 }
1023
1024 #[inline]
1026 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1027 self.value.byte_range(bytes)
1028 }
1029}
1030
1031impl TextAreaState {
1032 #[inline]
1034 pub fn clear(&mut self) -> bool {
1035 if !self.is_empty() {
1036 self.value.clear();
1037 true
1038 } else {
1039 false
1040 }
1041 }
1042
1043 #[inline]
1046 pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1047 self.vscroll.set_offset(0);
1048 self.hscroll.set_offset(0);
1049
1050 self.value.set_text(TextRope::new_text(s.as_ref()));
1051 }
1052
1053 #[inline]
1056 pub fn set_rope(&mut self, r: Rope) {
1057 self.vscroll.set_offset(0);
1058 self.hscroll.set_offset(0);
1059
1060 self.value.set_text(TextRope::new_rope(r));
1061 }
1062
1063 pub fn insert_char(&mut self, c: char) -> bool {
1070 let mut insert = true;
1071 if self.has_selection() {
1072 if self.auto_quote
1073 && (c == '\''
1074 || c == '"'
1075 || c == '`'
1076 || c == '<'
1077 || c == '['
1078 || c == '('
1079 || c == '{')
1080 {
1081 self.value
1082 .insert_quotes(self.selection(), c)
1083 .expect("valid_selection");
1084 insert = false;
1085 } else {
1086 self.value
1087 .remove_str_range(self.selection())
1088 .expect("valid_selection");
1089 }
1090 }
1091
1092 if insert {
1093 if c == '\n' {
1094 self.value
1095 .insert_newline(self.cursor())
1096 .expect("valid_cursor");
1097 } else if c == '\t' {
1098 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1099 } else {
1100 self.value
1101 .insert_char(self.cursor(), c)
1102 .expect("valid_cursor");
1103 }
1104 }
1105
1106 self.scroll_cursor_to_visible();
1107
1108 true
1109 }
1110
1111 pub fn insert_tab(&mut self) -> bool {
1117 if self.has_selection() {
1118 if self.auto_indent {
1119 let sel = self.selection();
1120 let indent = " ".repeat(self.tab_width() as usize);
1121
1122 self.value.begin_undo_seq();
1123 for r in sel.start.y..=sel.end.y {
1124 self.value
1125 .insert_str(TextPosition::new(0, r), &indent)
1126 .expect("valid_row");
1127 }
1128 self.value.end_undo_seq();
1129
1130 true
1131 } else {
1132 false
1133 }
1134 } else {
1135 self.value.insert_tab(self.cursor()).expect("valid_cursor");
1136 self.scroll_cursor_to_visible();
1137
1138 true
1139 }
1140 }
1141
1142 pub fn insert_backtab(&mut self) -> bool {
1147 let sel = self.selection();
1148
1149 self.value.begin_undo_seq();
1150 for r in sel.start.y..=sel.end.y {
1151 let mut idx = 0;
1152 let g_it = self
1153 .value
1154 .graphemes(TextRange::new((0, r), (0, r + 1)), TextPosition::new(0, r))
1155 .expect("valid_range")
1156 .take(self.tab_width() as usize);
1157 for g in g_it {
1158 if g != " " && g != "\t" {
1159 break;
1160 }
1161 idx += 1;
1162 }
1163
1164 self.value
1165 .remove_str_range(TextRange::new((0, r), (idx, r)))
1166 .expect("valid_range");
1167 }
1168 self.value.end_undo_seq();
1169
1170 true
1171 }
1172
1173 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1176 let t = t.as_ref();
1177 if self.has_selection() {
1178 self.value
1179 .remove_str_range(self.selection())
1180 .expect("valid_selection");
1181 }
1182 self.value
1183 .insert_str(self.cursor(), t)
1184 .expect("valid_cursor");
1185 self.scroll_cursor_to_visible();
1186 true
1187 }
1188
1189 pub fn insert_newline(&mut self) -> bool {
1194 if self.has_selection() {
1195 self.value
1196 .remove_str_range(self.selection())
1197 .expect("valid_selection");
1198 }
1199 self.value
1200 .insert_newline(self.cursor())
1201 .expect("valid_cursor");
1202
1203 if self.auto_indent {
1205 let cursor = self.cursor();
1206 if cursor.y > 0 {
1207 let mut blanks = String::new();
1208 for g in self.line_graphemes(cursor.y - 1) {
1209 if g == " " || g == "\t" {
1210 blanks.push_str(g.grapheme());
1211 } else {
1212 break;
1213 }
1214 }
1215 if !blanks.is_empty() {
1216 self.value
1217 .insert_str(cursor, &blanks)
1218 .expect("valid_cursor");
1219 }
1220 }
1221 }
1222
1223 self.scroll_cursor_to_visible();
1224 true
1225 }
1226
1227 #[inline]
1229 pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1230 self.try_delete_range(range).expect("valid_range")
1231 }
1232
1233 #[inline]
1235 pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1236 let range = range.into();
1237 if !range.is_empty() {
1238 self.value.remove_str_range(range)?;
1239 self.scroll_cursor_to_visible();
1240 Ok(true)
1241 } else {
1242 Ok(false)
1243 }
1244 }
1245}
1246
1247impl TextAreaState {
1248 pub fn duplicate_text(&mut self) -> bool {
1251 if self.has_selection() {
1252 let sel_range = self.selection();
1253 if !sel_range.is_empty() {
1254 let v = self.str_slice(sel_range).to_string();
1255 self.value
1256 .insert_str(sel_range.end, &v)
1257 .expect("valid_selection");
1258 true
1259 } else {
1260 false
1261 }
1262 } else {
1263 let pos = self.cursor();
1264 let row_range = TextRange::new((0, pos.y), (0, pos.y + 1));
1265 let v = self.str_slice(row_range).to_string();
1266 self.value
1267 .insert_str(row_range.start, &v)
1268 .expect("valid_cursor");
1269 true
1270 }
1271 }
1272
1273 pub fn delete_line(&mut self) -> bool {
1276 let pos = self.cursor();
1277 if pos.y + 1 < self.len_lines() {
1278 self.delete_range(TextRange::new((0, pos.y), (0, pos.y + 1)))
1279 } else {
1280 let width = self.line_width(pos.y);
1281 self.delete_range(TextRange::new((0, pos.y), (width, pos.y)))
1282 }
1283 }
1284
1285 pub fn delete_next_char(&mut self) -> bool {
1288 if self.has_selection() {
1289 self.delete_range(self.selection())
1290 } else {
1291 let r = self
1292 .value
1293 .remove_next_char(self.cursor())
1294 .expect("valid_cursor");
1295 let s = self.scroll_cursor_to_visible();
1296
1297 r || s
1298 }
1299 }
1300
1301 pub fn delete_prev_char(&mut self) -> bool {
1304 if self.has_selection() {
1305 self.delete_range(self.selection())
1306 } else {
1307 let r = self
1308 .value
1309 .remove_prev_char(self.cursor())
1310 .expect("valid_cursor");
1311 let s = self.scroll_cursor_to_visible();
1312
1313 r || s
1314 }
1315 }
1316
1317 pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1320 self.value.next_word_start(pos.into()).expect("valid_pos")
1321 }
1322
1323 pub fn try_next_word_start(
1326 &self,
1327 pos: impl Into<TextPosition>,
1328 ) -> Result<TextPosition, TextError> {
1329 self.value.next_word_start(pos.into())
1330 }
1331
1332 pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1335 self.value.next_word_end(pos.into()).expect("valid_pos")
1336 }
1337
1338 pub fn try_next_word_end(
1341 &self,
1342 pos: impl Into<TextPosition>,
1343 ) -> Result<TextPosition, TextError> {
1344 self.value.next_word_end(pos.into())
1345 }
1346
1347 pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1353 self.value.prev_word_start(pos.into()).expect("valid_pos")
1354 }
1355
1356 pub fn try_prev_word_start(
1362 &self,
1363 pos: impl Into<TextPosition>,
1364 ) -> Result<TextPosition, TextError> {
1365 self.value.prev_word_start(pos.into())
1366 }
1367
1368 pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1372 self.value.prev_word_end(pos.into()).expect("valid_pos")
1373 }
1374
1375 pub fn try_prev_word_end(
1379 &self,
1380 pos: impl Into<TextPosition>,
1381 ) -> Result<TextPosition, TextError> {
1382 self.value.prev_word_end(pos.into())
1383 }
1384
1385 pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1387 self.value.is_word_boundary(pos.into()).expect("valid_pos")
1388 }
1389
1390 pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1392 self.value.is_word_boundary(pos.into())
1393 }
1394
1395 pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1398 self.value.word_start(pos.into()).expect("valid_pos")
1399 }
1400
1401 pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1404 self.value.word_start(pos.into())
1405 }
1406
1407 pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1410 self.value.word_end(pos.into()).expect("valid_pos")
1411 }
1412
1413 pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1416 self.value.word_end(pos.into())
1417 }
1418
1419 pub fn delete_next_word(&mut self) -> bool {
1422 if self.has_selection() {
1423 self.delete_range(self.selection())
1424 } else {
1425 let cursor = self.cursor();
1426
1427 let start = self.next_word_start(cursor);
1428 if start != cursor {
1429 self.delete_range(cursor..start)
1430 } else {
1431 let end = self.next_word_end(cursor);
1432 self.delete_range(cursor..end)
1433 }
1434 }
1435 }
1436
1437 pub fn delete_prev_word(&mut self) -> bool {
1440 if self.has_selection() {
1441 self.delete_range(self.selection())
1442 } else {
1443 let cursor = self.cursor();
1444
1445 let till_line_start = if cursor.x != 0 {
1447 self.graphemes(TextRange::new((0, cursor.y), cursor), cursor)
1448 .rev_cursor()
1449 .all(|v| v.is_whitespace())
1450 } else {
1451 false
1452 };
1453
1454 if till_line_start {
1455 self.delete_range(TextRange::new((0, cursor.y), cursor))
1456 } else {
1457 let end = self.prev_word_end(cursor);
1458 if end != cursor {
1459 self.delete_range(end..cursor)
1460 } else {
1461 let start = self.prev_word_start(cursor);
1462 self.delete_range(start..cursor)
1463 }
1464 }
1465 }
1466 }
1467
1468 pub fn move_left(&mut self, n: upos_type, extend_selection: bool) -> bool {
1471 let mut cursor = self.cursor();
1472
1473 if cursor.x == 0 {
1474 if cursor.y > 0 {
1475 cursor.y = cursor.y.saturating_sub(1);
1476 cursor.x = self.line_width(cursor.y);
1477 }
1478 } else {
1479 cursor.x = cursor.x.saturating_sub(n);
1480 }
1481
1482 self.set_move_col(Some(cursor.x));
1483 let c = self.set_cursor(cursor, extend_selection);
1484 let s = self.scroll_cursor_to_visible();
1485 c || s
1486 }
1487
1488 pub fn move_right(&mut self, n: upos_type, extend_selection: bool) -> bool {
1491 let mut cursor = self.cursor();
1492
1493 let c_line_width = self.line_width(cursor.y);
1494 if cursor.x == c_line_width {
1495 if cursor.y + 1 < self.len_lines() {
1496 cursor.y += 1;
1497 cursor.x = 0;
1498 }
1499 } else {
1500 cursor.x = min(cursor.x + n, c_line_width)
1501 }
1502
1503 self.set_move_col(Some(cursor.x));
1504 let c = self.set_cursor(cursor, extend_selection);
1505 let s = self.scroll_cursor_to_visible();
1506 c || s
1507 }
1508
1509 pub fn move_up(&mut self, n: upos_type, extend_selection: bool) -> bool {
1512 let mut cursor = self.cursor();
1513
1514 cursor.y = cursor.y.saturating_sub(n);
1515 let c_line_width = self.line_width(cursor.y);
1516 if let Some(move_col) = self.move_col() {
1517 cursor.x = min(move_col, c_line_width);
1518 } else {
1519 cursor.x = min(cursor.x, c_line_width);
1520 }
1521
1522 let c = self.set_cursor(cursor, extend_selection);
1523 let s = self.scroll_cursor_to_visible();
1524 c || s
1525 }
1526
1527 pub fn move_down(&mut self, n: upos_type, extend_selection: bool) -> bool {
1530 let mut cursor = self.cursor();
1531
1532 cursor.y = min(cursor.y + n, self.len_lines() - 1);
1533 let c_line_width = self.line_width(cursor.y);
1534 if let Some(move_col) = self.move_col() {
1535 cursor.x = min(move_col, c_line_width);
1536 } else {
1537 cursor.x = min(cursor.x, c_line_width);
1538 }
1539
1540 let c = self.set_cursor(cursor, extend_selection);
1541 let s = self.scroll_cursor_to_visible();
1542 c || s
1543 }
1544
1545 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1549 let mut cursor = self.cursor();
1550
1551 cursor.x = 'f: {
1552 for (idx, g) in self.line_graphemes(cursor.y).enumerate() {
1553 if g != " " && g != "\t" {
1554 if cursor.x != idx as upos_type {
1555 break 'f idx as upos_type;
1556 } else {
1557 break 'f 0;
1558 }
1559 }
1560 }
1561 0
1562 };
1563
1564 self.set_move_col(Some(cursor.x));
1565 let c = self.set_cursor(cursor, extend_selection);
1566 let s = self.scroll_cursor_to_visible();
1567 c || s
1568 }
1569
1570 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1574 let mut cursor = self.cursor();
1575
1576 cursor.x = self.line_width(cursor.y);
1577
1578 self.set_move_col(Some(cursor.x));
1579 let c = self.set_cursor(cursor, extend_selection);
1580 let s = self.scroll_cursor_to_visible();
1581 c || s
1582 }
1583
1584 pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1586 let cursor = TextPosition::new(0, 0);
1587
1588 let c = self.set_cursor(cursor, extend_selection);
1589 let s = self.scroll_cursor_to_visible();
1590 c || s
1591 }
1592
1593 pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1595 let len = self.len_lines();
1596
1597 let cursor = TextPosition::new(0, len - 1);
1598
1599 let c = self.set_cursor(cursor, extend_selection);
1600 let s = self.scroll_cursor_to_visible();
1601 c || s
1602 }
1603
1604 pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1606 let (ox, oy) = self.offset();
1607
1608 let cursor = TextPosition::new(ox as upos_type, oy as upos_type);
1609
1610 let c = self.set_cursor(cursor, extend_selection);
1611 let s = self.scroll_cursor_to_visible();
1612 c || s
1613 }
1614
1615 pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1617 let (ox, oy) = self.offset();
1618 let (ox, oy) = (ox as upos_type, oy as upos_type);
1619 let len = self.len_lines();
1620
1621 let cursor =
1622 TextPosition::new(ox, min(oy + self.vertical_page() as upos_type - 1, len - 1));
1623
1624 let c = self.set_cursor(cursor, extend_selection);
1625 let s = self.scroll_cursor_to_visible();
1626 c || s
1627 }
1628
1629 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1631 let cursor = self.cursor();
1632
1633 let word = self.next_word_end(cursor);
1634
1635 let c = self.set_cursor(word, extend_selection);
1636 let s = self.scroll_cursor_to_visible();
1637 c || s
1638 }
1639
1640 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1642 let cursor = self.cursor();
1643
1644 let word = self.prev_word_start(cursor);
1645
1646 let c = self.set_cursor(word, extend_selection);
1647 let s = self.scroll_cursor_to_visible();
1648 c || s
1649 }
1650}
1651
1652impl HasScreenCursor for TextAreaState {
1653 fn screen_cursor(&self) -> Option<(u16, u16)> {
1655 if self.is_focused() {
1656 let cursor = self.cursor();
1657 let (ox, oy) = self.offset();
1658 let (ox, oy) = (ox as upos_type, oy as upos_type);
1659
1660 if cursor.y < oy {
1661 None
1662 } else if cursor.y >= oy + (self.inner.height + self.dark_offset.1) as upos_type {
1663 None
1664 } else {
1665 if cursor.x < ox {
1666 None
1667 } else if cursor.x > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1668 None
1669 } else {
1670 let sy = self.row_to_screen(cursor);
1671 let sx = self.col_to_screen(cursor);
1672
1673 if let Some((sx, sy)) = sx.iter().zip(sy.iter()).next() {
1674 Some((self.inner.x + *sx, self.inner.y + *sy))
1675 } else {
1676 None
1677 }
1678 }
1679 }
1680 } else {
1681 None
1682 }
1683 }
1684}
1685
1686impl RelocatableState for TextAreaState {
1687 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1688 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1690 self.area = relocate_area(self.area, shift, clip);
1691 self.inner = relocate_area(self.inner, shift, clip);
1692 }
1693}
1694
1695impl TextAreaState {
1696 pub fn screen_to_row(&self, scy: i16) -> upos_type {
1699 let (_, oy) = self.offset();
1700 let oy = oy as upos_type + self.dark_offset.1 as upos_type;
1701
1702 if scy < 0 {
1703 oy.saturating_sub((scy as ipos_type).unsigned_abs())
1704 } else if scy as u16 >= (self.inner.height + self.dark_offset.1) {
1705 min(oy + scy as upos_type, self.len_lines().saturating_sub(1))
1706 } else {
1707 let scy = oy + scy as upos_type;
1708 let len = self.len_lines();
1709 if scy < len {
1710 scy
1711 } else {
1712 len.saturating_sub(1)
1713 }
1714 }
1715 }
1716
1717 pub fn screen_to_col(&self, row: upos_type, scx: i16) -> upos_type {
1724 self.try_screen_to_col(row, scx).expect("valid_row")
1725 }
1726
1727 pub fn try_screen_to_col(&self, row: upos_type, scx: i16) -> Result<upos_type, TextError> {
1734 let (ox, _) = self.offset();
1735
1736 let ox = ox as upos_type + self.dark_offset.0 as upos_type;
1737
1738 if scx < 0 {
1739 Ok(ox.saturating_sub((scx as ipos_type).unsigned_abs()))
1740 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
1741 Ok(min(ox + scx as upos_type, self.line_width(row)))
1742 } else {
1743 let scx = scx as u16;
1744
1745 let line = self.try_glyphs(
1746 row..row + 1,
1747 ox as u16,
1748 self.inner.width + self.dark_offset.0,
1749 )?;
1750
1751 let mut col = ox;
1752 for g in line {
1753 if scx < g.screen_pos().0 + g.screen_width() {
1754 break;
1755 }
1756 col = g.pos().x + 1;
1757 }
1758 Ok(col)
1759 }
1760 }
1761
1762 pub fn row_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
1765 let pos = pos.into();
1766 let (_, oy) = self.offset();
1767
1768 if pos.y < oy as upos_type {
1769 return None;
1770 }
1771
1772 let screen_y = pos.y - oy as upos_type;
1773
1774 if screen_y >= self.dark_offset.1 as upos_type {
1775 Some(screen_y as u16 - self.dark_offset.1)
1776 } else {
1777 None
1778 }
1779 }
1780
1781 pub fn col_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
1784 self.try_col_to_screen(pos).expect("valid_pos")
1785 }
1786
1787 pub fn try_col_to_screen(
1790 &self,
1791 pos: impl Into<TextPosition>,
1792 ) -> Result<Option<u16>, TextError> {
1793 let pos = pos.into();
1794 let (ox, _) = self.offset();
1795
1796 if pos.x < ox as upos_type {
1797 return Ok(None);
1798 }
1799
1800 let line = self.try_glyphs(
1801 pos.y..pos.y + 1,
1802 ox as u16,
1803 self.inner.width + self.dark_offset.0,
1804 )?;
1805 let mut screen_x = 0;
1806 for g in line {
1807 if g.pos().x == pos.x {
1808 break;
1809 }
1810 screen_x = g.screen_pos().0 + g.screen_width();
1811 }
1812
1813 if screen_x >= self.dark_offset.0 {
1814 Ok(Some(screen_x - self.dark_offset.0))
1815 } else {
1816 Ok(None)
1817 }
1818 }
1819
1820 pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
1826 let (scx, scy) = (cursor.0, cursor.1);
1827
1828 let cy = self.screen_to_row(scy);
1829 let cx = self.screen_to_col(cy, scx);
1830
1831 let c = self.set_cursor(TextPosition::new(cx, cy), extend_selection);
1832 let s = self.scroll_cursor_to_visible();
1833 c || s
1834 }
1835
1836 pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
1843 let (scx, scy) = (cursor.0, cursor.1);
1844 let anchor = self.anchor();
1845
1846 let cy = self.screen_to_row(scy);
1847 let cx = self.screen_to_col(cy, scx);
1848 let cursor = TextPosition::new(cx, cy);
1849
1850 let cursor = if cursor < anchor {
1851 self.word_start(cursor)
1852 } else {
1853 self.word_end(cursor)
1854 };
1855
1856 if !self.is_word_boundary(anchor) {
1858 if cursor < anchor {
1859 self.set_cursor(self.word_end(anchor), false);
1860 } else {
1861 self.set_cursor(self.word_start(anchor), false);
1862 }
1863 }
1864
1865 let c = self.set_cursor(cursor, extend_selection);
1866 let s = self.scroll_cursor_to_visible();
1867 c || s
1868 }
1869}
1870
1871impl TextAreaState {
1872 pub fn vertical_max_offset(&self) -> usize {
1877 self.vscroll.max_offset()
1878 }
1879
1880 pub fn vertical_offset(&self) -> usize {
1882 self.vscroll.offset()
1883 }
1884
1885 pub fn vertical_page(&self) -> usize {
1887 self.vscroll.page_len()
1888 }
1889
1890 pub fn vertical_scroll(&self) -> usize {
1892 self.vscroll.scroll_by()
1893 }
1894
1895 pub fn horizontal_max_offset(&self) -> usize {
1899 self.hscroll.max_offset()
1900 }
1901
1902 pub fn horizontal_offset(&self) -> usize {
1904 self.hscroll.offset()
1905 }
1906
1907 pub fn horizontal_page(&self) -> usize {
1909 self.hscroll.page_len()
1910 }
1911
1912 pub fn horizontal_scroll(&self) -> usize {
1914 self.hscroll.scroll_by()
1915 }
1916
1917 #[allow(unused_assignments)]
1924 pub fn set_vertical_offset(&mut self, row_offset: usize) -> bool {
1925 self.vscroll.set_offset(row_offset)
1926 }
1927
1928 #[allow(unused_assignments)]
1935 pub fn set_horizontal_offset(&mut self, col_offset: usize) -> bool {
1936 self.hscroll.set_offset(col_offset)
1937 }
1938
1939 pub fn scroll_to_row(&mut self, pos: usize) -> bool {
1941 self.vscroll.set_offset(pos)
1942 }
1943
1944 pub fn scroll_to_col(&mut self, pos: usize) -> bool {
1946 self.hscroll.set_offset(pos)
1947 }
1948
1949 pub fn scroll_up(&mut self, delta: usize) -> bool {
1951 self.vscroll.scroll_up(delta)
1952 }
1953
1954 pub fn scroll_down(&mut self, delta: usize) -> bool {
1956 self.vscroll.scroll_down(delta)
1957 }
1958
1959 pub fn scroll_left(&mut self, delta: usize) -> bool {
1961 self.hscroll.scroll_left(delta)
1962 }
1963
1964 pub fn scroll_right(&mut self, delta: usize) -> bool {
1966 self.hscroll.scroll_right(delta)
1967 }
1968}
1969
1970impl TextAreaState {
1971 pub fn scroll_cursor_to_visible(&mut self) -> bool {
1974 let old_offset = self.offset();
1975
1976 let cursor = self.cursor();
1977 let (ox, oy) = self.offset();
1978 let (ox, oy) = (ox as upos_type, oy as upos_type);
1979
1980 let noy = if cursor.y < oy {
1981 cursor.y
1982 } else if cursor.y >= oy + (self.inner.height + self.dark_offset.1) as upos_type {
1983 cursor
1984 .y
1985 .saturating_sub((self.inner.height + self.dark_offset.1) as upos_type - 1)
1986 } else {
1987 oy
1988 };
1989
1990 let nox = if cursor.x < ox {
1991 cursor.x
1992 } else if cursor.x >= ox + (self.inner.width + self.dark_offset.0) as upos_type {
1993 cursor
1994 .x
1995 .saturating_sub((self.inner.width + self.dark_offset.0) as upos_type)
1996 } else {
1997 ox
1998 };
1999
2000 self.set_offset((nox as usize, noy as usize));
2001
2002 self.offset() != old_offset
2003 }
2004}
2005
2006impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2007 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2008 fn tc(r: bool) -> TextOutcome {
2010 if r {
2011 TextOutcome::TextChanged
2012 } else {
2013 TextOutcome::Unchanged
2014 }
2015 }
2016
2017 let mut r = if self.is_focused() {
2018 match event {
2019 ct_event!(key press c)
2020 | ct_event!(key press SHIFT-c)
2021 | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2022 ct_event!(keycode press Tab) => {
2023 tc(if !self.focus.gained() {
2025 self.insert_tab()
2026 } else {
2027 false
2028 })
2029 }
2030 ct_event!(keycode press SHIFT-BackTab) => {
2031 tc(if !self.focus.gained() {
2033 self.insert_backtab()
2034 } else {
2035 false
2036 })
2037 }
2038 ct_event!(keycode press Enter) => tc(self.insert_newline()),
2039 ct_event!(keycode press Backspace) => tc(self.delete_prev_char()),
2040 ct_event!(keycode press Delete) => tc(self.delete_next_char()),
2041 ct_event!(keycode press CONTROL-Backspace)
2042 | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2043 ct_event!(keycode press CONTROL-Delete) | ct_event!(keycode press ALT-Delete) => {
2044 tc(self.delete_next_word())
2045 }
2046 ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2047 ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2048 ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2049 ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2050 ct_event!(key press CONTROL-'z') => tc(self.undo()),
2051 ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2052
2053 ct_event!(key release _)
2054 | ct_event!(key release SHIFT-_)
2055 | ct_event!(key release CONTROL_ALT-_)
2056 | ct_event!(keycode release Tab)
2057 | ct_event!(keycode release Enter)
2058 | ct_event!(keycode release Backspace)
2059 | ct_event!(keycode release Delete)
2060 | ct_event!(keycode release CONTROL-Backspace)
2061 | ct_event!(keycode release ALT-Backspace)
2062 | ct_event!(keycode release CONTROL-Delete)
2063 | ct_event!(key release CONTROL-'x')
2064 | ct_event!(key release CONTROL-'v')
2065 | ct_event!(key release CONTROL-'d')
2066 | ct_event!(key release CONTROL-'y')
2067 | ct_event!(key release CONTROL-'z')
2068 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2069 _ => TextOutcome::Continue,
2070 }
2071 } else {
2072 TextOutcome::Continue
2073 };
2074 if r == TextOutcome::Continue {
2075 r = self.handle(event, ReadOnly);
2076 }
2077 r
2078 }
2079}
2080
2081impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
2082 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2083 let mut r = if self.is_focused() {
2084 match event {
2085 ct_event!(keycode press Left) => self.move_left(1, false).into(),
2086 ct_event!(keycode press Right) => self.move_right(1, false).into(),
2087 ct_event!(keycode press Up) => self.move_up(1, false).into(),
2088 ct_event!(keycode press Down) => self.move_down(1, false).into(),
2089 ct_event!(keycode press PageUp) => self
2090 .move_up(self.vertical_page() as upos_type, false)
2091 .into(),
2092 ct_event!(keycode press PageDown) => self
2093 .move_down(self.vertical_page() as upos_type, false)
2094 .into(),
2095 ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
2096 ct_event!(keycode press End) => self.move_to_line_end(false).into(),
2097 ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
2098 ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
2099 ct_event!(keycode press CONTROL-Up) => false.into(),
2100 ct_event!(keycode press CONTROL-Down) => false.into(),
2101 ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
2102 ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
2103 ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
2104 ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
2105
2106 ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
2107 ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
2108 ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
2109 ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
2110 ct_event!(keycode press ALT-PageUp) => {
2111 self.scroll_up(max(self.vertical_page() / 2, 1)).into()
2112 }
2113 ct_event!(keycode press ALT-PageDown) => {
2114 self.scroll_down(max(self.vertical_page() / 2, 1)).into()
2115 }
2116 ct_event!(keycode press ALT_SHIFT-PageUp) => {
2117 self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
2118 }
2119 ct_event!(keycode press ALT_SHIFT-PageDown) => {
2120 self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
2121 }
2122
2123 ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
2124 ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
2125 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
2126 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
2127 ct_event!(keycode press SHIFT-PageUp) => {
2128 self.move_up(self.vertical_page() as upos_type, true).into()
2129 }
2130 ct_event!(keycode press SHIFT-PageDown) => self
2131 .move_down(self.vertical_page() as upos_type, true)
2132 .into(),
2133 ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
2134 ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
2135 ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
2136 ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
2137 ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
2138 ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
2139 ct_event!(key press CONTROL-'a') => self.select_all().into(),
2140 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
2141
2142 ct_event!(keycode release Left)
2143 | ct_event!(keycode release Right)
2144 | ct_event!(keycode release Up)
2145 | ct_event!(keycode release Down)
2146 | ct_event!(keycode release PageUp)
2147 | ct_event!(keycode release PageDown)
2148 | ct_event!(keycode release Home)
2149 | ct_event!(keycode release End)
2150 | ct_event!(keycode release CONTROL-Left)
2151 | ct_event!(keycode release CONTROL-Right)
2152 | ct_event!(keycode release CONTROL-Up)
2153 | ct_event!(keycode release CONTROL-Down)
2154 | ct_event!(keycode release CONTROL-PageUp)
2155 | ct_event!(keycode release CONTROL-PageDown)
2156 | ct_event!(keycode release CONTROL-Home)
2157 | ct_event!(keycode release CONTROL-End)
2158 | ct_event!(keycode release ALT-Left)
2159 | ct_event!(keycode release ALT-Right)
2160 | ct_event!(keycode release ALT-Up)
2161 | ct_event!(keycode release ALT-Down)
2162 | ct_event!(keycode release ALT-PageUp)
2163 | ct_event!(keycode release ALT-PageDown)
2164 | ct_event!(keycode release ALT_SHIFT-PageUp)
2165 | ct_event!(keycode release ALT_SHIFT-PageDown)
2166 | ct_event!(keycode release SHIFT-Left)
2167 | ct_event!(keycode release SHIFT-Right)
2168 | ct_event!(keycode release SHIFT-Up)
2169 | ct_event!(keycode release SHIFT-Down)
2170 | ct_event!(keycode release SHIFT-PageUp)
2171 | ct_event!(keycode release SHIFT-PageDown)
2172 | ct_event!(keycode release SHIFT-Home)
2173 | ct_event!(keycode release SHIFT-End)
2174 | ct_event!(keycode release CONTROL_SHIFT-Left)
2175 | ct_event!(keycode release CONTROL_SHIFT-Right)
2176 | ct_event!(keycode release CONTROL_SHIFT-Home)
2177 | ct_event!(keycode release CONTROL_SHIFT-End)
2178 | ct_event!(key release CONTROL-'a')
2179 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2180 _ => TextOutcome::Continue,
2181 }
2182 } else {
2183 TextOutcome::Continue
2184 };
2185
2186 if r == TextOutcome::Continue {
2187 r = self.handle(event, MouseOnly);
2188 }
2189 r
2190 }
2191}
2192
2193impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
2194 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
2195 flow!(match event {
2196 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2197 let cx = m.column as i16 - self.inner.x as i16;
2198 let cy = m.row as i16 - self.inner.y as i16;
2199 self.set_screen_cursor((cx, cy), true).into()
2200 }
2201 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2202 let cx = m.column as i16 - self.inner.x as i16;
2203 let cy = m.row as i16 - self.inner.y as i16;
2204 self.set_screen_cursor_words((cx, cy), true).into()
2205 }
2206 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2207 let ty = self.screen_to_row(m.row as i16 - self.inner.y as i16);
2208 let tx = self.screen_to_col(ty, m.column as i16 - self.inner.x as i16);
2209 let test = TextPosition::new(tx, ty);
2210 let start = self.word_start(test);
2211 let end = self.word_end(test);
2212 self.set_selection(start, end).into()
2213 }
2214 ct_event!(mouse down Left for column,row) => {
2215 if self.inner.contains((*column, *row).into()) {
2216 let cx = (column - self.inner.x) as i16;
2217 let cy = (row - self.inner.y) as i16;
2218 self.set_screen_cursor((cx, cy), false).into()
2219 } else {
2220 TextOutcome::Continue
2221 }
2222 }
2223 ct_event!(mouse down CONTROL-Left for column,row) => {
2224 if self.inner.contains((*column, *row).into()) {
2225 let cx = (column - self.inner.x) as i16;
2226 let cy = (row - self.inner.y) as i16;
2227 self.set_screen_cursor((cx, cy), true).into()
2228 } else {
2229 TextOutcome::Continue
2230 }
2231 }
2232 ct_event!(mouse down ALT-Left for column,row) => {
2233 if self.inner.contains((*column, *row).into()) {
2234 let cx = (column - self.inner.x) as i16;
2235 let cy = (row - self.inner.y) as i16;
2236 self.set_screen_cursor_words((cx, cy), true).into()
2237 } else {
2238 TextOutcome::Continue
2239 }
2240 }
2241 _ => TextOutcome::Continue,
2242 });
2243
2244 let mut sas = ScrollAreaState::new()
2245 .area(self.inner)
2246 .h_scroll(&mut self.hscroll)
2247 .v_scroll(&mut self.vscroll);
2248 let r = match sas.handle(event, MouseOnly) {
2249 ScrollOutcome::Up(v) => self.scroll_up(v),
2250 ScrollOutcome::Down(v) => self.scroll_down(v),
2251 ScrollOutcome::Left(v) => self.scroll_left(v),
2252 ScrollOutcome::Right(v) => self.scroll_right(v),
2253 ScrollOutcome::VPos(v) => self.set_vertical_offset(v),
2254 ScrollOutcome::HPos(v) => self.set_horizontal_offset(v),
2255 _ => false,
2256 };
2257 if r {
2258 return TextOutcome::Changed;
2259 }
2260
2261 TextOutcome::Continue
2262 }
2263}
2264
2265pub fn handle_events(
2269 state: &mut TextAreaState,
2270 focus: bool,
2271 event: &crossterm::event::Event,
2272) -> TextOutcome {
2273 state.focus.set(focus);
2274 state.handle(event, Regular)
2275}
2276
2277pub fn handle_readonly_events(
2281 state: &mut TextAreaState,
2282 focus: bool,
2283 event: &crossterm::event::Event,
2284) -> TextOutcome {
2285 state.focus.set(focus);
2286 state.handle(event, ReadOnly)
2287}
2288
2289pub fn handle_mouse_events(
2291 state: &mut TextAreaState,
2292 event: &crossterm::event::Event,
2293) -> TextOutcome {
2294 state.handle(event, MouseOnly)
2295}