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};
34use ratatui::widgets::{Block, StatefulWidget, Widget};
35use std::borrow::Cow;
36use std::cmp::min;
37use std::ops::Range;
38
39#[derive(Debug, Default, Clone)]
45pub struct TextInput<'a> {
46 block: Option<Block<'a>>,
47 style: Style,
48 focus_style: Option<Style>,
49 select_style: Option<Style>,
50 invalid_style: Option<Style>,
51 on_focus_gained: TextFocusGained,
52 on_focus_lost: TextFocusLost,
53 passwd: bool,
54 text_style: Vec<Style>,
55}
56
57#[derive(Debug)]
59pub struct TextInputState {
60 pub area: Rect,
63 pub inner: Rect,
66
67 pub offset: upos_type,
70 pub dark_offset: (u16, u16),
73 pub scroll_to_cursor: bool,
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
221impl<'a> StatefulWidget for &TextInput<'a> {
222 type State = TextInputState;
223
224 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
225 render_ref(self, area, buf, state);
226 }
227}
228
229impl StatefulWidget for TextInput<'_> {
230 type State = TextInputState;
231
232 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
233 render_ref(&self, area, buf, state);
234 }
235}
236
237fn render_ref(widget: &TextInput<'_>, area: Rect, buf: &mut Buffer, state: &mut TextInputState) {
238 state.area = area;
239 state.inner = widget.block.inner_if_some(area);
240 state.passwd = widget.passwd;
241 state.on_focus_gained = widget.on_focus_gained;
242 state.on_focus_lost = widget.on_focus_lost;
243
244 if state.scroll_to_cursor {
245 let c = state.cursor();
246 let o = state.offset();
247
248 let mut no = if c < o {
249 c
250 } else if c >= o + (state.inner.width + state.dark_offset.0) as upos_type {
251 c.saturating_sub((state.inner.width + state.dark_offset.0) as upos_type)
252 } else {
253 o
254 };
255 if c == no + (state.inner.width + state.dark_offset.0) as upos_type {
258 no = no.saturating_add(1);
259 }
260 state.set_offset(no);
261 }
262
263 let inner = state.inner;
264
265 let style = widget.style;
266 let focus_style = if let Some(focus_style) = widget.focus_style {
267 focus_style
268 } else {
269 style
270 };
271 let select_style = if let Some(select_style) = widget.select_style {
272 select_style
273 } else {
274 Style::default().black().on_yellow()
275 };
276 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
277 invalid_style
278 } else {
279 Style::default().red()
280 };
281
282 let (style, select_style) = if state.focus.get() {
283 if state.invalid {
284 (
285 style.patch(focus_style).patch(invalid_style),
286 style
287 .patch(focus_style)
288 .patch(select_style)
289 .patch(invalid_style),
290 )
291 } else {
292 (
293 style.patch(focus_style),
294 style.patch(focus_style).patch(select_style),
295 )
296 }
297 } else {
298 if state.invalid {
299 (style.patch(invalid_style), style.patch(invalid_style))
300 } else {
301 (style, style)
302 }
303 };
304
305 if let Some(block) = &widget.block {
307 block.render(area, buf);
308 } else {
309 buf.set_style(area, style);
310 }
311
312 if inner.width == 0 || inner.height == 0 {
313 return;
315 }
316
317 let ox = state.offset() as u16;
318 let show_range = {
320 let start = ox as upos_type;
321 let end = min(start + inner.width as upos_type, state.len());
322 state.bytes_at_range(start..end)
323 };
324 let selection = state.selection();
325 let mut styles = Vec::new();
326
327 if widget.passwd {
328 let glyph_iter = state
330 .value
331 .glyphs(0..1, ox, inner.width)
332 .expect("valid_offset");
333 for g in glyph_iter {
334 if g.screen_width() > 0 {
335 let mut style = style;
336 styles.clear();
337 state
338 .value
339 .styles_at_page(show_range.clone(), g.text_bytes().start, &mut styles);
340 for style_nr in &styles {
341 if let Some(s) = widget.text_style.get(*style_nr) {
342 style = style.patch(*s);
343 }
344 }
345 if selection.contains(&g.pos().x) {
347 style = style.patch(select_style);
348 };
349
350 let screen_pos = g.screen_pos();
352
353 if let Some(cell) = buf.cell_mut((inner.x + screen_pos.0, inner.y + screen_pos.1)) {
355 cell.set_symbol("*");
356 cell.set_style(style);
357 }
358 }
359 }
360 } else {
361 let glyph_iter = state
362 .value
363 .glyphs(0..1, ox, inner.width)
364 .expect("valid_offset");
365 for g in glyph_iter {
366 if g.screen_width() > 0 {
367 let mut style = style;
368 styles.clear();
369 state
370 .value
371 .styles_at_page(show_range.clone(), g.text_bytes().start, &mut styles);
372 for style_nr in &styles {
373 if let Some(s) = widget.text_style.get(*style_nr) {
374 style = style.patch(*s);
375 }
376 }
377 if selection.contains(&g.pos().x) {
379 style = style.patch(select_style);
380 };
381
382 let screen_pos = g.screen_pos();
384
385 if let Some(cell) = buf.cell_mut((inner.x + screen_pos.0, inner.y + screen_pos.1)) {
387 cell.set_symbol(g.glyph());
388 cell.set_style(style);
389 }
390 for d in 1..g.screen_width() {
392 if let Some(cell) =
393 buf.cell_mut((inner.x + screen_pos.0 + d, inner.y + screen_pos.1))
394 {
395 cell.reset();
396 cell.set_style(style);
397 }
398 }
399 }
400 }
401 }
402}
403
404impl Clone for TextInputState {
405 fn clone(&self) -> Self {
406 Self {
407 area: self.area,
408 inner: self.inner,
409 offset: self.offset,
410 dark_offset: self.dark_offset,
411 scroll_to_cursor: self.scroll_to_cursor,
412 value: self.value.clone(),
413 invalid: self.invalid,
414 passwd: Default::default(),
415 overwrite: Default::default(),
416 on_focus_gained: Default::default(),
417 on_focus_lost: Default::default(),
418 focus: FocusFlag::named(self.focus.name()),
419 mouse: Default::default(),
420 non_exhaustive: NonExhaustive,
421 }
422 }
423}
424
425impl Default for TextInputState {
426 fn default() -> Self {
427 let mut value = TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard()));
428 value.set_glyph_line_break(false);
429
430 Self {
431 area: Default::default(),
432 inner: Default::default(),
433 offset: Default::default(),
434 dark_offset: Default::default(),
435 scroll_to_cursor: Default::default(),
436 value,
437 invalid: Default::default(),
438 passwd: Default::default(),
439 overwrite: Default::default(),
440 on_focus_gained: Default::default(),
441 on_focus_lost: Default::default(),
442 focus: Default::default(),
443 mouse: Default::default(),
444 non_exhaustive: NonExhaustive,
445 }
446 }
447}
448
449impl HasFocus for TextInputState {
450 fn build(&self, builder: &mut FocusBuilder) {
451 builder.leaf_widget(self);
452 }
453
454 fn focus(&self) -> FocusFlag {
455 self.focus.clone()
456 }
457
458 fn area(&self) -> Rect {
459 self.area
460 }
461}
462
463impl TextInputState {
464 pub fn new() -> Self {
465 Self::default()
466 }
467
468 pub fn named(name: &str) -> Self {
469 Self {
470 focus: FocusFlag::named(name),
471 ..TextInputState::default()
472 }
473 }
474
475 #[inline]
477 pub fn set_invalid(&mut self, invalid: bool) {
478 self.invalid = invalid;
479 }
480
481 #[inline]
483 pub fn get_invalid(&self) -> bool {
484 self.invalid
485 }
486
487 #[inline]
491 pub fn set_overwrite(&mut self, overwrite: bool) {
492 self.overwrite = overwrite;
493 }
494
495 #[inline]
497 pub fn overwrite(&self) -> bool {
498 self.overwrite
499 }
500}
501
502impl TextInputState {
503 #[inline]
506 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
507 match clip {
508 None => self.value.set_clipboard(None),
509 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
510 }
511 }
512
513 #[inline]
516 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
517 self.value.clipboard()
518 }
519
520 #[inline]
522 pub fn copy_to_clip(&mut self) -> bool {
523 let Some(clip) = self.value.clipboard() else {
524 return false;
525 };
526 if self.passwd {
527 return false;
528 }
529
530 _ = clip.set_string(self.selected_text().as_ref());
531 false
532 }
533
534 #[inline]
536 pub fn cut_to_clip(&mut self) -> bool {
537 let Some(clip) = self.value.clipboard() else {
538 return false;
539 };
540 if self.passwd {
541 return false;
542 }
543
544 match clip.set_string(self.selected_text().as_ref()) {
545 Ok(_) => self.delete_range(self.selection()),
546 Err(_) => false,
547 }
548 }
549
550 #[inline]
552 pub fn paste_from_clip(&mut self) -> bool {
553 let Some(clip) = self.value.clipboard() else {
554 return false;
555 };
556
557 if let Ok(text) = clip.get_string() {
558 self.insert_str(text)
559 } else {
560 false
561 }
562 }
563}
564
565impl TextInputState {
566 #[inline]
568 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
569 match undo {
570 None => self.value.set_undo_buffer(None),
571 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
572 }
573 }
574
575 #[inline]
577 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
578 self.value.undo_buffer()
579 }
580
581 #[inline]
583 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
584 self.value.undo_buffer_mut()
585 }
586
587 #[inline]
589 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
590 self.value.recent_replay_log()
591 }
592
593 #[inline]
595 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
596 self.value.replay_log(replay)
597 }
598
599 #[inline]
601 pub fn undo(&mut self) -> bool {
602 self.value.undo()
603 }
604
605 #[inline]
607 pub fn redo(&mut self) -> bool {
608 self.value.redo()
609 }
610}
611
612impl TextInputState {
613 #[inline]
624 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
625 self.value.set_styles(styles);
626 }
627
628 #[inline]
633 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
634 self.value.add_style(range, style);
635 }
636
637 #[inline]
640 pub fn add_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.add_style(r, style);
649 Ok(())
650 }
651
652 #[inline]
654 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
655 self.value.remove_style(range, style);
656 }
657
658 #[inline]
660 pub fn remove_range_style(
661 &mut self,
662 range: Range<upos_type>,
663 style: usize,
664 ) -> Result<(), TextError> {
665 let r = self
666 .value
667 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
668 self.value.remove_style(r, style);
669 Ok(())
670 }
671
672 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
674 self.value.styles_in(range, buf)
675 }
676
677 #[inline]
679 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
680 self.value.styles_at(byte_pos, buf)
681 }
682
683 #[inline]
686 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
687 self.value.style_match(byte_pos, style)
688 }
689
690 #[inline]
692 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
693 self.value.styles()
694 }
695}
696
697impl TextInputState {
698 #[inline]
700 pub fn offset(&self) -> upos_type {
701 self.offset
702 }
703
704 #[inline]
706 pub fn set_offset(&mut self, offset: upos_type) {
707 self.scroll_to_cursor = false;
708 self.offset = offset;
709 }
710
711 #[inline]
713 pub fn cursor(&self) -> upos_type {
714 self.value.cursor().x
715 }
716
717 #[inline]
719 pub fn anchor(&self) -> upos_type {
720 self.value.anchor().x
721 }
722
723 #[inline]
726 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
727 self.scroll_cursor_to_visible();
728 self.value
729 .set_cursor(TextPosition::new(cursor, 0), extend_selection)
730 }
731
732 #[inline]
734 pub fn has_selection(&self) -> bool {
735 self.value.has_selection()
736 }
737
738 #[inline]
740 pub fn selection(&self) -> Range<upos_type> {
741 let v = self.value.selection();
742 v.start.x..v.end.x
743 }
744
745 #[inline]
748 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
749 self.scroll_cursor_to_visible();
750 self.value
751 .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
752 }
753
754 #[inline]
757 pub fn select_all(&mut self) -> bool {
758 self.scroll_cursor_to_visible();
759 self.value.select_all()
760 }
761
762 #[inline]
764 pub fn selected_text(&self) -> &str {
765 match self.str_slice(self.selection()) {
766 Cow::Borrowed(v) => v,
767 Cow::Owned(_) => {
768 unreachable!()
769 }
770 }
771 }
772}
773
774impl TextInputState {
775 #[inline]
777 pub fn is_empty(&self) -> bool {
778 self.value.is_empty()
779 }
780
781 #[inline]
783 pub fn value<T: for<'a> From<&'a str>>(&self) -> T {
784 self.value.text().as_str().into()
785 }
786
787 #[inline]
789 pub fn text(&self) -> &str {
790 self.value.text().as_str()
791 }
792
793 #[inline]
795 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
796 self.value.str_slice_byte(range).expect("valid_range")
797 }
798
799 #[inline]
801 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
802 self.value.str_slice_byte(range)
803 }
804
805 #[inline]
807 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
808 self.value
809 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
810 .expect("valid_range")
811 }
812
813 #[inline]
815 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
816 self.value
817 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
818 }
819
820 #[inline]
822 pub fn len(&self) -> upos_type {
823 self.value.line_width(0).expect("valid_row")
824 }
825
826 #[inline]
828 pub fn line_width(&self) -> upos_type {
829 self.value.line_width(0).expect("valid_row")
830 }
831
832 #[inline]
835 pub fn glyphs(&self, screen_offset: u16, screen_width: u16) -> impl Iterator<Item = Glyph<'_>> {
836 self.value
837 .glyphs(0..1, screen_offset, screen_width)
838 .expect("valid_rows")
839 }
840
841 #[inline]
843 pub fn text_graphemes(&self, pos: upos_type) -> impl Cursor<Item = Grapheme<'_>> {
844 self.value
845 .text_graphemes(TextPosition::new(pos, 0))
846 .expect("valid_pos")
847 }
848
849 #[inline]
851 pub fn try_text_graphemes(
852 &self,
853 pos: upos_type,
854 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
855 self.value.text_graphemes(TextPosition::new(pos, 0))
856 }
857
858 #[inline]
860 pub fn graphemes(
861 &self,
862 range: Range<upos_type>,
863 pos: upos_type,
864 ) -> impl Cursor<Item = Grapheme<'_>> {
865 self.value
866 .graphemes(
867 TextRange::new((range.start, 0), (range.end, 0)),
868 TextPosition::new(pos, 0),
869 )
870 .expect("valid_args")
871 }
872
873 #[inline]
875 pub fn try_graphemes(
876 &self,
877 range: Range<upos_type>,
878 pos: upos_type,
879 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
880 self.value.graphemes(
881 TextRange::new((range.start, 0), (range.end, 0)),
882 TextPosition::new(pos, 0),
883 )
884 }
885
886 #[inline]
889 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
890 self.value
891 .byte_at(TextPosition::new(pos, 0))
892 .expect("valid_pos")
893 }
894
895 #[inline]
898 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
899 self.value.byte_at(TextPosition::new(pos, 0))
900 }
901
902 #[inline]
904 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
905 self.value
906 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
907 .expect("valid_range")
908 }
909
910 #[inline]
912 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
913 self.value
914 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
915 }
916
917 #[inline]
920 pub fn byte_pos(&self, byte: usize) -> upos_type {
921 self.value.byte_pos(byte).map(|v| v.x).expect("valid_pos")
922 }
923
924 #[inline]
927 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
928 self.value.byte_pos(byte).map(|v| v.x)
929 }
930
931 #[inline]
933 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
934 self.value
935 .byte_range(bytes)
936 .map(|v| v.start.x..v.end.x)
937 .expect("valid_range")
938 }
939
940 #[inline]
942 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
943 self.value.byte_range(bytes).map(|v| v.start.x..v.end.x)
944 }
945}
946
947impl TextInputState {
948 #[inline]
950 pub fn clear(&mut self) -> bool {
951 if self.is_empty() {
952 false
953 } else {
954 self.offset = 0;
955 self.value.clear();
956 true
957 }
958 }
959
960 #[inline]
964 pub fn set_value<S: Into<String>>(&mut self, s: S) {
965 self.offset = 0;
966 self.value.set_text(TextString::new_string(s.into()));
967 }
968
969 #[inline]
973 pub fn set_text<S: Into<String>>(&mut self, s: S) {
974 self.offset = 0;
975 self.value.set_text(TextString::new_string(s.into()));
976 }
977
978 #[inline]
980 pub fn insert_char(&mut self, c: char) -> bool {
981 if self.has_selection() {
982 self.value
983 .remove_str_range(self.value.selection())
984 .expect("valid_selection");
985 }
986 if c == '\n' {
987 return false;
988 } else if c == '\t' {
989 self.value
990 .insert_tab(self.value.cursor())
991 .expect("valid_cursor");
992 } else {
993 self.value
994 .insert_char(self.value.cursor(), c)
995 .expect("valid_cursor");
996 }
997 self.scroll_cursor_to_visible();
998 true
999 }
1000
1001 pub fn insert_tab(&mut self) -> bool {
1004 if self.has_selection() {
1005 self.value
1006 .remove_str_range(self.value.selection())
1007 .expect("valid_selection");
1008 }
1009 self.value
1010 .insert_tab(self.value.cursor())
1011 .expect("valid_cursor");
1012 self.scroll_cursor_to_visible();
1013 true
1014 }
1015
1016 #[inline]
1018 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1019 let t = t.as_ref();
1020 if self.has_selection() {
1021 self.value
1022 .remove_str_range(self.value.selection())
1023 .expect("valid_selection");
1024 }
1025 self.value
1026 .insert_str(self.value.cursor(), t)
1027 .expect("valid_cursor");
1028 self.scroll_cursor_to_visible();
1029 true
1030 }
1031
1032 #[inline]
1034 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1035 self.try_delete_range(range).expect("valid_range")
1036 }
1037
1038 #[inline]
1040 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1041 if !range.is_empty() {
1042 self.value
1043 .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1044 self.scroll_cursor_to_visible();
1045 Ok(true)
1046 } else {
1047 Ok(false)
1048 }
1049 }
1050}
1051
1052impl TextInputState {
1053 #[inline]
1055 pub fn delete_next_char(&mut self) -> bool {
1056 if self.has_selection() {
1057 self.delete_range(self.selection())
1058 } else {
1059 let r = self
1060 .value
1061 .remove_next_char(self.value.cursor())
1062 .expect("valid_cursor");
1063 self.scroll_cursor_to_visible();
1064 r
1065 }
1066 }
1067
1068 #[inline]
1070 pub fn delete_prev_char(&mut self) -> bool {
1071 if self.value.has_selection() {
1072 self.delete_range(self.selection())
1073 } else {
1074 let r = self
1075 .value
1076 .remove_prev_char(self.value.cursor())
1077 .expect("valid_cursor");
1078 self.scroll_cursor_to_visible();
1079 r
1080 }
1081 }
1082
1083 pub fn next_word_start(&self, pos: upos_type) -> upos_type {
1085 self.try_next_word_start(pos).expect("valid_pos")
1086 }
1087
1088 pub fn try_next_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1090 self.value
1091 .next_word_start(TextPosition::new(pos, 0))
1092 .map(|v| v.x)
1093 }
1094
1095 pub fn next_word_end(&self, pos: upos_type) -> upos_type {
1098 self.try_next_word_end(pos).expect("valid_pos")
1099 }
1100
1101 pub fn try_next_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1104 self.value
1105 .next_word_end(TextPosition::new(pos, 0))
1106 .map(|v| v.x)
1107 }
1108
1109 pub fn prev_word_start(&self, pos: upos_type) -> upos_type {
1113 self.try_prev_word_start(pos).expect("valid_pos")
1114 }
1115
1116 pub fn try_prev_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1120 self.value
1121 .prev_word_start(TextPosition::new(pos, 0))
1122 .map(|v| v.x)
1123 }
1124
1125 pub fn prev_word_end(&self, pos: upos_type) -> upos_type {
1129 self.try_prev_word_end(pos).expect("valid_pos")
1130 }
1131
1132 pub fn try_prev_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1136 self.value
1137 .prev_word_end(TextPosition::new(pos, 0))
1138 .map(|v| v.x)
1139 }
1140
1141 pub fn is_word_boundary(&self, pos: upos_type) -> bool {
1143 self.try_is_word_boundary(pos).expect("valid_pos")
1144 }
1145
1146 pub fn try_is_word_boundary(&self, pos: upos_type) -> Result<bool, TextError> {
1148 self.value.is_word_boundary(TextPosition::new(pos, 0))
1149 }
1150
1151 pub fn word_start(&self, pos: upos_type) -> upos_type {
1153 self.try_word_start(pos).expect("valid_pos")
1154 }
1155
1156 pub fn try_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1158 self.value
1159 .word_start(TextPosition::new(pos, 0))
1160 .map(|v| v.x)
1161 }
1162
1163 pub fn word_end(&self, pos: upos_type) -> upos_type {
1165 self.try_word_end(pos).expect("valid_pos")
1166 }
1167
1168 pub fn try_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1170 self.value.word_end(TextPosition::new(pos, 0)).map(|v| v.x)
1171 }
1172
1173 #[inline]
1175 pub fn delete_next_word(&mut self) -> bool {
1176 if self.has_selection() {
1177 self.delete_range(self.selection())
1178 } else {
1179 let cursor = self.cursor();
1180
1181 let start = self.next_word_start(cursor);
1182 if start != cursor {
1183 self.delete_range(cursor..start)
1184 } else {
1185 let end = self.next_word_end(cursor);
1186 self.delete_range(cursor..end)
1187 }
1188 }
1189 }
1190
1191 #[inline]
1193 pub fn delete_prev_word(&mut self) -> bool {
1194 if self.has_selection() {
1195 self.delete_range(self.selection())
1196 } else {
1197 let cursor = self.cursor();
1198
1199 let end = self.prev_word_end(cursor);
1200 if end != cursor {
1201 self.delete_range(end..cursor)
1202 } else {
1203 let start = self.prev_word_start(cursor);
1204 self.delete_range(start..cursor)
1205 }
1206 }
1207 }
1208
1209 #[inline]
1211 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1212 let c = min(self.cursor() + 1, self.len());
1213 self.set_cursor(c, extend_selection)
1214 }
1215
1216 #[inline]
1218 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1219 let c = self.cursor().saturating_sub(1);
1220 self.set_cursor(c, extend_selection)
1221 }
1222
1223 #[inline]
1225 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1226 self.set_cursor(0, extend_selection)
1227 }
1228
1229 #[inline]
1231 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1232 self.set_cursor(self.len(), extend_selection)
1233 }
1234
1235 #[inline]
1236 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1237 let cursor = self.cursor();
1238 let end = self.next_word_end(cursor);
1239 self.set_cursor(end, extend_selection)
1240 }
1241
1242 #[inline]
1243 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1244 let cursor = self.cursor();
1245 let start = self.prev_word_start(cursor);
1246 self.set_cursor(start, extend_selection)
1247 }
1248}
1249
1250impl HasScreenCursor for TextInputState {
1251 #[inline]
1253 fn screen_cursor(&self) -> Option<(u16, u16)> {
1254 if self.is_focused() {
1255 if self.has_selection() {
1256 None
1257 } else {
1258 let cx = self.cursor();
1259 let ox = self.offset();
1260
1261 if cx < ox {
1262 None
1263 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1264 None
1265 } else {
1266 self.col_to_screen(cx)
1267 .map(|sc| (self.inner.x + sc, self.inner.y))
1268 }
1269 }
1270 } else {
1271 None
1272 }
1273 }
1274}
1275
1276impl RelocatableState for TextInputState {
1277 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1278 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1280 self.area = relocate_area(self.area, shift, clip);
1281 self.inner = relocate_area(self.inner, shift, clip);
1282 }
1283}
1284
1285impl TextInputState {
1286 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1289 let ox = self.offset();
1290
1291 let scx = scx + self.dark_offset.0 as i16;
1292
1293 if scx < 0 {
1294 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1295 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
1296 min(ox + scx as upos_type, self.len())
1297 } else {
1298 let scx = scx as u16;
1299
1300 let line = self.glyphs(ox as u16, self.inner.width + self.dark_offset.0);
1301
1302 let mut col = ox;
1303 for g in line {
1304 if scx < g.screen_pos().0 + g.screen_width() {
1305 break;
1306 }
1307 col = g.pos().x + 1;
1308 }
1309 col
1310 }
1311 }
1312
1313 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1316 let ox = self.offset();
1317
1318 if pos < ox {
1319 return None;
1320 }
1321
1322 let line = self.glyphs(ox as u16, self.inner.width + self.dark_offset.0);
1323 let mut screen_x = 0;
1324 for g in line {
1325 if g.pos().x == pos {
1326 break;
1327 }
1328 screen_x = g.screen_pos().0 + g.screen_width();
1329 }
1330
1331 if screen_x >= self.dark_offset.0 {
1332 Some(screen_x - self.dark_offset.0)
1333 } else {
1334 None
1335 }
1336 }
1337
1338 #[inline]
1342 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1343 let scx = cursor;
1344
1345 let cx = self.screen_to_col(scx);
1346
1347 self.set_cursor(cx, extend_selection)
1348 }
1349
1350 pub fn set_screen_cursor_words(&mut self, screen_cursor: i16, extend_selection: bool) -> bool {
1357 let anchor = self.anchor();
1358
1359 let cx = self.screen_to_col(screen_cursor);
1360 let cursor = cx;
1361
1362 let cursor = if cursor < anchor {
1363 self.word_start(cursor)
1364 } else {
1365 self.word_end(cursor)
1366 };
1367
1368 if !self.is_word_boundary(anchor) {
1370 if cursor < anchor {
1371 self.set_cursor(self.word_end(anchor), false);
1372 } else {
1373 self.set_cursor(self.word_start(anchor), false);
1374 }
1375 }
1376
1377 self.set_cursor(cursor, extend_selection)
1378 }
1379
1380 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1382 self.set_offset(self.offset.saturating_sub(delta));
1383 true
1384 }
1385
1386 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1388 self.set_offset(self.offset + delta);
1389 true
1390 }
1391
1392 pub fn scroll_cursor_to_visible(&mut self) {
1394 self.scroll_to_cursor = true;
1395 }
1396}
1397
1398impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextInputState {
1399 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1400 fn tc(r: bool) -> TextOutcome {
1402 if r {
1403 TextOutcome::TextChanged
1404 } else {
1405 TextOutcome::Unchanged
1406 }
1407 }
1408 fn overwrite(state: &mut TextInputState) {
1409 if state.overwrite {
1410 state.overwrite = false;
1411 state.clear();
1412 }
1413 }
1414 fn clear_overwrite(state: &mut TextInputState) {
1415 state.overwrite = false;
1416 }
1417
1418 if self.lost_focus() {
1420 match self.on_focus_lost {
1421 TextFocusLost::None => {}
1422 TextFocusLost::Position0 => {
1423 self.move_to_line_start(false);
1424 }
1426 }
1427 }
1428 if self.gained_focus() {
1429 match self.on_focus_gained {
1430 TextFocusGained::None => {}
1431 TextFocusGained::Overwrite => {
1432 self.overwrite = true;
1433 }
1434 TextFocusGained::SelectAll => {
1435 self.select_all();
1436 }
1438 }
1439 }
1440
1441 let mut r = if self.is_focused() {
1442 match event {
1443 ct_event!(key press c)
1444 | ct_event!(key press SHIFT-c)
1445 | ct_event!(key press CONTROL_ALT-c) => {
1446 overwrite(self);
1447 tc(self.insert_char(*c))
1448 }
1449 ct_event!(keycode press Tab) => {
1450 tc(if !self.focus.gained() {
1452 clear_overwrite(self);
1453 self.insert_tab()
1454 } else {
1455 false
1456 })
1457 }
1458 ct_event!(keycode press Backspace) => {
1459 clear_overwrite(self);
1460 tc(self.delete_prev_char())
1461 }
1462 ct_event!(keycode press Delete) => {
1463 clear_overwrite(self);
1464 tc(self.delete_next_char())
1465 }
1466 ct_event!(keycode press CONTROL-Backspace)
1467 | ct_event!(keycode press ALT-Backspace) => {
1468 clear_overwrite(self);
1469 tc(self.delete_prev_word())
1470 }
1471 ct_event!(keycode press CONTROL-Delete) => {
1472 clear_overwrite(self);
1473 tc(self.delete_next_word())
1474 }
1475 ct_event!(key press CONTROL-'x') => {
1476 clear_overwrite(self);
1477 tc(self.cut_to_clip())
1478 }
1479 ct_event!(key press CONTROL-'v') => {
1480 overwrite(self);
1481 tc(self.paste_from_clip())
1482 }
1483 ct_event!(key press CONTROL-'d') => {
1484 clear_overwrite(self);
1485 tc(self.clear())
1486 }
1487 ct_event!(key press CONTROL-'z') => {
1488 clear_overwrite(self);
1489 tc(self.undo())
1490 }
1491 ct_event!(key press CONTROL_SHIFT-'Z') => {
1492 clear_overwrite(self);
1493 tc(self.redo())
1494 }
1495
1496 ct_event!(key release _)
1497 | ct_event!(key release SHIFT-_)
1498 | ct_event!(key release CONTROL_ALT-_)
1499 | ct_event!(keycode release Tab)
1500 | ct_event!(keycode release Backspace)
1501 | ct_event!(keycode release Delete)
1502 | ct_event!(keycode release CONTROL-Backspace)
1503 | ct_event!(keycode release ALT-Backspace)
1504 | ct_event!(keycode release CONTROL-Delete)
1505 | ct_event!(key release CONTROL-'x')
1506 | ct_event!(key release CONTROL-'v')
1507 | ct_event!(key release CONTROL-'d')
1508 | ct_event!(key release CONTROL-'y')
1509 | ct_event!(key release CONTROL-'z')
1510 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1511
1512 _ => TextOutcome::Continue,
1513 }
1514 } else {
1515 TextOutcome::Continue
1516 };
1517 if r == TextOutcome::Continue {
1518 r = self.handle(event, ReadOnly);
1519 }
1520 r
1521 }
1522}
1523
1524impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextInputState {
1525 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1526 fn clear_overwrite(state: &mut TextInputState) {
1527 state.overwrite = false;
1528 }
1529
1530 let mut r = if self.is_focused() {
1531 match event {
1532 ct_event!(keycode press Left) => {
1533 clear_overwrite(self);
1534 self.move_left(false).into()
1535 }
1536 ct_event!(keycode press Right) => {
1537 clear_overwrite(self);
1538 self.move_right(false).into()
1539 }
1540 ct_event!(keycode press CONTROL-Left) => {
1541 clear_overwrite(self);
1542 self.move_to_prev_word(false).into()
1543 }
1544 ct_event!(keycode press CONTROL-Right) => {
1545 clear_overwrite(self);
1546 self.move_to_next_word(false).into()
1547 }
1548 ct_event!(keycode press Home) => {
1549 clear_overwrite(self);
1550 self.move_to_line_start(false).into()
1551 }
1552 ct_event!(keycode press End) => {
1553 clear_overwrite(self);
1554 self.move_to_line_end(false).into()
1555 }
1556 ct_event!(keycode press SHIFT-Left) => {
1557 clear_overwrite(self);
1558 self.move_left(true).into()
1559 }
1560 ct_event!(keycode press SHIFT-Right) => {
1561 clear_overwrite(self);
1562 self.move_right(true).into()
1563 }
1564 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1565 clear_overwrite(self);
1566 self.move_to_prev_word(true).into()
1567 }
1568 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1569 clear_overwrite(self);
1570 self.move_to_next_word(true).into()
1571 }
1572 ct_event!(keycode press SHIFT-Home) => {
1573 clear_overwrite(self);
1574 self.move_to_line_start(true).into()
1575 }
1576 ct_event!(keycode press SHIFT-End) => {
1577 clear_overwrite(self);
1578 self.move_to_line_end(true).into()
1579 }
1580 ct_event!(keycode press ALT-Left) => {
1581 clear_overwrite(self);
1582 self.scroll_left(1).into()
1583 }
1584 ct_event!(keycode press ALT-Right) => {
1585 clear_overwrite(self);
1586 self.scroll_right(1).into()
1587 }
1588 ct_event!(key press CONTROL-'a') => {
1589 clear_overwrite(self);
1590 self.select_all().into()
1591 }
1592 ct_event!(key press CONTROL-'c') => {
1593 clear_overwrite(self);
1594 self.copy_to_clip().into()
1595 }
1596
1597 ct_event!(keycode release Left)
1598 | ct_event!(keycode release Right)
1599 | ct_event!(keycode release CONTROL-Left)
1600 | ct_event!(keycode release CONTROL-Right)
1601 | ct_event!(keycode release Home)
1602 | ct_event!(keycode release End)
1603 | ct_event!(keycode release SHIFT-Left)
1604 | ct_event!(keycode release SHIFT-Right)
1605 | ct_event!(keycode release CONTROL_SHIFT-Left)
1606 | ct_event!(keycode release CONTROL_SHIFT-Right)
1607 | ct_event!(keycode release SHIFT-Home)
1608 | ct_event!(keycode release SHIFT-End)
1609 | ct_event!(key release CONTROL-'a')
1610 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1611
1612 _ => TextOutcome::Continue,
1613 }
1614 } else {
1615 TextOutcome::Continue
1616 };
1617
1618 if r == TextOutcome::Continue {
1619 r = self.handle(event, MouseOnly);
1620 }
1621 r
1622 }
1623}
1624
1625impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextInputState {
1626 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1627 fn clear_overwrite(state: &mut TextInputState) {
1628 state.overwrite = false;
1629 }
1630
1631 match event {
1632 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1633 let c = (m.column as i16) - (self.inner.x as i16);
1634 clear_overwrite(self);
1635 self.set_screen_cursor(c, true).into()
1636 }
1637 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1638 let cx = m.column as i16 - self.inner.x as i16;
1639 clear_overwrite(self);
1640 self.set_screen_cursor_words(cx, true).into()
1641 }
1642 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1643 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1644 let start = self.word_start(tx);
1645 let end = self.word_end(tx);
1646 clear_overwrite(self);
1647 self.set_selection(start, end).into()
1648 }
1649 ct_event!(mouse down Left for column,row) => {
1650 if self.gained_focus() {
1651 TextOutcome::Unchanged
1654 } else if self.inner.contains((*column, *row).into()) {
1655 let c = (column - self.inner.x) as i16;
1656 clear_overwrite(self);
1657 self.set_screen_cursor(c, false).into()
1658 } else {
1659 TextOutcome::Continue
1660 }
1661 }
1662 ct_event!(mouse down CONTROL-Left for column,row) => {
1663 if self.inner.contains((*column, *row).into()) {
1664 let cx = (column - self.inner.x) as i16;
1665 clear_overwrite(self);
1666 self.set_screen_cursor(cx, true).into()
1667 } else {
1668 TextOutcome::Continue
1669 }
1670 }
1671 ct_event!(mouse down ALT-Left for column,row) => {
1672 if self.inner.contains((*column, *row).into()) {
1673 let cx = (column - self.inner.x) as i16;
1674 clear_overwrite(self);
1675 self.set_screen_cursor_words(cx, true).into()
1676 } else {
1677 TextOutcome::Continue
1678 }
1679 }
1680 _ => TextOutcome::Continue,
1681 }
1682 }
1683}
1684
1685pub fn handle_events(
1689 state: &mut TextInputState,
1690 focus: bool,
1691 event: &crossterm::event::Event,
1692) -> TextOutcome {
1693 state.focus.set(focus);
1694 state.handle(event, Regular)
1695}
1696
1697pub fn handle_readonly_events(
1701 state: &mut TextInputState,
1702 focus: bool,
1703 event: &crossterm::event::Event,
1704) -> TextOutcome {
1705 state.focus.set(focus);
1706 state.handle(event, ReadOnly)
1707}
1708
1709pub fn handle_mouse_events(
1711 state: &mut TextInputState,
1712 event: &crossterm::event::Event,
1713) -> TextOutcome {
1714 state.handle(event, MouseOnly)
1715}