1use crate::_private::NonExhaustive;
17use crate::clipboard::{global_clipboard, Clipboard};
18use crate::core::{TextCore, TextString};
19use crate::event::{ReadOnly, TextOutcome};
20use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
21use crate::{
22 ipos_type, upos_type, Cursor, Glyph, Grapheme, HasScreenCursor, TextError, TextFocusGained,
23 TextFocusLost, TextPosition, TextRange, TextStyle,
24};
25use crossterm::event::KeyModifiers;
26use rat_event::util::MouseFlags;
27use rat_event::{ct_event, HandleEvent, MouseOnly, Regular};
28use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
29use rat_reloc::{relocate_area, relocate_dark_offset, RelocatableState};
30use ratatui::buffer::Buffer;
31use ratatui::layout::Rect;
32use ratatui::prelude::BlockExt;
33use ratatui::style::{Style, Stylize};
34#[cfg(feature = "unstable-widget-ref")]
35use ratatui::widgets::StatefulWidgetRef;
36use ratatui::widgets::{Block, StatefulWidget, Widget};
37use std::borrow::Cow;
38use std::cmp::min;
39use std::ops::Range;
40
41#[derive(Debug, Default, Clone)]
47pub struct TextInput<'a> {
48 block: Option<Block<'a>>,
49 style: Style,
50 focus_style: Option<Style>,
51 select_style: Option<Style>,
52 invalid_style: Option<Style>,
53 on_focus_gained: TextFocusGained,
54 on_focus_lost: TextFocusLost,
55 passwd: bool,
56 text_style: Vec<Style>,
57}
58
59#[derive(Debug)]
61pub struct TextInputState {
62 pub area: Rect,
65 pub inner: Rect,
68
69 pub offset: upos_type,
72 pub dark_offset: (u16, u16),
75
76 pub value: TextCore<TextString>,
78 pub invalid: bool,
81 pub passwd: bool,
84 pub overwrite: bool,
87 pub on_focus_gained: TextFocusGained,
90 pub on_focus_lost: TextFocusLost,
93
94 pub focus: FocusFlag,
97
98 pub mouse: MouseFlags,
101
102 pub non_exhaustive: NonExhaustive,
104}
105
106impl<'a> TextInput<'a> {
107 pub fn new() -> Self {
109 Self::default()
110 }
111
112 #[inline]
114 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
115 if let Some(styles) = styles {
116 self.styles(styles)
117 } else {
118 self
119 }
120 }
121
122 #[inline]
124 pub fn styles(mut self, styles: TextStyle) -> Self {
125 self.style = styles.style;
126 if styles.focus.is_some() {
127 self.focus_style = styles.focus;
128 }
129 if styles.select.is_some() {
130 self.select_style = styles.select;
131 }
132 if styles.invalid.is_some() {
133 self.invalid_style = styles.invalid;
134 }
135 if let Some(of) = styles.on_focus_gained {
136 self.on_focus_gained = of;
137 }
138 if let Some(of) = styles.on_focus_lost {
139 self.on_focus_lost = of;
140 }
141 if let Some(border_style) = styles.border_style {
142 self.block = self.block.map(|v| v.border_style(border_style));
143 }
144 self.block = self.block.map(|v| v.style(self.style));
145 if styles.block.is_some() {
146 self.block = styles.block;
147 }
148 self.block = self.block.map(|v| v.style(self.style));
149 self
150 }
151
152 #[inline]
154 pub fn style(mut self, style: impl Into<Style>) -> Self {
155 self.style = style.into();
156 self.block = self.block.map(|v| v.style(self.style));
157 self
158 }
159
160 #[inline]
162 pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
163 self.focus_style = Some(style.into());
164 self
165 }
166
167 #[inline]
169 pub fn select_style(mut self, style: impl Into<Style>) -> Self {
170 self.select_style = Some(style.into());
171 self
172 }
173
174 #[inline]
177 pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
178 self.invalid_style = Some(style.into());
179 self
180 }
181
182 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
187 self.text_style = styles.into_iter().collect();
188 self
189 }
190
191 #[inline]
193 pub fn block(mut self, block: Block<'a>) -> Self {
194 self.block = Some(block);
195 self.block = self.block.map(|v| v.style(self.style));
196 self
197 }
198
199 #[inline]
201 pub fn passwd(mut self) -> Self {
202 self.passwd = true;
203 self
204 }
205
206 #[inline]
208 pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
209 self.on_focus_gained = of;
210 self
211 }
212
213 #[inline]
215 pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
216 self.on_focus_lost = of;
217 self
218 }
219}
220
221#[cfg(feature = "unstable-widget-ref")]
222impl<'a> StatefulWidgetRef for TextInput<'a> {
223 type State = TextInputState;
224
225 fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
226 render_ref(self, area, buf, state);
227 }
228}
229
230impl StatefulWidget for TextInput<'_> {
231 type State = TextInputState;
232
233 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
234 render_ref(&self, area, buf, state);
235 }
236}
237
238fn render_ref(widget: &TextInput<'_>, area: Rect, buf: &mut Buffer, state: &mut TextInputState) {
239 state.area = area;
240 state.inner = widget.block.inner_if_some(area);
241 state.passwd = widget.passwd;
242 state.on_focus_gained = widget.on_focus_gained;
243 state.on_focus_lost = widget.on_focus_lost;
244
245 let inner = state.inner;
246
247 let style = widget.style;
248 let focus_style = if let Some(focus_style) = widget.focus_style {
249 focus_style
250 } else {
251 style
252 };
253 let select_style = if let Some(select_style) = widget.select_style {
254 select_style
255 } else {
256 Style::default().black().on_yellow()
257 };
258 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
259 invalid_style
260 } else {
261 Style::default().red()
262 };
263
264 let (style, select_style) = if state.focus.get() {
265 if state.invalid {
266 (
267 style.patch(focus_style).patch(invalid_style),
268 style
269 .patch(focus_style)
270 .patch(select_style)
271 .patch(invalid_style),
272 )
273 } else {
274 (
275 style.patch(focus_style),
276 style.patch(focus_style).patch(select_style),
277 )
278 }
279 } else {
280 if state.invalid {
281 (style.patch(invalid_style), style.patch(invalid_style))
282 } else {
283 (style, style)
284 }
285 };
286
287 if widget.block.is_some() {
289 widget.block.render(area, buf);
290 } else {
291 buf.set_style(area, style);
292 }
293
294 if inner.width == 0 || inner.height == 0 {
295 return;
297 }
298
299 let ox = state.offset() as u16;
300 let show_range = {
302 let start = ox as upos_type;
303 let end = min(start + inner.width as upos_type, state.len());
304 state.bytes_at_range(start..end)
305 };
306 let selection = state.selection();
307 let mut styles = Vec::new();
308
309 if widget.passwd {
310 let glyph_iter = state
312 .value
313 .glyphs(0..1, ox, inner.width)
314 .expect("valid_offset");
315 for g in glyph_iter {
316 if g.screen_width() > 0 {
317 let mut style = style;
318 styles.clear();
319 state
320 .value
321 .styles_at_page(show_range.clone(), g.text_bytes().start, &mut styles);
322 for style_nr in &styles {
323 if let Some(s) = widget.text_style.get(*style_nr) {
324 style = style.patch(*s);
325 }
326 }
327 if selection.contains(&g.pos().x) {
329 style = style.patch(select_style);
330 };
331
332 let screen_pos = g.screen_pos();
334
335 if let Some(cell) = buf.cell_mut((inner.x + screen_pos.0, inner.y + screen_pos.1)) {
337 cell.set_symbol("*");
338 cell.set_style(style);
339 }
340 }
341 }
342 } else {
343 let glyph_iter = state
344 .value
345 .glyphs(0..1, ox, inner.width)
346 .expect("valid_offset");
347 for g in glyph_iter {
348 if g.screen_width() > 0 {
349 let mut style = style;
350 styles.clear();
351 state
352 .value
353 .styles_at_page(show_range.clone(), g.text_bytes().start, &mut styles);
354 for style_nr in &styles {
355 if let Some(s) = widget.text_style.get(*style_nr) {
356 style = style.patch(*s);
357 }
358 }
359 if selection.contains(&g.pos().x) {
361 style = style.patch(select_style);
362 };
363
364 let screen_pos = g.screen_pos();
366
367 if let Some(cell) = buf.cell_mut((inner.x + screen_pos.0, inner.y + screen_pos.1)) {
369 cell.set_symbol(g.glyph());
370 cell.set_style(style);
371 }
372 for d in 1..g.screen_width() {
374 if let Some(cell) =
375 buf.cell_mut((inner.x + screen_pos.0 + d, inner.y + screen_pos.1))
376 {
377 cell.reset();
378 cell.set_style(style);
379 }
380 }
381 }
382 }
383 }
384}
385
386impl Clone for TextInputState {
387 fn clone(&self) -> Self {
388 Self {
389 area: self.area,
390 inner: self.inner,
391 offset: self.offset,
392 dark_offset: self.dark_offset,
393 value: self.value.clone(),
394 invalid: self.invalid,
395 passwd: Default::default(),
396 overwrite: Default::default(),
397 on_focus_gained: Default::default(),
398 on_focus_lost: Default::default(),
399 focus: FocusFlag::named(self.focus.name()),
400 mouse: Default::default(),
401 non_exhaustive: NonExhaustive,
402 }
403 }
404}
405
406impl Default for TextInputState {
407 fn default() -> Self {
408 let mut value = TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard()));
409 value.set_glyph_line_break(false);
410
411 Self {
412 area: Default::default(),
413 inner: Default::default(),
414 offset: Default::default(),
415 dark_offset: Default::default(),
416 value,
417 invalid: Default::default(),
418 passwd: Default::default(),
419 overwrite: Default::default(),
420 on_focus_gained: Default::default(),
421 on_focus_lost: Default::default(),
422 focus: Default::default(),
423 mouse: Default::default(),
424 non_exhaustive: NonExhaustive,
425 }
426 }
427}
428
429impl HasFocus for TextInputState {
430 fn build(&self, builder: &mut FocusBuilder) {
431 builder.leaf_widget(self);
432 }
433
434 fn focus(&self) -> FocusFlag {
435 self.focus.clone()
436 }
437
438 fn area(&self) -> Rect {
439 self.area
440 }
441}
442
443impl TextInputState {
444 pub fn new() -> Self {
445 Self::default()
446 }
447
448 pub fn named(name: &str) -> Self {
449 Self {
450 focus: FocusFlag::named(name),
451 ..TextInputState::default()
452 }
453 }
454
455 #[inline]
457 pub fn set_invalid(&mut self, invalid: bool) {
458 self.invalid = invalid;
459 }
460
461 #[inline]
463 pub fn get_invalid(&self) -> bool {
464 self.invalid
465 }
466
467 #[inline]
471 pub fn set_overwrite(&mut self, overwrite: bool) {
472 self.overwrite = overwrite;
473 }
474
475 #[inline]
477 pub fn overwrite(&self) -> bool {
478 self.overwrite
479 }
480}
481
482impl TextInputState {
483 #[inline]
486 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
487 match clip {
488 None => self.value.set_clipboard(None),
489 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
490 }
491 }
492
493 #[inline]
496 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
497 self.value.clipboard()
498 }
499
500 #[inline]
502 pub fn copy_to_clip(&mut self) -> bool {
503 let Some(clip) = self.value.clipboard() else {
504 return false;
505 };
506 if self.passwd {
507 return false;
508 }
509
510 _ = clip.set_string(self.selected_text().as_ref());
511 false
512 }
513
514 #[inline]
516 pub fn cut_to_clip(&mut self) -> bool {
517 let Some(clip) = self.value.clipboard() else {
518 return false;
519 };
520 if self.passwd {
521 return false;
522 }
523
524 match clip.set_string(self.selected_text().as_ref()) {
525 Ok(_) => self.delete_range(self.selection()),
526 Err(_) => false,
527 }
528 }
529
530 #[inline]
532 pub fn paste_from_clip(&mut self) -> bool {
533 let Some(clip) = self.value.clipboard() else {
534 return false;
535 };
536
537 if let Ok(text) = clip.get_string() {
538 self.insert_str(text)
539 } else {
540 false
541 }
542 }
543}
544
545impl TextInputState {
546 #[inline]
548 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
549 match undo {
550 None => self.value.set_undo_buffer(None),
551 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
552 }
553 }
554
555 #[inline]
557 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
558 self.value.undo_buffer()
559 }
560
561 #[inline]
563 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
564 self.value.undo_buffer_mut()
565 }
566
567 #[inline]
569 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
570 self.value.recent_replay_log()
571 }
572
573 #[inline]
575 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
576 self.value.replay_log(replay)
577 }
578
579 #[inline]
581 pub fn undo(&mut self) -> bool {
582 self.value.undo()
583 }
584
585 #[inline]
587 pub fn redo(&mut self) -> bool {
588 self.value.redo()
589 }
590}
591
592impl TextInputState {
593 #[inline]
604 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
605 self.value.set_styles(styles);
606 }
607
608 #[inline]
613 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
614 self.value.add_style(range, style);
615 }
616
617 #[inline]
620 pub fn add_range_style(
621 &mut self,
622 range: Range<upos_type>,
623 style: usize,
624 ) -> Result<(), TextError> {
625 let r = self
626 .value
627 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
628 self.value.add_style(r, style);
629 Ok(())
630 }
631
632 #[inline]
634 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
635 self.value.remove_style(range, style);
636 }
637
638 #[inline]
640 pub fn remove_range_style(
641 &mut self,
642 range: Range<upos_type>,
643 style: usize,
644 ) -> Result<(), TextError> {
645 let r = self
646 .value
647 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
648 self.value.remove_style(r, style);
649 Ok(())
650 }
651
652 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
654 self.value.styles_in(range, buf)
655 }
656
657 #[inline]
659 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
660 self.value.styles_at(byte_pos, buf)
661 }
662
663 #[inline]
666 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
667 self.value.style_match(byte_pos, style)
668 }
669
670 #[inline]
672 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
673 self.value.styles()
674 }
675}
676
677impl TextInputState {
678 #[inline]
680 pub fn offset(&self) -> upos_type {
681 self.offset
682 }
683
684 #[inline]
686 pub fn set_offset(&mut self, offset: upos_type) {
687 self.offset = offset;
688 }
689
690 #[inline]
692 pub fn cursor(&self) -> upos_type {
693 self.value.cursor().x
694 }
695
696 #[inline]
698 pub fn anchor(&self) -> upos_type {
699 self.value.anchor().x
700 }
701
702 #[inline]
704 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
705 self.value
706 .set_cursor(TextPosition::new(cursor, 0), extend_selection)
707 }
708
709 #[inline]
711 pub fn has_selection(&self) -> bool {
712 self.value.has_selection()
713 }
714
715 #[inline]
717 pub fn selection(&self) -> Range<upos_type> {
718 let v = self.value.selection();
719 v.start.x..v.end.x
720 }
721
722 #[inline]
724 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
725 self.value
726 .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
727 }
728
729 #[inline]
731 pub fn select_all(&mut self) -> bool {
732 self.value.select_all()
733 }
734
735 #[inline]
737 pub fn selected_text(&self) -> &str {
738 match self.str_slice(self.selection()) {
739 Cow::Borrowed(v) => v,
740 Cow::Owned(_) => {
741 unreachable!()
742 }
743 }
744 }
745}
746
747impl TextInputState {
748 #[inline]
750 pub fn is_empty(&self) -> bool {
751 self.value.is_empty()
752 }
753
754 #[inline]
756 pub fn value<T: for<'a> From<&'a str>>(&self) -> T {
757 self.value.text().as_str().into()
758 }
759
760 #[inline]
762 pub fn text(&self) -> &str {
763 self.value.text().as_str()
764 }
765
766 #[inline]
768 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
769 self.value.str_slice_byte(range).expect("valid_range")
770 }
771
772 #[inline]
774 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
775 self.value.str_slice_byte(range)
776 }
777
778 #[inline]
780 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
781 self.value
782 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
783 .expect("valid_range")
784 }
785
786 #[inline]
788 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
789 self.value
790 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
791 }
792
793 #[inline]
795 pub fn len(&self) -> upos_type {
796 self.value.line_width(0).expect("valid_row")
797 }
798
799 #[inline]
801 pub fn line_width(&self) -> upos_type {
802 self.value.line_width(0).expect("valid_row")
803 }
804
805 #[inline]
808 pub fn glyphs(&self, screen_offset: u16, screen_width: u16) -> impl Iterator<Item = Glyph<'_>> {
809 self.value
810 .glyphs(0..1, screen_offset, screen_width)
811 .expect("valid_rows")
812 }
813
814 #[inline]
816 pub fn text_graphemes(&self, pos: upos_type) -> impl Cursor<Item = Grapheme<'_>> {
817 self.value
818 .text_graphemes(TextPosition::new(pos, 0))
819 .expect("valid_pos")
820 }
821
822 #[inline]
824 pub fn try_text_graphemes(
825 &self,
826 pos: upos_type,
827 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
828 self.value.text_graphemes(TextPosition::new(pos, 0))
829 }
830
831 #[inline]
833 pub fn graphemes(
834 &self,
835 range: Range<upos_type>,
836 pos: upos_type,
837 ) -> impl Cursor<Item = Grapheme<'_>> {
838 self.value
839 .graphemes(
840 TextRange::new((range.start, 0), (range.end, 0)),
841 TextPosition::new(pos, 0),
842 )
843 .expect("valid_args")
844 }
845
846 #[inline]
848 pub fn try_graphemes(
849 &self,
850 range: Range<upos_type>,
851 pos: upos_type,
852 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
853 self.value.graphemes(
854 TextRange::new((range.start, 0), (range.end, 0)),
855 TextPosition::new(pos, 0),
856 )
857 }
858
859 #[inline]
862 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
863 self.value
864 .byte_at(TextPosition::new(pos, 0))
865 .expect("valid_pos")
866 }
867
868 #[inline]
871 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
872 self.value.byte_at(TextPosition::new(pos, 0))
873 }
874
875 #[inline]
877 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
878 self.value
879 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
880 .expect("valid_range")
881 }
882
883 #[inline]
885 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
886 self.value
887 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
888 }
889
890 #[inline]
893 pub fn byte_pos(&self, byte: usize) -> upos_type {
894 self.value.byte_pos(byte).map(|v| v.x).expect("valid_pos")
895 }
896
897 #[inline]
900 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
901 self.value.byte_pos(byte).map(|v| v.x)
902 }
903
904 #[inline]
906 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
907 self.value
908 .byte_range(bytes)
909 .map(|v| v.start.x..v.end.x)
910 .expect("valid_range")
911 }
912
913 #[inline]
915 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
916 self.value.byte_range(bytes).map(|v| v.start.x..v.end.x)
917 }
918}
919
920impl TextInputState {
921 #[inline]
923 pub fn clear(&mut self) -> bool {
924 if self.is_empty() {
925 false
926 } else {
927 self.offset = 0;
928 self.value.clear();
929 true
930 }
931 }
932
933 #[inline]
937 pub fn set_value<S: Into<String>>(&mut self, s: S) {
938 self.offset = 0;
939 self.value.set_text(TextString::new_string(s.into()));
940 }
941
942 #[inline]
946 pub fn set_text<S: Into<String>>(&mut self, s: S) {
947 self.offset = 0;
948 self.value.set_text(TextString::new_string(s.into()));
949 }
950
951 #[inline]
953 pub fn insert_char(&mut self, c: char) -> bool {
954 if self.has_selection() {
955 self.value
956 .remove_str_range(self.value.selection())
957 .expect("valid_selection");
958 }
959 if c == '\n' {
960 return false;
961 } else if c == '\t' {
962 self.value
963 .insert_tab(self.value.cursor())
964 .expect("valid_cursor");
965 } else {
966 self.value
967 .insert_char(self.value.cursor(), c)
968 .expect("valid_cursor");
969 }
970 self.scroll_cursor_to_visible();
971 true
972 }
973
974 pub fn insert_tab(&mut self) -> bool {
977 if self.has_selection() {
978 self.value
979 .remove_str_range(self.value.selection())
980 .expect("valid_selection");
981 }
982 self.value
983 .insert_tab(self.value.cursor())
984 .expect("valid_cursor");
985 self.scroll_cursor_to_visible();
986 true
987 }
988
989 #[inline]
991 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
992 let t = t.as_ref();
993 if self.has_selection() {
994 self.value
995 .remove_str_range(self.value.selection())
996 .expect("valid_selection");
997 }
998 self.value
999 .insert_str(self.value.cursor(), t)
1000 .expect("valid_cursor");
1001 self.scroll_cursor_to_visible();
1002 true
1003 }
1004
1005 #[inline]
1007 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1008 self.try_delete_range(range).expect("valid_range")
1009 }
1010
1011 #[inline]
1013 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1014 if !range.is_empty() {
1015 self.value
1016 .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1017 self.scroll_cursor_to_visible();
1018 Ok(true)
1019 } else {
1020 Ok(false)
1021 }
1022 }
1023}
1024
1025impl TextInputState {
1026 #[inline]
1028 pub fn delete_next_char(&mut self) -> bool {
1029 if self.has_selection() {
1030 self.delete_range(self.selection())
1031 } else {
1032 let r = self
1033 .value
1034 .remove_next_char(self.value.cursor())
1035 .expect("valid_cursor");
1036 let s = self.scroll_cursor_to_visible();
1037
1038 r || s
1039 }
1040 }
1041
1042 #[inline]
1044 pub fn delete_prev_char(&mut self) -> bool {
1045 if self.value.has_selection() {
1046 self.delete_range(self.selection())
1047 } else {
1048 let r = self
1049 .value
1050 .remove_prev_char(self.value.cursor())
1051 .expect("valid_cursor");
1052 let s = self.scroll_cursor_to_visible();
1053
1054 r || s
1055 }
1056 }
1057
1058 pub fn next_word_start(&self, pos: upos_type) -> upos_type {
1060 self.try_next_word_start(pos).expect("valid_pos")
1061 }
1062
1063 pub fn try_next_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1065 self.value
1066 .next_word_start(TextPosition::new(pos, 0))
1067 .map(|v| v.x)
1068 }
1069
1070 pub fn next_word_end(&self, pos: upos_type) -> upos_type {
1073 self.try_next_word_end(pos).expect("valid_pos")
1074 }
1075
1076 pub fn try_next_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1079 self.value
1080 .next_word_end(TextPosition::new(pos, 0))
1081 .map(|v| v.x)
1082 }
1083
1084 pub fn prev_word_start(&self, pos: upos_type) -> upos_type {
1088 self.try_prev_word_start(pos).expect("valid_pos")
1089 }
1090
1091 pub fn try_prev_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1095 self.value
1096 .prev_word_start(TextPosition::new(pos, 0))
1097 .map(|v| v.x)
1098 }
1099
1100 pub fn prev_word_end(&self, pos: upos_type) -> upos_type {
1104 self.try_prev_word_end(pos).expect("valid_pos")
1105 }
1106
1107 pub fn try_prev_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1111 self.value
1112 .prev_word_end(TextPosition::new(pos, 0))
1113 .map(|v| v.x)
1114 }
1115
1116 pub fn is_word_boundary(&self, pos: upos_type) -> bool {
1118 self.try_is_word_boundary(pos).expect("valid_pos")
1119 }
1120
1121 pub fn try_is_word_boundary(&self, pos: upos_type) -> Result<bool, TextError> {
1123 self.value.is_word_boundary(TextPosition::new(pos, 0))
1124 }
1125
1126 pub fn word_start(&self, pos: upos_type) -> upos_type {
1128 self.try_word_start(pos).expect("valid_pos")
1129 }
1130
1131 pub fn try_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1133 self.value
1134 .word_start(TextPosition::new(pos, 0))
1135 .map(|v| v.x)
1136 }
1137
1138 pub fn word_end(&self, pos: upos_type) -> upos_type {
1140 self.try_word_end(pos).expect("valid_pos")
1141 }
1142
1143 pub fn try_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1145 self.value.word_end(TextPosition::new(pos, 0)).map(|v| v.x)
1146 }
1147
1148 #[inline]
1150 pub fn delete_next_word(&mut self) -> bool {
1151 if self.has_selection() {
1152 self.delete_range(self.selection())
1153 } else {
1154 let cursor = self.cursor();
1155
1156 let start = self.next_word_start(cursor);
1157 if start != cursor {
1158 self.delete_range(cursor..start)
1159 } else {
1160 let end = self.next_word_end(cursor);
1161 self.delete_range(cursor..end)
1162 }
1163 }
1164 }
1165
1166 #[inline]
1168 pub fn delete_prev_word(&mut self) -> bool {
1169 if self.has_selection() {
1170 self.delete_range(self.selection())
1171 } else {
1172 let cursor = self.cursor();
1173
1174 let end = self.prev_word_end(cursor);
1175 if end != cursor {
1176 self.delete_range(end..cursor)
1177 } else {
1178 let start = self.prev_word_start(cursor);
1179 self.delete_range(start..cursor)
1180 }
1181 }
1182 }
1183
1184 #[inline]
1186 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1187 let c = min(self.cursor() + 1, self.len());
1188 let c = self.set_cursor(c, extend_selection);
1189 let s = self.scroll_cursor_to_visible();
1190 c || s
1191 }
1192
1193 #[inline]
1195 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1196 let c = self.cursor().saturating_sub(1);
1197 let c = self.set_cursor(c, extend_selection);
1198 let s = self.scroll_cursor_to_visible();
1199 c || s
1200 }
1201
1202 #[inline]
1204 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1205 let c = self.set_cursor(0, extend_selection);
1206 let s = self.scroll_cursor_to_visible();
1207 c || s
1208 }
1209
1210 #[inline]
1212 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1213 let c = self.len();
1214 let c = self.set_cursor(c, extend_selection);
1215 let s = self.scroll_cursor_to_visible();
1216 c || s
1217 }
1218
1219 #[inline]
1220 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1221 let cursor = self.cursor();
1222 let end = self.next_word_end(cursor);
1223 let c = self.set_cursor(end, extend_selection);
1224 let s = self.scroll_cursor_to_visible();
1225 c || s
1226 }
1227
1228 #[inline]
1229 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1230 let cursor = self.cursor();
1231 let start = self.prev_word_start(cursor);
1232 let c = self.set_cursor(start, extend_selection);
1233 let s = self.scroll_cursor_to_visible();
1234 c || s
1235 }
1236}
1237
1238impl HasScreenCursor for TextInputState {
1239 #[inline]
1241 fn screen_cursor(&self) -> Option<(u16, u16)> {
1242 if self.is_focused() {
1243 let cx = self.cursor();
1244 let ox = self.offset();
1245
1246 if cx < ox {
1247 None
1248 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1249 None
1250 } else {
1251 self.col_to_screen(cx)
1252 .map(|sc| (self.inner.x + sc, self.inner.y))
1253 }
1254 } else {
1255 None
1256 }
1257 }
1258}
1259
1260impl RelocatableState for TextInputState {
1261 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1262 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1264 self.area = relocate_area(self.area, shift, clip);
1265 self.inner = relocate_area(self.inner, shift, clip);
1266 }
1267}
1268
1269impl TextInputState {
1270 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1273 let ox = self.offset();
1274
1275 let scx = scx + self.dark_offset.0 as i16;
1276
1277 if scx < 0 {
1278 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1279 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
1280 min(ox + scx as upos_type, self.len())
1281 } else {
1282 let scx = scx as u16;
1283
1284 let line = self.glyphs(ox as u16, self.inner.width + self.dark_offset.0);
1285
1286 let mut col = ox;
1287 for g in line {
1288 if scx < g.screen_pos().0 + g.screen_width() {
1289 break;
1290 }
1291 col = g.pos().x + 1;
1292 }
1293 col
1294 }
1295 }
1296
1297 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1300 let ox = self.offset();
1301
1302 if pos < ox {
1303 return None;
1304 }
1305
1306 let line = self.glyphs(ox as u16, self.inner.width + self.dark_offset.0);
1307 let mut screen_x = 0;
1308 for g in line {
1309 if g.pos().x == pos {
1310 break;
1311 }
1312 screen_x = g.screen_pos().0 + g.screen_width();
1313 }
1314
1315 if screen_x >= self.dark_offset.0 {
1316 Some(screen_x - self.dark_offset.0)
1317 } else {
1318 None
1319 }
1320 }
1321
1322 #[inline]
1326 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1327 let scx = cursor;
1328
1329 let cx = self.screen_to_col(scx);
1330
1331 let c = self.set_cursor(cx, extend_selection);
1332 let s = self.scroll_cursor_to_visible();
1333 c || s
1334 }
1335
1336 pub fn set_screen_cursor_words(&mut self, screen_cursor: i16, extend_selection: bool) -> bool {
1343 let anchor = self.anchor();
1344
1345 let cx = self.screen_to_col(screen_cursor);
1346 let cursor = cx;
1347
1348 let cursor = if cursor < anchor {
1349 self.word_start(cursor)
1350 } else {
1351 self.word_end(cursor)
1352 };
1353
1354 if !self.is_word_boundary(anchor) {
1356 if cursor < anchor {
1357 self.set_cursor(self.word_end(anchor), false);
1358 } else {
1359 self.set_cursor(self.word_start(anchor), false);
1360 }
1361 }
1362
1363 let c = self.set_cursor(cursor, extend_selection);
1364 let s = self.scroll_cursor_to_visible();
1365 c || s
1366 }
1367
1368 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1370 self.set_offset(self.offset.saturating_sub(delta));
1371 true
1372 }
1373
1374 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1376 self.set_offset(self.offset + delta);
1377 true
1378 }
1379
1380 pub fn scroll_cursor_to_visible(&mut self) -> bool {
1382 let old_offset = self.offset();
1383
1384 let c = self.cursor();
1385 let o = self.offset();
1386
1387 let no = if c < o {
1388 c
1389 } else if c >= o + (self.inner.width + self.dark_offset.0) as upos_type {
1390 c.saturating_sub((self.inner.width + self.dark_offset.0) as upos_type)
1391 } else {
1392 o
1393 };
1394
1395 self.set_offset(no);
1396
1397 self.offset() != old_offset
1398 }
1399}
1400
1401impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextInputState {
1402 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1403 fn tc(r: bool) -> TextOutcome {
1405 if r {
1406 TextOutcome::TextChanged
1407 } else {
1408 TextOutcome::Unchanged
1409 }
1410 }
1411 fn overwrite(state: &mut TextInputState) {
1412 if state.overwrite {
1413 state.overwrite = false;
1414 state.clear();
1415 }
1416 }
1417 fn clear_overwrite(state: &mut TextInputState) {
1418 state.overwrite = false;
1419 }
1420
1421 if self.lost_focus() {
1423 match self.on_focus_lost {
1424 TextFocusLost::None => {}
1425 TextFocusLost::Position0 => {
1426 self.move_to_line_start(false);
1427 }
1429 }
1430 }
1431 if self.gained_focus() {
1432 match self.on_focus_gained {
1433 TextFocusGained::None => {}
1434 TextFocusGained::Overwrite => {
1435 self.overwrite = true;
1436 }
1437 TextFocusGained::SelectAll => {
1438 self.select_all();
1439 }
1441 }
1442 }
1443
1444 let mut r = if self.is_focused() {
1445 match event {
1446 ct_event!(key press c)
1447 | ct_event!(key press SHIFT-c)
1448 | ct_event!(key press CONTROL_ALT-c) => {
1449 overwrite(self);
1450 tc(self.insert_char(*c))
1451 }
1452 ct_event!(keycode press Tab) => {
1453 tc(if !self.focus.gained() {
1455 clear_overwrite(self);
1456 self.insert_tab()
1457 } else {
1458 false
1459 })
1460 }
1461 ct_event!(keycode press Backspace) => {
1462 clear_overwrite(self);
1463 tc(self.delete_prev_char())
1464 }
1465 ct_event!(keycode press Delete) => {
1466 clear_overwrite(self);
1467 tc(self.delete_next_char())
1468 }
1469 ct_event!(keycode press CONTROL-Backspace)
1470 | ct_event!(keycode press ALT-Backspace) => {
1471 clear_overwrite(self);
1472 tc(self.delete_prev_word())
1473 }
1474 ct_event!(keycode press CONTROL-Delete) => {
1475 clear_overwrite(self);
1476 tc(self.delete_next_word())
1477 }
1478 ct_event!(key press CONTROL-'x') => {
1479 clear_overwrite(self);
1480 tc(self.cut_to_clip())
1481 }
1482 ct_event!(key press CONTROL-'v') => {
1483 overwrite(self);
1484 tc(self.paste_from_clip())
1485 }
1486 ct_event!(key press CONTROL-'d') => {
1487 clear_overwrite(self);
1488 tc(self.clear())
1489 }
1490 ct_event!(key press CONTROL-'z') => {
1491 clear_overwrite(self);
1492 tc(self.undo())
1493 }
1494 ct_event!(key press CONTROL_SHIFT-'Z') => {
1495 clear_overwrite(self);
1496 tc(self.redo())
1497 }
1498
1499 ct_event!(key release _)
1500 | ct_event!(key release SHIFT-_)
1501 | ct_event!(key release CONTROL_ALT-_)
1502 | ct_event!(keycode release Tab)
1503 | ct_event!(keycode release Backspace)
1504 | ct_event!(keycode release Delete)
1505 | ct_event!(keycode release CONTROL-Backspace)
1506 | ct_event!(keycode release ALT-Backspace)
1507 | ct_event!(keycode release CONTROL-Delete)
1508 | ct_event!(key release CONTROL-'x')
1509 | ct_event!(key release CONTROL-'v')
1510 | ct_event!(key release CONTROL-'d')
1511 | ct_event!(key release CONTROL-'y')
1512 | ct_event!(key release CONTROL-'z')
1513 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1514
1515 _ => TextOutcome::Continue,
1516 }
1517 } else {
1518 TextOutcome::Continue
1519 };
1520 if r == TextOutcome::Continue {
1521 r = self.handle(event, ReadOnly);
1522 }
1523 r
1524 }
1525}
1526
1527impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextInputState {
1528 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1529 fn clear_overwrite(state: &mut TextInputState) {
1530 state.overwrite = false;
1531 }
1532
1533 let mut r = if self.is_focused() {
1534 match event {
1535 ct_event!(keycode press Left) => {
1536 clear_overwrite(self);
1537 self.move_left(false).into()
1538 }
1539 ct_event!(keycode press Right) => {
1540 clear_overwrite(self);
1541 self.move_right(false).into()
1542 }
1543 ct_event!(keycode press CONTROL-Left) => {
1544 clear_overwrite(self);
1545 self.move_to_prev_word(false).into()
1546 }
1547 ct_event!(keycode press CONTROL-Right) => {
1548 clear_overwrite(self);
1549 self.move_to_next_word(false).into()
1550 }
1551 ct_event!(keycode press Home) => {
1552 clear_overwrite(self);
1553 self.move_to_line_start(false).into()
1554 }
1555 ct_event!(keycode press End) => {
1556 clear_overwrite(self);
1557 self.move_to_line_end(false).into()
1558 }
1559 ct_event!(keycode press SHIFT-Left) => {
1560 clear_overwrite(self);
1561 self.move_left(true).into()
1562 }
1563 ct_event!(keycode press SHIFT-Right) => {
1564 clear_overwrite(self);
1565 self.move_right(true).into()
1566 }
1567 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1568 clear_overwrite(self);
1569 self.move_to_prev_word(true).into()
1570 }
1571 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1572 clear_overwrite(self);
1573 self.move_to_next_word(true).into()
1574 }
1575 ct_event!(keycode press SHIFT-Home) => {
1576 clear_overwrite(self);
1577 self.move_to_line_start(true).into()
1578 }
1579 ct_event!(keycode press SHIFT-End) => {
1580 clear_overwrite(self);
1581 self.move_to_line_end(true).into()
1582 }
1583 ct_event!(keycode press ALT-Left) => {
1584 clear_overwrite(self);
1585 self.scroll_left(1).into()
1586 }
1587 ct_event!(keycode press ALT-Right) => {
1588 clear_overwrite(self);
1589 self.scroll_right(1).into()
1590 }
1591 ct_event!(key press CONTROL-'a') => {
1592 clear_overwrite(self);
1593 self.select_all().into()
1594 }
1595 ct_event!(key press CONTROL-'c') => {
1596 clear_overwrite(self);
1597 self.copy_to_clip().into()
1598 }
1599
1600 ct_event!(keycode release Left)
1601 | ct_event!(keycode release Right)
1602 | ct_event!(keycode release CONTROL-Left)
1603 | ct_event!(keycode release CONTROL-Right)
1604 | ct_event!(keycode release Home)
1605 | ct_event!(keycode release End)
1606 | ct_event!(keycode release SHIFT-Left)
1607 | ct_event!(keycode release SHIFT-Right)
1608 | ct_event!(keycode release CONTROL_SHIFT-Left)
1609 | ct_event!(keycode release CONTROL_SHIFT-Right)
1610 | ct_event!(keycode release SHIFT-Home)
1611 | ct_event!(keycode release SHIFT-End)
1612 | ct_event!(key release CONTROL-'a')
1613 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1614
1615 _ => TextOutcome::Continue,
1616 }
1617 } else {
1618 TextOutcome::Continue
1619 };
1620
1621 if r == TextOutcome::Continue {
1622 r = self.handle(event, MouseOnly);
1623 }
1624 r
1625 }
1626}
1627
1628impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextInputState {
1629 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1630 fn clear_overwrite(state: &mut TextInputState) {
1631 state.overwrite = false;
1632 }
1633
1634 match event {
1635 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1636 let c = (m.column as i16) - (self.inner.x as i16);
1637 clear_overwrite(self);
1638 self.set_screen_cursor(c, true).into()
1639 }
1640 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1641 let cx = m.column as i16 - self.inner.x as i16;
1642 clear_overwrite(self);
1643 self.set_screen_cursor_words(cx, true).into()
1644 }
1645 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1646 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1647 let start = self.word_start(tx);
1648 let end = self.word_end(tx);
1649 clear_overwrite(self);
1650 self.set_selection(start, end).into()
1651 }
1652 ct_event!(mouse down Left for column,row) => {
1653 if self.gained_focus() {
1654 TextOutcome::Unchanged
1657 } else if self.inner.contains((*column, *row).into()) {
1658 let c = (column - self.inner.x) as i16;
1659 clear_overwrite(self);
1660 self.set_screen_cursor(c, false).into()
1661 } else {
1662 TextOutcome::Continue
1663 }
1664 }
1665 ct_event!(mouse down CONTROL-Left for column,row) => {
1666 if self.inner.contains((*column, *row).into()) {
1667 let cx = (column - self.inner.x) as i16;
1668 clear_overwrite(self);
1669 self.set_screen_cursor(cx, true).into()
1670 } else {
1671 TextOutcome::Continue
1672 }
1673 }
1674 ct_event!(mouse down ALT-Left for column,row) => {
1675 if self.inner.contains((*column, *row).into()) {
1676 let cx = (column - self.inner.x) as i16;
1677 clear_overwrite(self);
1678 self.set_screen_cursor_words(cx, true).into()
1679 } else {
1680 TextOutcome::Continue
1681 }
1682 }
1683 _ => TextOutcome::Continue,
1684 }
1685 }
1686}
1687
1688pub fn handle_events(
1692 state: &mut TextInputState,
1693 focus: bool,
1694 event: &crossterm::event::Event,
1695) -> TextOutcome {
1696 state.focus.set(focus);
1697 state.handle(event, Regular)
1698}
1699
1700pub fn handle_readonly_events(
1704 state: &mut TextInputState,
1705 focus: bool,
1706 event: &crossterm::event::Event,
1707) -> TextOutcome {
1708 state.focus.set(focus);
1709 state.handle(event, ReadOnly)
1710}
1711
1712pub fn handle_mouse_events(
1714 state: &mut TextInputState,
1715 event: &crossterm::event::Event,
1716) -> TextOutcome {
1717 state.handle(event, MouseOnly)
1718}