1use crate::_private::NonExhaustive;
17#[allow(deprecated)]
18use crate::Glyph;
19use crate::clipboard::{Clipboard, global_clipboard};
20use crate::core::{TextCore, TextString};
21use crate::event::{ReadOnly, TextOutcome};
22use crate::glyph2::{Glyph2, TextWrap2};
23use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
24use crate::{
25 Cursor, Grapheme, HasScreenCursor, TextError, TextFocusGained, TextFocusLost, TextPosition,
26 TextRange, TextStyle, ipos_type, upos_type,
27};
28use crossterm::event::KeyModifiers;
29use rat_event::util::MouseFlags;
30use rat_event::{HandleEvent, MouseOnly, Regular, ct_event};
31use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
32use rat_reloc::{RelocatableState, relocate_area, relocate_dark_offset};
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 (
308 style.patch(invalid_style),
309 style.patch(select_style).patch(invalid_style),
310 )
311 } else {
312 (style, style.patch(select_style))
313 }
314 };
315
316 if let Some(block) = &widget.block {
318 block.render(area, buf);
319 }
320 buf.set_style(state.inner, style);
321
322 if state.inner.width == 0 || state.inner.height == 0 {
323 return;
325 }
326
327 let ox = state.offset() as u16;
328 let show_range = {
330 let start = min(ox as upos_type, state.len());
331 let end = min(start + state.inner.width as upos_type, state.len());
332 state.bytes_at_range(start..end)
333 };
334 let selection = state.selection();
335 let mut styles = Vec::new();
336
337 if widget.passwd {
338 for g in state.glyphs2() {
340 if g.screen_width() > 0 {
341 let mut style = style;
342 state
343 .value
344 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
345 for style_nr in &styles {
346 if let Some(s) = widget.text_style.get(*style_nr) {
347 style = style.patch(*s);
348 }
349 }
350 if selection.contains(&g.pos().x) {
352 style = style.patch(select_style);
353 };
354
355 let screen_pos = g.screen_pos();
357
358 if let Some(cell) =
360 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
361 {
362 cell.set_symbol("*");
363 cell.set_style(style);
364 }
365 for d in 1..g.screen_width() {
367 if let Some(cell) = buf.cell_mut((
368 state.inner.x + screen_pos.0 + d,
369 state.inner.y + screen_pos.1,
370 )) {
371 cell.reset();
372 cell.set_style(style);
373 }
374 }
375 }
376 }
377 } else {
378 for g in state.glyphs2() {
379 if g.screen_width() > 0 {
380 let mut style = style;
381 state
382 .value
383 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
384 for style_nr in &styles {
385 if let Some(s) = widget.text_style.get(*style_nr) {
386 style = style.patch(*s);
387 }
388 }
389 if selection.contains(&g.pos().x) {
391 style = style.patch(select_style);
392 };
393
394 let screen_pos = g.screen_pos();
396
397 if let Some(cell) =
399 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
400 {
401 cell.set_symbol(g.glyph());
402 cell.set_style(style);
403 }
404 for d in 1..g.screen_width() {
406 if let Some(cell) = buf.cell_mut((
407 state.inner.x + screen_pos.0 + d,
408 state.inner.y + screen_pos.1,
409 )) {
410 cell.reset();
411 cell.set_style(style);
412 }
413 }
414 }
415 }
416 }
417}
418
419impl Clone for TextInputState {
420 fn clone(&self) -> Self {
421 Self {
422 area: self.area,
423 inner: self.inner,
424 rendered: self.rendered,
425 offset: self.offset,
426 dark_offset: self.dark_offset,
427 scroll_to_cursor: self.scroll_to_cursor,
428 value: self.value.clone(),
429 invalid: self.invalid,
430 passwd: Default::default(),
431 overwrite: Default::default(),
432 on_focus_gained: Default::default(),
433 on_focus_lost: Default::default(),
434 focus: FocusFlag::named(self.focus.name()),
435 mouse: Default::default(),
436 non_exhaustive: NonExhaustive,
437 }
438 }
439}
440
441impl Default for TextInputState {
442 fn default() -> Self {
443 let mut value = TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard()));
444 value.set_glyph_line_break(false);
445
446 Self {
447 area: Default::default(),
448 inner: Default::default(),
449 rendered: Default::default(),
450 offset: Default::default(),
451 dark_offset: Default::default(),
452 scroll_to_cursor: Default::default(),
453 value,
454 invalid: Default::default(),
455 passwd: Default::default(),
456 overwrite: Default::default(),
457 on_focus_gained: Default::default(),
458 on_focus_lost: Default::default(),
459 focus: Default::default(),
460 mouse: Default::default(),
461 non_exhaustive: NonExhaustive,
462 }
463 }
464}
465
466impl HasFocus for TextInputState {
467 fn build(&self, builder: &mut FocusBuilder) {
468 builder.leaf_widget(self);
469 }
470
471 fn focus(&self) -> FocusFlag {
472 self.focus.clone()
473 }
474
475 fn area(&self) -> Rect {
476 self.area
477 }
478}
479
480impl TextInputState {
481 pub fn new() -> Self {
482 Self::default()
483 }
484
485 pub fn named(name: &str) -> Self {
486 Self {
487 focus: FocusFlag::named(name),
488 ..TextInputState::default()
489 }
490 }
491
492 #[inline]
494 pub fn set_invalid(&mut self, invalid: bool) {
495 self.invalid = invalid;
496 }
497
498 #[inline]
500 #[deprecated(since = "1.0.5", note = "wrong convention")]
501 pub fn get_invalid(&self) -> bool {
502 self.invalid
503 }
504
505 #[inline]
507 pub fn invalid(&self) -> bool {
508 self.invalid
509 }
510
511 #[inline]
515 pub fn set_overwrite(&mut self, overwrite: bool) {
516 self.overwrite = overwrite;
517 }
518
519 #[inline]
521 pub fn overwrite(&self) -> bool {
522 self.overwrite
523 }
524
525 #[inline]
527 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
528 self.value.set_glyph_ctrl(show_ctrl);
529 }
530
531 pub fn show_ctrl(&self) -> bool {
533 self.value.glyph_ctrl()
534 }
535}
536
537impl TextInputState {
538 #[inline]
541 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
542 match clip {
543 None => self.value.set_clipboard(None),
544 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
545 }
546 }
547
548 #[inline]
551 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
552 self.value.clipboard()
553 }
554
555 #[inline]
557 pub fn copy_to_clip(&mut self) -> bool {
558 let Some(clip) = self.value.clipboard() else {
559 return false;
560 };
561 if self.passwd {
562 return false;
563 }
564
565 _ = clip.set_string(self.selected_text().as_ref());
566 false
567 }
568
569 #[inline]
571 pub fn cut_to_clip(&mut self) -> bool {
572 let Some(clip) = self.value.clipboard() else {
573 return false;
574 };
575 if self.passwd {
576 return false;
577 }
578
579 match clip.set_string(self.selected_text().as_ref()) {
580 Ok(_) => self.delete_range(self.selection()),
581 Err(_) => false,
582 }
583 }
584
585 #[inline]
587 pub fn paste_from_clip(&mut self) -> bool {
588 let Some(clip) = self.value.clipboard() else {
589 return false;
590 };
591
592 if let Ok(text) = clip.get_string() {
593 self.insert_str(text)
594 } else {
595 false
596 }
597 }
598}
599
600impl TextInputState {
601 #[inline]
603 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
604 match undo {
605 None => self.value.set_undo_buffer(None),
606 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
607 }
608 }
609
610 #[inline]
612 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
613 self.value.undo_buffer()
614 }
615
616 #[inline]
618 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
619 self.value.undo_buffer_mut()
620 }
621
622 #[inline]
624 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
625 self.value.recent_replay_log()
626 }
627
628 #[inline]
630 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
631 self.value.replay_log(replay)
632 }
633
634 #[inline]
636 pub fn undo(&mut self) -> bool {
637 self.value.undo()
638 }
639
640 #[inline]
642 pub fn redo(&mut self) -> bool {
643 self.value.redo()
644 }
645}
646
647impl TextInputState {
648 #[inline]
659 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
660 self.value.set_styles(styles);
661 }
662
663 #[inline]
668 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
669 self.value.add_style(range, style);
670 }
671
672 #[inline]
675 pub fn add_range_style(
676 &mut self,
677 range: Range<upos_type>,
678 style: usize,
679 ) -> Result<(), TextError> {
680 let r = self
681 .value
682 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
683 self.value.add_style(r, style);
684 Ok(())
685 }
686
687 #[inline]
689 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
690 self.value.remove_style(range, style);
691 }
692
693 #[inline]
695 pub fn remove_range_style(
696 &mut self,
697 range: Range<upos_type>,
698 style: usize,
699 ) -> Result<(), TextError> {
700 let r = self
701 .value
702 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
703 self.value.remove_style(r, style);
704 Ok(())
705 }
706
707 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
709 self.value.styles_in(range, buf)
710 }
711
712 #[inline]
714 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
715 self.value.styles_at(byte_pos, buf)
716 }
717
718 #[inline]
721 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
722 self.value.style_match(byte_pos, style)
723 }
724
725 #[inline]
727 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
728 self.value.styles()
729 }
730}
731
732impl TextInputState {
733 #[inline]
735 pub fn offset(&self) -> upos_type {
736 self.offset
737 }
738
739 #[inline]
741 pub fn set_offset(&mut self, offset: upos_type) {
742 self.scroll_to_cursor = false;
743 self.offset = offset;
744 }
745
746 #[inline]
748 pub fn cursor(&self) -> upos_type {
749 self.value.cursor().x
750 }
751
752 #[inline]
754 pub fn anchor(&self) -> upos_type {
755 self.value.anchor().x
756 }
757
758 #[inline]
761 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
762 self.scroll_cursor_to_visible();
763 self.value
764 .set_cursor(TextPosition::new(cursor, 0), extend_selection)
765 }
766
767 #[inline]
769 pub fn has_selection(&self) -> bool {
770 self.value.has_selection()
771 }
772
773 #[inline]
775 pub fn selection(&self) -> Range<upos_type> {
776 let mut v = self.value.selection();
777 if v.start == TextPosition::new(0, 1) {
778 v.start = TextPosition::new(self.line_width(), 0);
779 }
780 if v.end == TextPosition::new(0, 1) {
781 v.end = TextPosition::new(self.line_width(), 0);
782 }
783 v.start.x..v.end.x
784 }
785
786 #[inline]
789 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
790 self.scroll_cursor_to_visible();
791 self.value
792 .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
793 }
794
795 #[inline]
798 pub fn select_all(&mut self) -> bool {
799 self.scroll_cursor_to_visible();
800 self.value.select_all()
801 }
802
803 #[inline]
805 pub fn selected_text(&self) -> &str {
806 match self.str_slice(self.selection()) {
807 Cow::Borrowed(v) => v,
808 Cow::Owned(_) => {
809 unreachable!()
810 }
811 }
812 }
813}
814
815impl TextInputState {
816 #[inline]
818 pub fn is_empty(&self) -> bool {
819 self.value.is_empty()
820 }
821
822 #[inline]
824 pub fn value<T: for<'a> From<&'a str>>(&self) -> T {
825 self.value.text().as_str().into()
826 }
827
828 #[inline]
830 pub fn text(&self) -> &str {
831 self.value.text().as_str()
832 }
833
834 #[inline]
836 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
837 self.value.str_slice_byte(range).expect("valid_range")
838 }
839
840 #[inline]
842 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
843 self.value.str_slice_byte(range)
844 }
845
846 #[inline]
848 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
849 self.value
850 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
851 .expect("valid_range")
852 }
853
854 #[inline]
856 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
857 self.value
858 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
859 }
860
861 #[inline]
863 pub fn len(&self) -> upos_type {
864 self.value.line_width(0).expect("valid_row")
865 }
866
867 #[inline]
869 pub fn line_width(&self) -> upos_type {
870 self.value.line_width(0).expect("valid_row")
871 }
872
873 #[inline]
876 #[allow(deprecated)]
877 #[deprecated(since = "1.1.0", note = "discontinued api")]
878 pub fn glyphs(&self, screen_offset: u16, screen_width: u16) -> impl Iterator<Item = Glyph<'_>> {
879 self.value
880 .glyphs(0..1, screen_offset, screen_width)
881 .expect("valid_rows")
882 }
883
884 #[inline]
886 pub fn text_graphemes(&self, pos: upos_type) -> impl Cursor<Item = Grapheme<'_>> {
887 self.value
888 .text_graphemes(TextPosition::new(pos, 0))
889 .expect("valid_pos")
890 }
891
892 #[inline]
894 pub fn try_text_graphemes(
895 &self,
896 pos: upos_type,
897 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
898 self.value.text_graphemes(TextPosition::new(pos, 0))
899 }
900
901 #[inline]
903 pub fn graphemes(
904 &self,
905 range: Range<upos_type>,
906 pos: upos_type,
907 ) -> impl Cursor<Item = Grapheme<'_>> {
908 self.value
909 .graphemes(
910 TextRange::new((range.start, 0), (range.end, 0)),
911 TextPosition::new(pos, 0),
912 )
913 .expect("valid_args")
914 }
915
916 #[inline]
918 pub fn try_graphemes(
919 &self,
920 range: Range<upos_type>,
921 pos: upos_type,
922 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
923 self.value.graphemes(
924 TextRange::new((range.start, 0), (range.end, 0)),
925 TextPosition::new(pos, 0),
926 )
927 }
928
929 #[inline]
932 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
933 self.value
934 .byte_at(TextPosition::new(pos, 0))
935 .expect("valid_pos")
936 }
937
938 #[inline]
941 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
942 self.value.byte_at(TextPosition::new(pos, 0))
943 }
944
945 #[inline]
947 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
948 self.value
949 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
950 .expect("valid_range")
951 }
952
953 #[inline]
955 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
956 self.value
957 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
958 }
959
960 #[inline]
963 pub fn byte_pos(&self, byte: usize) -> upos_type {
964 self.value.byte_pos(byte).map(|v| v.x).expect("valid_pos")
965 }
966
967 #[inline]
970 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
971 self.value.byte_pos(byte).map(|v| v.x)
972 }
973
974 #[inline]
976 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
977 self.value
978 .byte_range(bytes)
979 .map(|v| v.start.x..v.end.x)
980 .expect("valid_range")
981 }
982
983 #[inline]
985 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
986 self.value.byte_range(bytes).map(|v| v.start.x..v.end.x)
987 }
988}
989
990impl TextInputState {
991 #[inline]
993 pub fn clear(&mut self) -> bool {
994 if self.is_empty() {
995 false
996 } else {
997 self.offset = 0;
998 self.value.clear();
999 true
1000 }
1001 }
1002
1003 #[inline]
1007 pub fn set_value<S: Into<String>>(&mut self, s: S) {
1008 self.offset = 0;
1009 self.value.set_text(TextString::new_string(s.into()));
1010 }
1011
1012 #[inline]
1016 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1017 self.offset = 0;
1018 self.value.set_text(TextString::new_string(s.into()));
1019 }
1020
1021 #[inline]
1023 pub fn insert_char(&mut self, c: char) -> bool {
1024 if self.has_selection() {
1025 self.value
1026 .remove_str_range(self.value.selection())
1027 .expect("valid_selection");
1028 }
1029 if c == '\n' {
1030 return false;
1031 } else if c == '\t' {
1032 self.value
1033 .insert_tab(self.value.cursor())
1034 .expect("valid_cursor");
1035 } else {
1036 self.value
1037 .insert_char(self.value.cursor(), c)
1038 .expect("valid_cursor");
1039 }
1040 self.scroll_cursor_to_visible();
1041 true
1042 }
1043
1044 pub fn insert_tab(&mut self) -> bool {
1047 if self.has_selection() {
1048 self.value
1049 .remove_str_range(self.value.selection())
1050 .expect("valid_selection");
1051 }
1052 self.value
1053 .insert_tab(self.value.cursor())
1054 .expect("valid_cursor");
1055 self.scroll_cursor_to_visible();
1056 true
1057 }
1058
1059 #[inline]
1061 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1062 let t = t.as_ref();
1063 if self.has_selection() {
1064 self.value
1065 .remove_str_range(self.value.selection())
1066 .expect("valid_selection");
1067 }
1068 self.value
1069 .insert_str(self.value.cursor(), t)
1070 .expect("valid_cursor");
1071 self.scroll_cursor_to_visible();
1072 true
1073 }
1074
1075 #[inline]
1077 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1078 self.try_delete_range(range).expect("valid_range")
1079 }
1080
1081 #[inline]
1083 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1084 if !range.is_empty() {
1085 self.value
1086 .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1087 self.scroll_cursor_to_visible();
1088 Ok(true)
1089 } else {
1090 Ok(false)
1091 }
1092 }
1093}
1094
1095impl TextInputState {
1096 #[inline]
1098 pub fn delete_next_char(&mut self) -> bool {
1099 if self.has_selection() {
1100 self.delete_range(self.selection())
1101 } else {
1102 let r = self
1103 .value
1104 .remove_next_char(self.value.cursor())
1105 .expect("valid_cursor");
1106 self.scroll_cursor_to_visible();
1107 r
1108 }
1109 }
1110
1111 #[inline]
1113 pub fn delete_prev_char(&mut self) -> bool {
1114 if self.value.has_selection() {
1115 self.delete_range(self.selection())
1116 } else {
1117 let r = self
1118 .value
1119 .remove_prev_char(self.value.cursor())
1120 .expect("valid_cursor");
1121 self.scroll_cursor_to_visible();
1122 r
1123 }
1124 }
1125
1126 pub fn next_word_start(&self, pos: upos_type) -> upos_type {
1128 self.try_next_word_start(pos).expect("valid_pos")
1129 }
1130
1131 pub fn try_next_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1133 self.value
1134 .next_word_start(TextPosition::new(pos, 0))
1135 .map(|v| v.x)
1136 }
1137
1138 pub fn next_word_end(&self, pos: upos_type) -> upos_type {
1141 self.try_next_word_end(pos).expect("valid_pos")
1142 }
1143
1144 pub fn try_next_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1147 self.value
1148 .next_word_end(TextPosition::new(pos, 0))
1149 .map(|v| v.x)
1150 }
1151
1152 pub fn prev_word_start(&self, pos: upos_type) -> upos_type {
1156 self.try_prev_word_start(pos).expect("valid_pos")
1157 }
1158
1159 pub fn try_prev_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1163 self.value
1164 .prev_word_start(TextPosition::new(pos, 0))
1165 .map(|v| v.x)
1166 }
1167
1168 pub fn prev_word_end(&self, pos: upos_type) -> upos_type {
1172 self.try_prev_word_end(pos).expect("valid_pos")
1173 }
1174
1175 pub fn try_prev_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1179 self.value
1180 .prev_word_end(TextPosition::new(pos, 0))
1181 .map(|v| v.x)
1182 }
1183
1184 pub fn is_word_boundary(&self, pos: upos_type) -> bool {
1186 self.try_is_word_boundary(pos).expect("valid_pos")
1187 }
1188
1189 pub fn try_is_word_boundary(&self, pos: upos_type) -> Result<bool, TextError> {
1191 self.value.is_word_boundary(TextPosition::new(pos, 0))
1192 }
1193
1194 pub fn word_start(&self, pos: upos_type) -> upos_type {
1196 self.try_word_start(pos).expect("valid_pos")
1197 }
1198
1199 pub fn try_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1201 self.value
1202 .word_start(TextPosition::new(pos, 0))
1203 .map(|v| v.x)
1204 }
1205
1206 pub fn word_end(&self, pos: upos_type) -> upos_type {
1208 self.try_word_end(pos).expect("valid_pos")
1209 }
1210
1211 pub fn try_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1213 self.value.word_end(TextPosition::new(pos, 0)).map(|v| v.x)
1214 }
1215
1216 #[inline]
1218 pub fn delete_next_word(&mut self) -> bool {
1219 if self.has_selection() {
1220 self.delete_range(self.selection())
1221 } else {
1222 let cursor = self.cursor();
1223
1224 let start = self.next_word_start(cursor);
1225 if start != cursor {
1226 self.delete_range(cursor..start)
1227 } else {
1228 let end = self.next_word_end(cursor);
1229 self.delete_range(cursor..end)
1230 }
1231 }
1232 }
1233
1234 #[inline]
1236 pub fn delete_prev_word(&mut self) -> bool {
1237 if self.has_selection() {
1238 self.delete_range(self.selection())
1239 } else {
1240 let cursor = self.cursor();
1241
1242 let end = self.prev_word_end(cursor);
1243 if end != cursor {
1244 self.delete_range(end..cursor)
1245 } else {
1246 let start = self.prev_word_start(cursor);
1247 self.delete_range(start..cursor)
1248 }
1249 }
1250 }
1251
1252 #[inline]
1254 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1255 let c = min(self.cursor() + 1, self.len());
1256 self.set_cursor(c, extend_selection)
1257 }
1258
1259 #[inline]
1261 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1262 let c = self.cursor().saturating_sub(1);
1263 self.set_cursor(c, extend_selection)
1264 }
1265
1266 #[inline]
1268 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1269 self.set_cursor(0, extend_selection)
1270 }
1271
1272 #[inline]
1274 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1275 self.set_cursor(self.len(), extend_selection)
1276 }
1277
1278 #[inline]
1279 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1280 let cursor = self.cursor();
1281 let end = self.next_word_end(cursor);
1282 self.set_cursor(end, extend_selection)
1283 }
1284
1285 #[inline]
1286 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1287 let cursor = self.cursor();
1288 let start = self.prev_word_start(cursor);
1289 self.set_cursor(start, extend_selection)
1290 }
1291}
1292
1293impl HasScreenCursor for TextInputState {
1294 #[inline]
1296 fn screen_cursor(&self) -> Option<(u16, u16)> {
1297 if self.is_focused() {
1298 if self.has_selection() {
1299 None
1300 } else {
1301 let cx = self.cursor();
1302 let ox = self.offset();
1303
1304 if cx < ox {
1305 None
1306 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1307 None
1308 } else {
1309 self.col_to_screen(cx)
1310 .map(|sc| (self.inner.x + sc, self.inner.y))
1311 }
1312 }
1313 } else {
1314 None
1315 }
1316 }
1317}
1318
1319impl RelocatableState for TextInputState {
1320 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1321 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1323 self.area = relocate_area(self.area, shift, clip);
1324 self.inner = relocate_area(self.inner, shift, clip);
1325 }
1326}
1327
1328impl TextInputState {
1329 fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1330 let (text_wrap, left_margin, right_margin, word_margin) = (
1331 TextWrap2::Shift,
1332 self.offset() as upos_type,
1333 self.offset() as upos_type + self.rendered.width as upos_type,
1334 self.offset() as upos_type + self.rendered.width as upos_type,
1335 );
1336 self.value
1337 .glyphs2(
1338 self.rendered,
1339 0,
1340 0..1,
1341 text_wrap,
1342 false,
1343 left_margin,
1344 right_margin,
1345 word_margin,
1346 )
1347 .expect("valid-row")
1348 }
1349
1350 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1353 let ox = self.offset();
1354
1355 let scx = scx + self.dark_offset.0 as i16;
1356
1357 if scx < 0 {
1358 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1359 } else if scx as u16 >= self.rendered.width {
1360 min(ox + scx as upos_type, self.len())
1361 } else {
1362 let scx = scx as u16;
1363
1364 let line = self.glyphs2();
1365
1366 let mut col = ox;
1367 for g in line {
1368 if g.contains_screen_x(scx) {
1369 break;
1370 }
1371 col = g.pos().x + 1;
1372 }
1373 col
1374 }
1375 }
1376
1377 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1380 let ox = self.offset();
1381
1382 if pos < ox {
1383 return None;
1384 }
1385
1386 let line = self.glyphs2();
1387 let mut screen_x = 0;
1388 for g in line {
1389 if g.pos().x == pos {
1390 break;
1391 }
1392 screen_x = g.screen_pos().0 + g.screen_width();
1393 }
1394
1395 if screen_x >= self.dark_offset.0 {
1396 Some(screen_x - self.dark_offset.0)
1397 } else {
1398 None
1399 }
1400 }
1401
1402 #[inline]
1406 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1407 let scx = cursor;
1408
1409 let cx = self.screen_to_col(scx);
1410
1411 self.set_cursor(cx, extend_selection)
1412 }
1413
1414 pub fn set_screen_cursor_words(&mut self, screen_cursor: i16, extend_selection: bool) -> bool {
1421 let anchor = self.anchor();
1422
1423 let cx = self.screen_to_col(screen_cursor);
1424 let cursor = cx;
1425
1426 let cursor = if cursor < anchor {
1427 self.word_start(cursor)
1428 } else {
1429 self.word_end(cursor)
1430 };
1431
1432 if !self.is_word_boundary(anchor) {
1434 if cursor < anchor {
1435 self.set_cursor(self.word_end(anchor), false);
1436 } else {
1437 self.set_cursor(self.word_start(anchor), false);
1438 }
1439 }
1440
1441 self.set_cursor(cursor, extend_selection)
1442 }
1443
1444 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1446 self.set_offset(self.offset.saturating_sub(delta));
1447 true
1448 }
1449
1450 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1452 self.set_offset(self.offset + delta);
1453 true
1454 }
1455
1456 pub fn scroll_cursor_to_visible(&mut self) {
1458 self.scroll_to_cursor = true;
1459 }
1460}
1461
1462impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextInputState {
1463 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1464 fn tc(r: bool) -> TextOutcome {
1466 if r {
1467 TextOutcome::TextChanged
1468 } else {
1469 TextOutcome::Unchanged
1470 }
1471 }
1472 fn overwrite(state: &mut TextInputState) {
1473 if state.overwrite {
1474 state.overwrite = false;
1475 state.clear();
1476 }
1477 }
1478 fn clear_overwrite(state: &mut TextInputState) {
1479 state.overwrite = false;
1480 }
1481
1482 if self.lost_focus() {
1484 match self.on_focus_lost {
1485 TextFocusLost::None => {}
1486 TextFocusLost::Position0 => {
1487 self.move_to_line_start(false);
1488 }
1490 }
1491 }
1492 if self.gained_focus() {
1493 match self.on_focus_gained {
1494 TextFocusGained::None => {}
1495 TextFocusGained::Overwrite => {
1496 self.overwrite = true;
1497 }
1498 TextFocusGained::SelectAll => {
1499 self.select_all();
1500 }
1502 }
1503 }
1504
1505 let mut r = if self.is_focused() {
1506 match event {
1507 ct_event!(key press c)
1508 | ct_event!(key press SHIFT-c)
1509 | ct_event!(key press CONTROL_ALT-c) => {
1510 overwrite(self);
1511 tc(self.insert_char(*c))
1512 }
1513 ct_event!(keycode press Tab) => {
1514 tc(if !self.focus.gained() {
1516 clear_overwrite(self);
1517 self.insert_tab()
1518 } else {
1519 false
1520 })
1521 }
1522 ct_event!(keycode press Backspace) => {
1523 clear_overwrite(self);
1524 tc(self.delete_prev_char())
1525 }
1526 ct_event!(keycode press Delete) => {
1527 clear_overwrite(self);
1528 tc(self.delete_next_char())
1529 }
1530 ct_event!(keycode press CONTROL-Backspace)
1531 | ct_event!(keycode press ALT-Backspace) => {
1532 clear_overwrite(self);
1533 tc(self.delete_prev_word())
1534 }
1535 ct_event!(keycode press CONTROL-Delete) => {
1536 clear_overwrite(self);
1537 tc(self.delete_next_word())
1538 }
1539 ct_event!(key press CONTROL-'x') => {
1540 clear_overwrite(self);
1541 tc(self.cut_to_clip())
1542 }
1543 ct_event!(key press CONTROL-'v') => {
1544 overwrite(self);
1545 tc(self.paste_from_clip())
1546 }
1547 ct_event!(key press CONTROL-'d') => {
1548 clear_overwrite(self);
1549 tc(self.clear())
1550 }
1551 ct_event!(key press CONTROL-'z') => {
1552 clear_overwrite(self);
1553 tc(self.undo())
1554 }
1555 ct_event!(key press CONTROL_SHIFT-'Z') => {
1556 clear_overwrite(self);
1557 tc(self.redo())
1558 }
1559
1560 ct_event!(key release _)
1561 | ct_event!(key release SHIFT-_)
1562 | ct_event!(key release CONTROL_ALT-_)
1563 | ct_event!(keycode release Tab)
1564 | ct_event!(keycode release Backspace)
1565 | ct_event!(keycode release Delete)
1566 | ct_event!(keycode release CONTROL-Backspace)
1567 | ct_event!(keycode release ALT-Backspace)
1568 | ct_event!(keycode release CONTROL-Delete)
1569 | ct_event!(key release CONTROL-'x')
1570 | ct_event!(key release CONTROL-'v')
1571 | ct_event!(key release CONTROL-'d')
1572 | ct_event!(key release CONTROL-'y')
1573 | ct_event!(key release CONTROL-'z')
1574 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1575
1576 _ => TextOutcome::Continue,
1577 }
1578 } else {
1579 TextOutcome::Continue
1580 };
1581 if r == TextOutcome::Continue {
1582 r = self.handle(event, ReadOnly);
1583 }
1584 r
1585 }
1586}
1587
1588impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextInputState {
1589 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1590 fn clear_overwrite(state: &mut TextInputState) {
1591 state.overwrite = false;
1592 }
1593
1594 let mut r = if self.is_focused() {
1595 match event {
1596 ct_event!(keycode press Left) => {
1597 clear_overwrite(self);
1598 self.move_left(false).into()
1599 }
1600 ct_event!(keycode press Right) => {
1601 clear_overwrite(self);
1602 self.move_right(false).into()
1603 }
1604 ct_event!(keycode press CONTROL-Left) => {
1605 clear_overwrite(self);
1606 self.move_to_prev_word(false).into()
1607 }
1608 ct_event!(keycode press CONTROL-Right) => {
1609 clear_overwrite(self);
1610 self.move_to_next_word(false).into()
1611 }
1612 ct_event!(keycode press Home) => {
1613 clear_overwrite(self);
1614 self.move_to_line_start(false).into()
1615 }
1616 ct_event!(keycode press End) => {
1617 clear_overwrite(self);
1618 self.move_to_line_end(false).into()
1619 }
1620 ct_event!(keycode press SHIFT-Left) => {
1621 clear_overwrite(self);
1622 self.move_left(true).into()
1623 }
1624 ct_event!(keycode press SHIFT-Right) => {
1625 clear_overwrite(self);
1626 self.move_right(true).into()
1627 }
1628 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1629 clear_overwrite(self);
1630 self.move_to_prev_word(true).into()
1631 }
1632 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1633 clear_overwrite(self);
1634 self.move_to_next_word(true).into()
1635 }
1636 ct_event!(keycode press SHIFT-Home) => {
1637 clear_overwrite(self);
1638 self.move_to_line_start(true).into()
1639 }
1640 ct_event!(keycode press SHIFT-End) => {
1641 clear_overwrite(self);
1642 self.move_to_line_end(true).into()
1643 }
1644 ct_event!(keycode press ALT-Left) => {
1645 clear_overwrite(self);
1646 self.scroll_left(1).into()
1647 }
1648 ct_event!(keycode press ALT-Right) => {
1649 clear_overwrite(self);
1650 self.scroll_right(1).into()
1651 }
1652 ct_event!(key press CONTROL-'a') => {
1653 clear_overwrite(self);
1654 self.select_all().into()
1655 }
1656 ct_event!(key press CONTROL-'c') => {
1657 clear_overwrite(self);
1658 self.copy_to_clip().into()
1659 }
1660
1661 ct_event!(keycode release Left)
1662 | ct_event!(keycode release Right)
1663 | ct_event!(keycode release CONTROL-Left)
1664 | ct_event!(keycode release CONTROL-Right)
1665 | ct_event!(keycode release Home)
1666 | ct_event!(keycode release End)
1667 | ct_event!(keycode release SHIFT-Left)
1668 | ct_event!(keycode release SHIFT-Right)
1669 | ct_event!(keycode release CONTROL_SHIFT-Left)
1670 | ct_event!(keycode release CONTROL_SHIFT-Right)
1671 | ct_event!(keycode release SHIFT-Home)
1672 | ct_event!(keycode release SHIFT-End)
1673 | ct_event!(key release CONTROL-'a')
1674 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1675
1676 _ => TextOutcome::Continue,
1677 }
1678 } else {
1679 TextOutcome::Continue
1680 };
1681
1682 if r == TextOutcome::Continue {
1683 r = self.handle(event, MouseOnly);
1684 }
1685 r
1686 }
1687}
1688
1689impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextInputState {
1690 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1691 fn clear_overwrite(state: &mut TextInputState) {
1692 state.overwrite = false;
1693 }
1694
1695 match event {
1696 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1697 let c = (m.column as i16) - (self.inner.x as i16);
1698 clear_overwrite(self);
1699 self.set_screen_cursor(c, true).into()
1700 }
1701 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1702 let cx = m.column as i16 - self.inner.x as i16;
1703 clear_overwrite(self);
1704 self.set_screen_cursor_words(cx, true).into()
1705 }
1706 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1707 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1708 let start = self.word_start(tx);
1709 let end = self.word_end(tx);
1710 clear_overwrite(self);
1711 self.set_selection(start, end).into()
1712 }
1713 ct_event!(mouse down Left for column,row) => {
1714 if self.gained_focus() {
1715 TextOutcome::Unchanged
1718 } else if self.inner.contains((*column, *row).into()) {
1719 let c = (column - self.inner.x) as i16;
1720 clear_overwrite(self);
1721 self.set_screen_cursor(c, false).into()
1722 } else {
1723 TextOutcome::Continue
1724 }
1725 }
1726 ct_event!(mouse down CONTROL-Left for column,row) => {
1727 if self.inner.contains((*column, *row).into()) {
1728 let cx = (column - self.inner.x) as i16;
1729 clear_overwrite(self);
1730 self.set_screen_cursor(cx, true).into()
1731 } else {
1732 TextOutcome::Continue
1733 }
1734 }
1735 ct_event!(mouse down ALT-Left for column,row) => {
1736 if self.inner.contains((*column, *row).into()) {
1737 let cx = (column - self.inner.x) as i16;
1738 clear_overwrite(self);
1739 self.set_screen_cursor_words(cx, true).into()
1740 } else {
1741 TextOutcome::Continue
1742 }
1743 }
1744 _ => TextOutcome::Continue,
1745 }
1746 }
1747}
1748
1749pub fn handle_events(
1753 state: &mut TextInputState,
1754 focus: bool,
1755 event: &crossterm::event::Event,
1756) -> TextOutcome {
1757 state.focus.set(focus);
1758 state.handle(event, Regular)
1759}
1760
1761pub fn handle_readonly_events(
1765 state: &mut TextInputState,
1766 focus: bool,
1767 event: &crossterm::event::Event,
1768) -> TextOutcome {
1769 state.focus.set(focus);
1770 state.handle(event, ReadOnly)
1771}
1772
1773pub fn handle_mouse_events(
1775 state: &mut TextInputState,
1776 event: &crossterm::event::Event,
1777) -> TextOutcome {
1778 state.handle(event, MouseOnly)
1779}