1use crate::_private::NonExhaustive;
17use crate::clipboard::{global_clipboard, Clipboard};
18use crate::core::{TextCore, TextString};
19use crate::event::{ReadOnly, TextOutcome};
20use crate::glyph2::{Glyph2, TextWrap2};
21use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
22#[allow(deprecated)]
23use crate::Glyph;
24use crate::{
25 ipos_type, upos_type, Cursor, Grapheme, HasScreenCursor, TextError, TextFocusGained,
26 TextFocusLost, TextPosition, TextRange, TextStyle,
27};
28use crossterm::event::KeyModifiers;
29use rat_event::util::MouseFlags;
30use rat_event::{ct_event, HandleEvent, MouseOnly, Regular};
31use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
32use rat_reloc::{relocate_area, relocate_dark_offset, RelocatableState};
33use ratatui::buffer::Buffer;
34use ratatui::layout::{Rect, Size};
35use ratatui::prelude::BlockExt;
36use ratatui::style::{Style, Stylize};
37use ratatui::widgets::{Block, StatefulWidget, Widget};
38use std::borrow::Cow;
39use std::cmp::min;
40use std::ops::Range;
41
42#[derive(Debug, Default, Clone)]
48pub struct TextInput<'a> {
49 block: Option<Block<'a>>,
50 style: Style,
51 focus_style: Option<Style>,
52 select_style: Option<Style>,
53 invalid_style: Option<Style>,
54 on_focus_gained: TextFocusGained,
55 on_focus_lost: TextFocusLost,
56 passwd: bool,
57 text_style: Vec<Style>,
58}
59
60#[derive(Debug)]
62pub struct TextInputState {
63 pub area: Rect,
66 pub inner: Rect,
69 pub rendered: Size,
73
74 pub offset: upos_type,
77 pub dark_offset: (u16, u16),
80 pub scroll_to_cursor: bool,
82
83 pub value: TextCore<TextString>,
85 pub invalid: bool,
88 pub passwd: bool,
91 pub overwrite: bool,
94 pub on_focus_gained: TextFocusGained,
97 pub on_focus_lost: TextFocusLost,
100
101 pub focus: FocusFlag,
104
105 pub mouse: MouseFlags,
108
109 pub non_exhaustive: NonExhaustive,
111}
112
113impl<'a> TextInput<'a> {
114 pub fn new() -> Self {
116 Self::default()
117 }
118
119 #[inline]
121 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
122 if let Some(styles) = styles {
123 self.styles(styles)
124 } else {
125 self
126 }
127 }
128
129 #[inline]
131 pub fn styles(mut self, styles: TextStyle) -> Self {
132 self.style = styles.style;
133 if styles.focus.is_some() {
134 self.focus_style = styles.focus;
135 }
136 if styles.select.is_some() {
137 self.select_style = styles.select;
138 }
139 if styles.invalid.is_some() {
140 self.invalid_style = styles.invalid;
141 }
142 if let Some(of) = styles.on_focus_gained {
143 self.on_focus_gained = of;
144 }
145 if let Some(of) = styles.on_focus_lost {
146 self.on_focus_lost = of;
147 }
148 if let Some(border_style) = styles.border_style {
149 self.block = self.block.map(|v| v.border_style(border_style));
150 }
151 self.block = self.block.map(|v| v.style(self.style));
152 if styles.block.is_some() {
153 self.block = styles.block;
154 }
155 self.block = self.block.map(|v| v.style(self.style));
156 self
157 }
158
159 #[inline]
161 pub fn style(mut self, style: impl Into<Style>) -> Self {
162 self.style = style.into();
163 self
164 }
165
166 #[inline]
168 pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
169 self.focus_style = Some(style.into());
170 self
171 }
172
173 #[inline]
175 pub fn select_style(mut self, style: impl Into<Style>) -> Self {
176 self.select_style = Some(style.into());
177 self
178 }
179
180 #[inline]
183 pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
184 self.invalid_style = Some(style.into());
185 self
186 }
187
188 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
193 self.text_style = styles.into_iter().collect();
194 self
195 }
196
197 #[inline]
199 pub fn block(mut self, block: Block<'a>) -> Self {
200 self.block = Some(block);
201 self
202 }
203
204 #[inline]
206 pub fn passwd(mut self) -> Self {
207 self.passwd = true;
208 self
209 }
210
211 #[inline]
213 pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
214 self.on_focus_gained = of;
215 self
216 }
217
218 #[inline]
220 pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
221 self.on_focus_lost = of;
222 self
223 }
224}
225
226impl<'a> StatefulWidget for &TextInput<'a> {
227 type State = TextInputState;
228
229 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
230 render_ref(self, area, buf, state);
231 }
232}
233
234impl StatefulWidget for TextInput<'_> {
235 type State = TextInputState;
236
237 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
238 render_ref(&self, area, buf, state);
239 }
240}
241
242fn render_ref(widget: &TextInput<'_>, area: Rect, buf: &mut Buffer, state: &mut TextInputState) {
243 state.area = area;
244 state.inner = widget.block.inner_if_some(area);
245 state.rendered = state.inner.as_size();
246 state.passwd = widget.passwd;
247 state.on_focus_gained = widget.on_focus_gained;
248 state.on_focus_lost = widget.on_focus_lost;
249
250 if state.scroll_to_cursor {
251 let c = state.cursor();
252 let o = state.offset();
253
254 if state.rendered.width > 0 {
255 let mut no = if c < o {
256 c
257 } else if c >= o + state.rendered.width as upos_type {
258 c.saturating_sub(state.rendered.width as upos_type)
259 } else {
260 o
261 };
262 if c == no + state.rendered.width as upos_type {
265 no = no.saturating_add(1);
266 }
267 state.set_offset(no);
268 } else {
269 }
271 }
272
273 let style = widget.style;
274 let focus_style = if let Some(focus_style) = widget.focus_style {
275 focus_style
276 } else {
277 style
278 };
279 let select_style = if let Some(select_style) = widget.select_style {
280 select_style
281 } else {
282 Style::default().black().on_yellow()
283 };
284 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
285 invalid_style
286 } else {
287 Style::default().red()
288 };
289
290 let (style, select_style) = if state.focus.get() {
291 if state.invalid {
292 (
293 style.patch(focus_style).patch(invalid_style),
294 style
295 .patch(focus_style)
296 .patch(select_style)
297 .patch(invalid_style),
298 )
299 } else {
300 (
301 style.patch(focus_style),
302 style.patch(focus_style).patch(select_style),
303 )
304 }
305 } else {
306 if state.invalid {
307 (style.patch(invalid_style), style.patch(invalid_style))
308 } else {
309 (style, style)
310 }
311 };
312
313 if let Some(block) = &widget.block {
315 block.render(area, buf);
316 }
317 buf.set_style(state.inner, style);
318
319 if state.inner.width == 0 || state.inner.height == 0 {
320 return;
322 }
323
324 let ox = state.offset() as u16;
325 let show_range = {
327 let start = min(ox as upos_type, state.len());
328 let end = min(start + state.inner.width as upos_type, state.len());
329 state.bytes_at_range(start..end)
330 };
331 let selection = state.selection();
332 let mut styles = Vec::new();
333
334 if widget.passwd {
335 for g in state.glyphs2() {
337 if g.screen_width() > 0 {
338 let mut style = style;
339 state
340 .value
341 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
342 for style_nr in &styles {
343 if let Some(s) = widget.text_style.get(*style_nr) {
344 style = style.patch(*s);
345 }
346 }
347 if selection.contains(&g.pos().x) {
349 style = style.patch(select_style);
350 };
351
352 let screen_pos = g.screen_pos();
354
355 if let Some(cell) =
357 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
358 {
359 cell.set_symbol("*");
360 cell.set_style(style);
361 }
362 for d in 1..g.screen_width() {
364 if let Some(cell) = buf.cell_mut((
365 state.inner.x + screen_pos.0 + d,
366 state.inner.y + screen_pos.1,
367 )) {
368 cell.reset();
369 cell.set_style(style);
370 }
371 }
372 }
373 }
374 } else {
375 for g in state.glyphs2() {
376 if g.screen_width() > 0 {
377 let mut style = style;
378 state
379 .value
380 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
381 for style_nr in &styles {
382 if let Some(s) = widget.text_style.get(*style_nr) {
383 style = style.patch(*s);
384 }
385 }
386 if selection.contains(&g.pos().x) {
388 style = style.patch(select_style);
389 };
390
391 let screen_pos = g.screen_pos();
393
394 if let Some(cell) =
396 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
397 {
398 cell.set_symbol(g.glyph());
399 cell.set_style(style);
400 }
401 for d in 1..g.screen_width() {
403 if let Some(cell) = buf.cell_mut((
404 state.inner.x + screen_pos.0 + d,
405 state.inner.y + screen_pos.1,
406 )) {
407 cell.reset();
408 cell.set_style(style);
409 }
410 }
411 }
412 }
413 }
414}
415
416impl Clone for TextInputState {
417 fn clone(&self) -> Self {
418 Self {
419 area: self.area,
420 inner: self.inner,
421 rendered: self.rendered,
422 offset: self.offset,
423 dark_offset: self.dark_offset,
424 scroll_to_cursor: self.scroll_to_cursor,
425 value: self.value.clone(),
426 invalid: self.invalid,
427 passwd: Default::default(),
428 overwrite: Default::default(),
429 on_focus_gained: Default::default(),
430 on_focus_lost: Default::default(),
431 focus: FocusFlag::named(self.focus.name()),
432 mouse: Default::default(),
433 non_exhaustive: NonExhaustive,
434 }
435 }
436}
437
438impl Default for TextInputState {
439 fn default() -> Self {
440 let mut value = TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard()));
441 value.set_glyph_line_break(false);
442
443 Self {
444 area: Default::default(),
445 inner: Default::default(),
446 rendered: Default::default(),
447 offset: Default::default(),
448 dark_offset: Default::default(),
449 scroll_to_cursor: Default::default(),
450 value,
451 invalid: Default::default(),
452 passwd: Default::default(),
453 overwrite: Default::default(),
454 on_focus_gained: Default::default(),
455 on_focus_lost: Default::default(),
456 focus: Default::default(),
457 mouse: Default::default(),
458 non_exhaustive: NonExhaustive,
459 }
460 }
461}
462
463impl HasFocus for TextInputState {
464 fn build(&self, builder: &mut FocusBuilder) {
465 builder.leaf_widget(self);
466 }
467
468 fn focus(&self) -> FocusFlag {
469 self.focus.clone()
470 }
471
472 fn area(&self) -> Rect {
473 self.area
474 }
475}
476
477impl TextInputState {
478 pub fn new() -> Self {
479 Self::default()
480 }
481
482 pub fn named(name: &str) -> Self {
483 Self {
484 focus: FocusFlag::named(name),
485 ..TextInputState::default()
486 }
487 }
488
489 #[inline]
491 pub fn set_invalid(&mut self, invalid: bool) {
492 self.invalid = invalid;
493 }
494
495 #[inline]
497 #[deprecated(since = "1.0.5", note = "wrong convention")]
498 pub fn get_invalid(&self) -> bool {
499 self.invalid
500 }
501
502 #[inline]
504 pub fn invalid(&self) -> bool {
505 self.invalid
506 }
507
508 #[inline]
512 pub fn set_overwrite(&mut self, overwrite: bool) {
513 self.overwrite = overwrite;
514 }
515
516 #[inline]
518 pub fn overwrite(&self) -> bool {
519 self.overwrite
520 }
521
522 #[inline]
524 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
525 self.value.set_glyph_ctrl(show_ctrl);
526 }
527
528 pub fn show_ctrl(&self) -> bool {
530 self.value.glyph_ctrl()
531 }
532}
533
534impl TextInputState {
535 #[inline]
538 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
539 match clip {
540 None => self.value.set_clipboard(None),
541 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
542 }
543 }
544
545 #[inline]
548 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
549 self.value.clipboard()
550 }
551
552 #[inline]
554 pub fn copy_to_clip(&mut self) -> bool {
555 let Some(clip) = self.value.clipboard() else {
556 return false;
557 };
558 if self.passwd {
559 return false;
560 }
561
562 _ = clip.set_string(self.selected_text().as_ref());
563 false
564 }
565
566 #[inline]
568 pub fn cut_to_clip(&mut self) -> bool {
569 let Some(clip) = self.value.clipboard() else {
570 return false;
571 };
572 if self.passwd {
573 return false;
574 }
575
576 match clip.set_string(self.selected_text().as_ref()) {
577 Ok(_) => self.delete_range(self.selection()),
578 Err(_) => false,
579 }
580 }
581
582 #[inline]
584 pub fn paste_from_clip(&mut self) -> bool {
585 let Some(clip) = self.value.clipboard() else {
586 return false;
587 };
588
589 if let Ok(text) = clip.get_string() {
590 self.insert_str(text)
591 } else {
592 false
593 }
594 }
595}
596
597impl TextInputState {
598 #[inline]
600 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
601 match undo {
602 None => self.value.set_undo_buffer(None),
603 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
604 }
605 }
606
607 #[inline]
609 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
610 self.value.undo_buffer()
611 }
612
613 #[inline]
615 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
616 self.value.undo_buffer_mut()
617 }
618
619 #[inline]
621 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
622 self.value.recent_replay_log()
623 }
624
625 #[inline]
627 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
628 self.value.replay_log(replay)
629 }
630
631 #[inline]
633 pub fn undo(&mut self) -> bool {
634 self.value.undo()
635 }
636
637 #[inline]
639 pub fn redo(&mut self) -> bool {
640 self.value.redo()
641 }
642}
643
644impl TextInputState {
645 #[inline]
656 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
657 self.value.set_styles(styles);
658 }
659
660 #[inline]
665 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
666 self.value.add_style(range, style);
667 }
668
669 #[inline]
672 pub fn add_range_style(
673 &mut self,
674 range: Range<upos_type>,
675 style: usize,
676 ) -> Result<(), TextError> {
677 let r = self
678 .value
679 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
680 self.value.add_style(r, style);
681 Ok(())
682 }
683
684 #[inline]
686 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
687 self.value.remove_style(range, style);
688 }
689
690 #[inline]
692 pub fn remove_range_style(
693 &mut self,
694 range: Range<upos_type>,
695 style: usize,
696 ) -> Result<(), TextError> {
697 let r = self
698 .value
699 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
700 self.value.remove_style(r, style);
701 Ok(())
702 }
703
704 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
706 self.value.styles_in(range, buf)
707 }
708
709 #[inline]
711 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
712 self.value.styles_at(byte_pos, buf)
713 }
714
715 #[inline]
718 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
719 self.value.style_match(byte_pos, style)
720 }
721
722 #[inline]
724 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
725 self.value.styles()
726 }
727}
728
729impl TextInputState {
730 #[inline]
732 pub fn offset(&self) -> upos_type {
733 self.offset
734 }
735
736 #[inline]
738 pub fn set_offset(&mut self, offset: upos_type) {
739 self.scroll_to_cursor = false;
740 self.offset = offset;
741 }
742
743 #[inline]
745 pub fn cursor(&self) -> upos_type {
746 self.value.cursor().x
747 }
748
749 #[inline]
751 pub fn anchor(&self) -> upos_type {
752 self.value.anchor().x
753 }
754
755 #[inline]
758 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
759 self.scroll_cursor_to_visible();
760 self.value
761 .set_cursor(TextPosition::new(cursor, 0), extend_selection)
762 }
763
764 #[inline]
766 pub fn has_selection(&self) -> bool {
767 self.value.has_selection()
768 }
769
770 #[inline]
772 pub fn selection(&self) -> Range<upos_type> {
773 let mut v = self.value.selection();
774 if v.start == TextPosition::new(0, 1) {
775 v.start = TextPosition::new(self.line_width(), 0);
776 }
777 if v.end == TextPosition::new(0, 1) {
778 v.end = TextPosition::new(self.line_width(), 0);
779 }
780 v.start.x..v.end.x
781 }
782
783 #[inline]
786 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
787 self.scroll_cursor_to_visible();
788 self.value
789 .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
790 }
791
792 #[inline]
795 pub fn select_all(&mut self) -> bool {
796 self.scroll_cursor_to_visible();
797 self.value.select_all()
798 }
799
800 #[inline]
802 pub fn selected_text(&self) -> &str {
803 match self.str_slice(self.selection()) {
804 Cow::Borrowed(v) => v,
805 Cow::Owned(_) => {
806 unreachable!()
807 }
808 }
809 }
810}
811
812impl TextInputState {
813 #[inline]
815 pub fn is_empty(&self) -> bool {
816 self.value.is_empty()
817 }
818
819 #[inline]
821 pub fn value<T: for<'a> From<&'a str>>(&self) -> T {
822 self.value.text().as_str().into()
823 }
824
825 #[inline]
827 pub fn text(&self) -> &str {
828 self.value.text().as_str()
829 }
830
831 #[inline]
833 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
834 self.value.str_slice_byte(range).expect("valid_range")
835 }
836
837 #[inline]
839 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
840 self.value.str_slice_byte(range)
841 }
842
843 #[inline]
845 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
846 self.value
847 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
848 .expect("valid_range")
849 }
850
851 #[inline]
853 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
854 self.value
855 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
856 }
857
858 #[inline]
860 pub fn len(&self) -> upos_type {
861 self.value.line_width(0).expect("valid_row")
862 }
863
864 #[inline]
866 pub fn line_width(&self) -> upos_type {
867 self.value.line_width(0).expect("valid_row")
868 }
869
870 #[inline]
873 #[allow(deprecated)]
874 #[deprecated(since = "1.1.0", note = "discontinued api")]
875 pub fn glyphs(&self, screen_offset: u16, screen_width: u16) -> impl Iterator<Item = Glyph<'_>> {
876 self.value
877 .glyphs(0..1, screen_offset, screen_width)
878 .expect("valid_rows")
879 }
880
881 #[inline]
883 pub fn text_graphemes(&self, pos: upos_type) -> impl Cursor<Item = Grapheme<'_>> {
884 self.value
885 .text_graphemes(TextPosition::new(pos, 0))
886 .expect("valid_pos")
887 }
888
889 #[inline]
891 pub fn try_text_graphemes(
892 &self,
893 pos: upos_type,
894 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
895 self.value.text_graphemes(TextPosition::new(pos, 0))
896 }
897
898 #[inline]
900 pub fn graphemes(
901 &self,
902 range: Range<upos_type>,
903 pos: upos_type,
904 ) -> impl Cursor<Item = Grapheme<'_>> {
905 self.value
906 .graphemes(
907 TextRange::new((range.start, 0), (range.end, 0)),
908 TextPosition::new(pos, 0),
909 )
910 .expect("valid_args")
911 }
912
913 #[inline]
915 pub fn try_graphemes(
916 &self,
917 range: Range<upos_type>,
918 pos: upos_type,
919 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
920 self.value.graphemes(
921 TextRange::new((range.start, 0), (range.end, 0)),
922 TextPosition::new(pos, 0),
923 )
924 }
925
926 #[inline]
929 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
930 self.value
931 .byte_at(TextPosition::new(pos, 0))
932 .expect("valid_pos")
933 }
934
935 #[inline]
938 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
939 self.value.byte_at(TextPosition::new(pos, 0))
940 }
941
942 #[inline]
944 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
945 self.value
946 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
947 .expect("valid_range")
948 }
949
950 #[inline]
952 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
953 self.value
954 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
955 }
956
957 #[inline]
960 pub fn byte_pos(&self, byte: usize) -> upos_type {
961 self.value.byte_pos(byte).map(|v| v.x).expect("valid_pos")
962 }
963
964 #[inline]
967 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
968 self.value.byte_pos(byte).map(|v| v.x)
969 }
970
971 #[inline]
973 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
974 self.value
975 .byte_range(bytes)
976 .map(|v| v.start.x..v.end.x)
977 .expect("valid_range")
978 }
979
980 #[inline]
982 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
983 self.value.byte_range(bytes).map(|v| v.start.x..v.end.x)
984 }
985}
986
987impl TextInputState {
988 #[inline]
990 pub fn clear(&mut self) -> bool {
991 if self.is_empty() {
992 false
993 } else {
994 self.offset = 0;
995 self.value.clear();
996 true
997 }
998 }
999
1000 #[inline]
1004 pub fn set_value<S: Into<String>>(&mut self, s: S) {
1005 self.offset = 0;
1006 self.value.set_text(TextString::new_string(s.into()));
1007 }
1008
1009 #[inline]
1013 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1014 self.offset = 0;
1015 self.value.set_text(TextString::new_string(s.into()));
1016 }
1017
1018 #[inline]
1020 pub fn insert_char(&mut self, c: char) -> bool {
1021 if self.has_selection() {
1022 self.value
1023 .remove_str_range(self.value.selection())
1024 .expect("valid_selection");
1025 }
1026 if c == '\n' {
1027 return false;
1028 } else if c == '\t' {
1029 self.value
1030 .insert_tab(self.value.cursor())
1031 .expect("valid_cursor");
1032 } else {
1033 self.value
1034 .insert_char(self.value.cursor(), c)
1035 .expect("valid_cursor");
1036 }
1037 self.scroll_cursor_to_visible();
1038 true
1039 }
1040
1041 pub fn insert_tab(&mut self) -> bool {
1044 if self.has_selection() {
1045 self.value
1046 .remove_str_range(self.value.selection())
1047 .expect("valid_selection");
1048 }
1049 self.value
1050 .insert_tab(self.value.cursor())
1051 .expect("valid_cursor");
1052 self.scroll_cursor_to_visible();
1053 true
1054 }
1055
1056 #[inline]
1058 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1059 let t = t.as_ref();
1060 if self.has_selection() {
1061 self.value
1062 .remove_str_range(self.value.selection())
1063 .expect("valid_selection");
1064 }
1065 self.value
1066 .insert_str(self.value.cursor(), t)
1067 .expect("valid_cursor");
1068 self.scroll_cursor_to_visible();
1069 true
1070 }
1071
1072 #[inline]
1074 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1075 self.try_delete_range(range).expect("valid_range")
1076 }
1077
1078 #[inline]
1080 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1081 if !range.is_empty() {
1082 self.value
1083 .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1084 self.scroll_cursor_to_visible();
1085 Ok(true)
1086 } else {
1087 Ok(false)
1088 }
1089 }
1090}
1091
1092impl TextInputState {
1093 #[inline]
1095 pub fn delete_next_char(&mut self) -> bool {
1096 if self.has_selection() {
1097 self.delete_range(self.selection())
1098 } else {
1099 let r = self
1100 .value
1101 .remove_next_char(self.value.cursor())
1102 .expect("valid_cursor");
1103 self.scroll_cursor_to_visible();
1104 r
1105 }
1106 }
1107
1108 #[inline]
1110 pub fn delete_prev_char(&mut self) -> bool {
1111 if self.value.has_selection() {
1112 self.delete_range(self.selection())
1113 } else {
1114 let r = self
1115 .value
1116 .remove_prev_char(self.value.cursor())
1117 .expect("valid_cursor");
1118 self.scroll_cursor_to_visible();
1119 r
1120 }
1121 }
1122
1123 pub fn next_word_start(&self, pos: upos_type) -> upos_type {
1125 self.try_next_word_start(pos).expect("valid_pos")
1126 }
1127
1128 pub fn try_next_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1130 self.value
1131 .next_word_start(TextPosition::new(pos, 0))
1132 .map(|v| v.x)
1133 }
1134
1135 pub fn next_word_end(&self, pos: upos_type) -> upos_type {
1138 self.try_next_word_end(pos).expect("valid_pos")
1139 }
1140
1141 pub fn try_next_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1144 self.value
1145 .next_word_end(TextPosition::new(pos, 0))
1146 .map(|v| v.x)
1147 }
1148
1149 pub fn prev_word_start(&self, pos: upos_type) -> upos_type {
1153 self.try_prev_word_start(pos).expect("valid_pos")
1154 }
1155
1156 pub fn try_prev_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1160 self.value
1161 .prev_word_start(TextPosition::new(pos, 0))
1162 .map(|v| v.x)
1163 }
1164
1165 pub fn prev_word_end(&self, pos: upos_type) -> upos_type {
1169 self.try_prev_word_end(pos).expect("valid_pos")
1170 }
1171
1172 pub fn try_prev_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1176 self.value
1177 .prev_word_end(TextPosition::new(pos, 0))
1178 .map(|v| v.x)
1179 }
1180
1181 pub fn is_word_boundary(&self, pos: upos_type) -> bool {
1183 self.try_is_word_boundary(pos).expect("valid_pos")
1184 }
1185
1186 pub fn try_is_word_boundary(&self, pos: upos_type) -> Result<bool, TextError> {
1188 self.value.is_word_boundary(TextPosition::new(pos, 0))
1189 }
1190
1191 pub fn word_start(&self, pos: upos_type) -> upos_type {
1193 self.try_word_start(pos).expect("valid_pos")
1194 }
1195
1196 pub fn try_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1198 self.value
1199 .word_start(TextPosition::new(pos, 0))
1200 .map(|v| v.x)
1201 }
1202
1203 pub fn word_end(&self, pos: upos_type) -> upos_type {
1205 self.try_word_end(pos).expect("valid_pos")
1206 }
1207
1208 pub fn try_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1210 self.value.word_end(TextPosition::new(pos, 0)).map(|v| v.x)
1211 }
1212
1213 #[inline]
1215 pub fn delete_next_word(&mut self) -> bool {
1216 if self.has_selection() {
1217 self.delete_range(self.selection())
1218 } else {
1219 let cursor = self.cursor();
1220
1221 let start = self.next_word_start(cursor);
1222 if start != cursor {
1223 self.delete_range(cursor..start)
1224 } else {
1225 let end = self.next_word_end(cursor);
1226 self.delete_range(cursor..end)
1227 }
1228 }
1229 }
1230
1231 #[inline]
1233 pub fn delete_prev_word(&mut self) -> bool {
1234 if self.has_selection() {
1235 self.delete_range(self.selection())
1236 } else {
1237 let cursor = self.cursor();
1238
1239 let end = self.prev_word_end(cursor);
1240 if end != cursor {
1241 self.delete_range(end..cursor)
1242 } else {
1243 let start = self.prev_word_start(cursor);
1244 self.delete_range(start..cursor)
1245 }
1246 }
1247 }
1248
1249 #[inline]
1251 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1252 let c = min(self.cursor() + 1, self.len());
1253 self.set_cursor(c, extend_selection)
1254 }
1255
1256 #[inline]
1258 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1259 let c = self.cursor().saturating_sub(1);
1260 self.set_cursor(c, extend_selection)
1261 }
1262
1263 #[inline]
1265 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1266 self.set_cursor(0, extend_selection)
1267 }
1268
1269 #[inline]
1271 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1272 self.set_cursor(self.len(), extend_selection)
1273 }
1274
1275 #[inline]
1276 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1277 let cursor = self.cursor();
1278 let end = self.next_word_end(cursor);
1279 self.set_cursor(end, extend_selection)
1280 }
1281
1282 #[inline]
1283 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1284 let cursor = self.cursor();
1285 let start = self.prev_word_start(cursor);
1286 self.set_cursor(start, extend_selection)
1287 }
1288}
1289
1290impl HasScreenCursor for TextInputState {
1291 #[inline]
1293 fn screen_cursor(&self) -> Option<(u16, u16)> {
1294 if self.is_focused() {
1295 if self.has_selection() {
1296 None
1297 } else {
1298 let cx = self.cursor();
1299 let ox = self.offset();
1300
1301 if cx < ox {
1302 None
1303 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1304 None
1305 } else {
1306 self.col_to_screen(cx)
1307 .map(|sc| (self.inner.x + sc, self.inner.y))
1308 }
1309 }
1310 } else {
1311 None
1312 }
1313 }
1314}
1315
1316impl RelocatableState for TextInputState {
1317 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1318 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1320 self.area = relocate_area(self.area, shift, clip);
1321 self.inner = relocate_area(self.inner, shift, clip);
1322 }
1323}
1324
1325impl TextInputState {
1326 fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1327 let (text_wrap, left_margin, right_margin, word_margin) = (
1328 TextWrap2::Shift,
1329 self.offset() as upos_type,
1330 self.offset() as upos_type + self.rendered.width as upos_type,
1331 self.offset() as upos_type + self.rendered.width as upos_type,
1332 );
1333 self.value
1334 .glyphs2(
1335 self.rendered,
1336 0,
1337 0..1,
1338 text_wrap,
1339 false,
1340 left_margin,
1341 right_margin,
1342 word_margin,
1343 )
1344 .expect("valid-row")
1345 }
1346
1347 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1350 let ox = self.offset();
1351
1352 let scx = scx + self.dark_offset.0 as i16;
1353
1354 if scx < 0 {
1355 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1356 } else if scx as u16 >= self.rendered.width {
1357 min(ox + scx as upos_type, self.len())
1358 } else {
1359 let scx = scx as u16;
1360
1361 let line = self.glyphs2();
1362
1363 let mut col = ox;
1364 for g in line {
1365 if g.contains_screen_x(scx) {
1366 break;
1367 }
1368 col = g.pos().x + 1;
1369 }
1370 col
1371 }
1372 }
1373
1374 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1377 let ox = self.offset();
1378
1379 if pos < ox {
1380 return None;
1381 }
1382
1383 let line = self.glyphs2();
1384 let mut screen_x = 0;
1385 for g in line {
1386 if g.pos().x == pos {
1387 break;
1388 }
1389 screen_x = g.screen_pos().0 + g.screen_width();
1390 }
1391
1392 if screen_x >= self.dark_offset.0 {
1393 Some(screen_x - self.dark_offset.0)
1394 } else {
1395 None
1396 }
1397 }
1398
1399 #[inline]
1403 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1404 let scx = cursor;
1405
1406 let cx = self.screen_to_col(scx);
1407
1408 self.set_cursor(cx, extend_selection)
1409 }
1410
1411 pub fn set_screen_cursor_words(&mut self, screen_cursor: i16, extend_selection: bool) -> bool {
1418 let anchor = self.anchor();
1419
1420 let cx = self.screen_to_col(screen_cursor);
1421 let cursor = cx;
1422
1423 let cursor = if cursor < anchor {
1424 self.word_start(cursor)
1425 } else {
1426 self.word_end(cursor)
1427 };
1428
1429 if !self.is_word_boundary(anchor) {
1431 if cursor < anchor {
1432 self.set_cursor(self.word_end(anchor), false);
1433 } else {
1434 self.set_cursor(self.word_start(anchor), false);
1435 }
1436 }
1437
1438 self.set_cursor(cursor, extend_selection)
1439 }
1440
1441 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1443 self.set_offset(self.offset.saturating_sub(delta));
1444 true
1445 }
1446
1447 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1449 self.set_offset(self.offset + delta);
1450 true
1451 }
1452
1453 pub fn scroll_cursor_to_visible(&mut self) {
1455 self.scroll_to_cursor = true;
1456 }
1457}
1458
1459impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextInputState {
1460 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1461 fn tc(r: bool) -> TextOutcome {
1463 if r {
1464 TextOutcome::TextChanged
1465 } else {
1466 TextOutcome::Unchanged
1467 }
1468 }
1469 fn overwrite(state: &mut TextInputState) {
1470 if state.overwrite {
1471 state.overwrite = false;
1472 state.clear();
1473 }
1474 }
1475 fn clear_overwrite(state: &mut TextInputState) {
1476 state.overwrite = false;
1477 }
1478
1479 if self.lost_focus() {
1481 match self.on_focus_lost {
1482 TextFocusLost::None => {}
1483 TextFocusLost::Position0 => {
1484 self.move_to_line_start(false);
1485 }
1487 }
1488 }
1489 if self.gained_focus() {
1490 match self.on_focus_gained {
1491 TextFocusGained::None => {}
1492 TextFocusGained::Overwrite => {
1493 self.overwrite = true;
1494 }
1495 TextFocusGained::SelectAll => {
1496 self.select_all();
1497 }
1499 }
1500 }
1501
1502 let mut r = if self.is_focused() {
1503 match event {
1504 ct_event!(key press c)
1505 | ct_event!(key press SHIFT-c)
1506 | ct_event!(key press CONTROL_ALT-c) => {
1507 overwrite(self);
1508 tc(self.insert_char(*c))
1509 }
1510 ct_event!(keycode press Tab) => {
1511 tc(if !self.focus.gained() {
1513 clear_overwrite(self);
1514 self.insert_tab()
1515 } else {
1516 false
1517 })
1518 }
1519 ct_event!(keycode press Backspace) => {
1520 clear_overwrite(self);
1521 tc(self.delete_prev_char())
1522 }
1523 ct_event!(keycode press Delete) => {
1524 clear_overwrite(self);
1525 tc(self.delete_next_char())
1526 }
1527 ct_event!(keycode press CONTROL-Backspace)
1528 | ct_event!(keycode press ALT-Backspace) => {
1529 clear_overwrite(self);
1530 tc(self.delete_prev_word())
1531 }
1532 ct_event!(keycode press CONTROL-Delete) => {
1533 clear_overwrite(self);
1534 tc(self.delete_next_word())
1535 }
1536 ct_event!(key press CONTROL-'x') => {
1537 clear_overwrite(self);
1538 tc(self.cut_to_clip())
1539 }
1540 ct_event!(key press CONTROL-'v') => {
1541 overwrite(self);
1542 tc(self.paste_from_clip())
1543 }
1544 ct_event!(key press CONTROL-'d') => {
1545 clear_overwrite(self);
1546 tc(self.clear())
1547 }
1548 ct_event!(key press CONTROL-'z') => {
1549 clear_overwrite(self);
1550 tc(self.undo())
1551 }
1552 ct_event!(key press CONTROL_SHIFT-'Z') => {
1553 clear_overwrite(self);
1554 tc(self.redo())
1555 }
1556
1557 ct_event!(key release _)
1558 | ct_event!(key release SHIFT-_)
1559 | ct_event!(key release CONTROL_ALT-_)
1560 | ct_event!(keycode release Tab)
1561 | ct_event!(keycode release Backspace)
1562 | ct_event!(keycode release Delete)
1563 | ct_event!(keycode release CONTROL-Backspace)
1564 | ct_event!(keycode release ALT-Backspace)
1565 | ct_event!(keycode release CONTROL-Delete)
1566 | ct_event!(key release CONTROL-'x')
1567 | ct_event!(key release CONTROL-'v')
1568 | ct_event!(key release CONTROL-'d')
1569 | ct_event!(key release CONTROL-'y')
1570 | ct_event!(key release CONTROL-'z')
1571 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1572
1573 _ => TextOutcome::Continue,
1574 }
1575 } else {
1576 TextOutcome::Continue
1577 };
1578 if r == TextOutcome::Continue {
1579 r = self.handle(event, ReadOnly);
1580 }
1581 r
1582 }
1583}
1584
1585impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextInputState {
1586 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1587 fn clear_overwrite(state: &mut TextInputState) {
1588 state.overwrite = false;
1589 }
1590
1591 let mut r = if self.is_focused() {
1592 match event {
1593 ct_event!(keycode press Left) => {
1594 clear_overwrite(self);
1595 self.move_left(false).into()
1596 }
1597 ct_event!(keycode press Right) => {
1598 clear_overwrite(self);
1599 self.move_right(false).into()
1600 }
1601 ct_event!(keycode press CONTROL-Left) => {
1602 clear_overwrite(self);
1603 self.move_to_prev_word(false).into()
1604 }
1605 ct_event!(keycode press CONTROL-Right) => {
1606 clear_overwrite(self);
1607 self.move_to_next_word(false).into()
1608 }
1609 ct_event!(keycode press Home) => {
1610 clear_overwrite(self);
1611 self.move_to_line_start(false).into()
1612 }
1613 ct_event!(keycode press End) => {
1614 clear_overwrite(self);
1615 self.move_to_line_end(false).into()
1616 }
1617 ct_event!(keycode press SHIFT-Left) => {
1618 clear_overwrite(self);
1619 self.move_left(true).into()
1620 }
1621 ct_event!(keycode press SHIFT-Right) => {
1622 clear_overwrite(self);
1623 self.move_right(true).into()
1624 }
1625 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1626 clear_overwrite(self);
1627 self.move_to_prev_word(true).into()
1628 }
1629 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1630 clear_overwrite(self);
1631 self.move_to_next_word(true).into()
1632 }
1633 ct_event!(keycode press SHIFT-Home) => {
1634 clear_overwrite(self);
1635 self.move_to_line_start(true).into()
1636 }
1637 ct_event!(keycode press SHIFT-End) => {
1638 clear_overwrite(self);
1639 self.move_to_line_end(true).into()
1640 }
1641 ct_event!(keycode press ALT-Left) => {
1642 clear_overwrite(self);
1643 self.scroll_left(1).into()
1644 }
1645 ct_event!(keycode press ALT-Right) => {
1646 clear_overwrite(self);
1647 self.scroll_right(1).into()
1648 }
1649 ct_event!(key press CONTROL-'a') => {
1650 clear_overwrite(self);
1651 self.select_all().into()
1652 }
1653 ct_event!(key press CONTROL-'c') => {
1654 clear_overwrite(self);
1655 self.copy_to_clip().into()
1656 }
1657
1658 ct_event!(keycode release Left)
1659 | ct_event!(keycode release Right)
1660 | ct_event!(keycode release CONTROL-Left)
1661 | ct_event!(keycode release CONTROL-Right)
1662 | ct_event!(keycode release Home)
1663 | ct_event!(keycode release End)
1664 | ct_event!(keycode release SHIFT-Left)
1665 | ct_event!(keycode release SHIFT-Right)
1666 | ct_event!(keycode release CONTROL_SHIFT-Left)
1667 | ct_event!(keycode release CONTROL_SHIFT-Right)
1668 | ct_event!(keycode release SHIFT-Home)
1669 | ct_event!(keycode release SHIFT-End)
1670 | ct_event!(key release CONTROL-'a')
1671 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1672
1673 _ => TextOutcome::Continue,
1674 }
1675 } else {
1676 TextOutcome::Continue
1677 };
1678
1679 if r == TextOutcome::Continue {
1680 r = self.handle(event, MouseOnly);
1681 }
1682 r
1683 }
1684}
1685
1686impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextInputState {
1687 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1688 fn clear_overwrite(state: &mut TextInputState) {
1689 state.overwrite = false;
1690 }
1691
1692 match event {
1693 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1694 let c = (m.column as i16) - (self.inner.x as i16);
1695 clear_overwrite(self);
1696 self.set_screen_cursor(c, true).into()
1697 }
1698 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1699 let cx = m.column as i16 - self.inner.x as i16;
1700 clear_overwrite(self);
1701 self.set_screen_cursor_words(cx, true).into()
1702 }
1703 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1704 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1705 let start = self.word_start(tx);
1706 let end = self.word_end(tx);
1707 clear_overwrite(self);
1708 self.set_selection(start, end).into()
1709 }
1710 ct_event!(mouse down Left for column,row) => {
1711 if self.gained_focus() {
1712 TextOutcome::Unchanged
1715 } else if self.inner.contains((*column, *row).into()) {
1716 let c = (column - self.inner.x) as i16;
1717 clear_overwrite(self);
1718 self.set_screen_cursor(c, false).into()
1719 } else {
1720 TextOutcome::Continue
1721 }
1722 }
1723 ct_event!(mouse down CONTROL-Left for column,row) => {
1724 if self.inner.contains((*column, *row).into()) {
1725 let cx = (column - self.inner.x) as i16;
1726 clear_overwrite(self);
1727 self.set_screen_cursor(cx, true).into()
1728 } else {
1729 TextOutcome::Continue
1730 }
1731 }
1732 ct_event!(mouse down ALT-Left for column,row) => {
1733 if self.inner.contains((*column, *row).into()) {
1734 let cx = (column - self.inner.x) as i16;
1735 clear_overwrite(self);
1736 self.set_screen_cursor_words(cx, true).into()
1737 } else {
1738 TextOutcome::Continue
1739 }
1740 }
1741 _ => TextOutcome::Continue,
1742 }
1743 }
1744}
1745
1746pub fn handle_events(
1750 state: &mut TextInputState,
1751 focus: bool,
1752 event: &crossterm::event::Event,
1753) -> TextOutcome {
1754 state.focus.set(focus);
1755 state.handle(event, Regular)
1756}
1757
1758pub fn handle_readonly_events(
1762 state: &mut TextInputState,
1763 focus: bool,
1764 event: &crossterm::event::Event,
1765) -> TextOutcome {
1766 state.focus.set(focus);
1767 state.handle(event, ReadOnly)
1768}
1769
1770pub fn handle_mouse_events(
1772 state: &mut TextInputState,
1773 event: &crossterm::event::Event,
1774) -> TextOutcome {
1775 state.handle(event, MouseOnly)
1776}