1use crate::_private::NonExhaustive;
17use crate::clipboard::{Clipboard, global_clipboard};
18use crate::core::core_op::*;
19use crate::core::{TextCore, TextString};
20use crate::event::{ReadOnly, TextOutcome};
21use crate::glyph2::{Glyph2, TextWrap2};
22use crate::text_store::TextStore;
23use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
24use crate::{
25 HasScreenCursor, TextError, TextFocusGained, TextFocusLost, TextPosition, TextRange, TextStyle,
26 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 value = TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard()));
444
445 Self {
446 area: Default::default(),
447 inner: Default::default(),
448 rendered: Default::default(),
449 offset: Default::default(),
450 dark_offset: Default::default(),
451 scroll_to_cursor: Default::default(),
452 value,
453 invalid: Default::default(),
454 passwd: Default::default(),
455 overwrite: Default::default(),
456 on_focus_gained: Default::default(),
457 on_focus_lost: Default::default(),
458 focus: Default::default(),
459 mouse: Default::default(),
460 non_exhaustive: NonExhaustive,
461 }
462 }
463}
464
465impl HasFocus for TextInputState {
466 fn build(&self, builder: &mut FocusBuilder) {
467 builder.leaf_widget(self);
468 }
469
470 fn focus(&self) -> FocusFlag {
471 self.focus.clone()
472 }
473
474 fn area(&self) -> Rect {
475 self.area
476 }
477}
478
479impl TextInputState {
480 pub fn new() -> Self {
481 Self::default()
482 }
483
484 pub fn named(name: &str) -> Self {
485 Self {
486 focus: FocusFlag::named(name),
487 ..TextInputState::default()
488 }
489 }
490
491 #[inline]
493 pub fn set_invalid(&mut self, invalid: bool) {
494 self.invalid = invalid;
495 }
496
497 #[inline]
499 pub fn invalid(&self) -> bool {
500 self.invalid
501 }
502
503 #[inline]
507 pub fn set_overwrite(&mut self, overwrite: bool) {
508 self.overwrite = overwrite;
509 }
510
511 #[inline]
513 pub fn overwrite(&self) -> bool {
514 self.overwrite
515 }
516
517 #[inline]
519 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
520 self.value.set_glyph_ctrl(show_ctrl);
521 }
522
523 pub fn show_ctrl(&self) -> bool {
525 self.value.glyph_ctrl()
526 }
527}
528
529impl TextInputState {
530 #[inline]
533 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
534 match clip {
535 None => self.value.set_clipboard(None),
536 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
537 }
538 }
539
540 #[inline]
543 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
544 self.value.clipboard()
545 }
546
547 #[inline]
549 pub fn copy_to_clip(&mut self) -> bool {
550 let Some(clip) = self.value.clipboard() else {
551 return false;
552 };
553 if self.passwd {
554 return false;
555 }
556
557 _ = clip.set_string(self.selected_text().as_ref());
558 false
559 }
560
561 #[inline]
563 pub fn cut_to_clip(&mut self) -> bool {
564 let Some(clip) = self.value.clipboard() else {
565 return false;
566 };
567 if self.passwd {
568 return false;
569 }
570
571 match clip.set_string(self.selected_text().as_ref()) {
572 Ok(_) => self.delete_range(self.selection()),
573 Err(_) => false,
574 }
575 }
576
577 #[inline]
579 pub fn paste_from_clip(&mut self) -> bool {
580 let Some(clip) = self.value.clipboard() else {
581 return false;
582 };
583
584 if let Ok(text) = clip.get_string() {
585 self.insert_str(text)
586 } else {
587 false
588 }
589 }
590}
591
592impl TextInputState {
593 #[inline]
595 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
596 match undo {
597 None => self.value.set_undo_buffer(None),
598 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
599 }
600 }
601
602 #[inline]
604 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
605 self.value.undo_buffer()
606 }
607
608 #[inline]
610 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
611 self.value.undo_buffer_mut()
612 }
613
614 #[inline]
616 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
617 self.value.recent_replay_log()
618 }
619
620 #[inline]
622 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
623 self.value.replay_log(replay)
624 }
625
626 #[inline]
628 pub fn undo(&mut self) -> bool {
629 self.value.undo()
630 }
631
632 #[inline]
634 pub fn redo(&mut self) -> bool {
635 self.value.redo()
636 }
637}
638
639impl TextInputState {
640 #[inline]
651 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
652 self.value.set_styles(styles);
653 }
654
655 #[inline]
660 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
661 self.value.add_style(range, style);
662 }
663
664 #[inline]
667 pub fn add_range_style(
668 &mut self,
669 range: Range<upos_type>,
670 style: usize,
671 ) -> Result<(), TextError> {
672 let r = self
673 .value
674 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
675 self.value.add_style(r, style);
676 Ok(())
677 }
678
679 #[inline]
681 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
682 self.value.remove_style(range, style);
683 }
684
685 #[inline]
687 pub fn remove_range_style(
688 &mut self,
689 range: Range<upos_type>,
690 style: usize,
691 ) -> Result<(), TextError> {
692 let r = self
693 .value
694 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
695 self.value.remove_style(r, style);
696 Ok(())
697 }
698
699 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
701 self.value.styles_in(range, buf)
702 }
703
704 #[inline]
706 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
707 self.value.styles_at(byte_pos, buf)
708 }
709
710 #[inline]
713 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
714 self.value.styles_at_match(byte_pos, style)
715 }
716
717 #[inline]
719 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
720 self.value.styles()
721 }
722}
723
724impl TextInputState {
725 #[inline]
727 pub fn offset(&self) -> upos_type {
728 self.offset
729 }
730
731 #[inline]
733 pub fn set_offset(&mut self, offset: upos_type) {
734 self.scroll_to_cursor = false;
735 self.offset = offset;
736 }
737
738 #[inline]
740 pub fn cursor(&self) -> upos_type {
741 self.value.cursor().x
742 }
743
744 #[inline]
746 pub fn anchor(&self) -> upos_type {
747 self.value.anchor().x
748 }
749
750 #[inline]
753 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
754 self.scroll_cursor_to_visible();
755 self.value
756 .set_cursor(TextPosition::new(cursor, 0), extend_selection)
757 }
758
759 #[inline]
761 pub fn has_selection(&self) -> bool {
762 self.value.has_selection()
763 }
764
765 #[inline]
767 pub fn selection(&self) -> Range<upos_type> {
768 let mut v = self.value.selection();
769 if v.start == TextPosition::new(0, 1) {
770 v.start = TextPosition::new(self.line_width(), 0);
771 }
772 if v.end == TextPosition::new(0, 1) {
773 v.end = TextPosition::new(self.line_width(), 0);
774 }
775 v.start.x..v.end.x
776 }
777
778 #[inline]
781 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
782 self.scroll_cursor_to_visible();
783 self.value
784 .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
785 }
786
787 #[inline]
790 pub fn select_all(&mut self) -> bool {
791 self.scroll_cursor_to_visible();
792 self.value.select_all()
793 }
794
795 #[inline]
797 pub fn selected_text(&self) -> &str {
798 match self.str_slice(self.selection()) {
799 Cow::Borrowed(v) => v,
800 Cow::Owned(_) => {
801 unreachable!()
802 }
803 }
804 }
805}
806
807impl TextInputState {
808 #[inline]
810 pub fn is_empty(&self) -> bool {
811 self.value.is_empty()
812 }
813
814 #[inline]
816 pub fn value<T: for<'a> From<&'a str>>(&self) -> T {
817 self.value.text().as_str().into()
818 }
819
820 #[inline]
822 pub fn text(&self) -> &str {
823 self.value.text().as_str()
824 }
825
826 #[inline]
828 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
829 self.value.str_slice_byte(range).expect("valid_range")
830 }
831
832 #[inline]
834 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
835 self.value.str_slice_byte(range)
836 }
837
838 #[inline]
840 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
841 self.value
842 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
843 .expect("valid_range")
844 }
845
846 #[inline]
848 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
849 self.value
850 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
851 }
852
853 #[inline]
855 pub fn len(&self) -> upos_type {
856 self.value.line_width(0).expect("valid_row")
857 }
858
859 #[inline]
861 pub fn len_bytes(&self) -> usize {
862 self.value.len_bytes()
863 }
864
865 #[inline]
867 pub fn line_width(&self) -> upos_type {
868 self.value.line_width(0).expect("valid_row")
869 }
870
871 #[inline]
873 pub fn text_graphemes(&self, pos: upos_type) -> <TextString as TextStore>::GraphemeIter<'_> {
874 self.value
875 .text_graphemes(TextPosition::new(pos, 0))
876 .expect("valid_pos")
877 }
878
879 #[inline]
881 pub fn try_text_graphemes(
882 &self,
883 pos: upos_type,
884 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
885 self.value.text_graphemes(TextPosition::new(pos, 0))
886 }
887
888 #[inline]
890 pub fn graphemes(
891 &self,
892 range: Range<upos_type>,
893 pos: upos_type,
894 ) -> <TextString as TextStore>::GraphemeIter<'_> {
895 self.value
896 .graphemes(
897 TextRange::new((range.start, 0), (range.end, 0)),
898 TextPosition::new(pos, 0),
899 )
900 .expect("valid_args")
901 }
902
903 #[inline]
905 pub fn try_graphemes(
906 &self,
907 range: Range<upos_type>,
908 pos: upos_type,
909 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
910 self.value.graphemes(
911 TextRange::new((range.start, 0), (range.end, 0)),
912 TextPosition::new(pos, 0),
913 )
914 }
915
916 #[inline]
919 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
920 self.value
921 .byte_at(TextPosition::new(pos, 0))
922 .expect("valid_pos")
923 }
924
925 #[inline]
928 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
929 self.value.byte_at(TextPosition::new(pos, 0))
930 }
931
932 #[inline]
934 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
935 self.value
936 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
937 .expect("valid_range")
938 }
939
940 #[inline]
942 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
943 self.value
944 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
945 }
946
947 #[inline]
950 pub fn byte_pos(&self, byte: usize) -> upos_type {
951 self.value.byte_pos(byte).map(|v| v.x).expect("valid_pos")
952 }
953
954 #[inline]
957 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
958 self.value.byte_pos(byte).map(|v| v.x)
959 }
960
961 #[inline]
963 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
964 self.value
965 .byte_range(bytes)
966 .map(|v| v.start.x..v.end.x)
967 .expect("valid_range")
968 }
969
970 #[inline]
972 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
973 self.value.byte_range(bytes).map(|v| v.start.x..v.end.x)
974 }
975}
976
977impl TextInputState {
978 #[inline]
980 pub fn clear(&mut self) -> bool {
981 if self.is_empty() {
982 false
983 } else {
984 self.offset = 0;
985 self.value.clear();
986 true
987 }
988 }
989
990 #[inline]
994 pub fn set_value<S: Into<String>>(&mut self, s: S) {
995 self.offset = 0;
996 self.value.set_text(TextString::new_string(s.into()));
997 }
998
999 #[inline]
1003 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1004 self.offset = 0;
1005 self.value.set_text(TextString::new_string(s.into()));
1006 }
1007
1008 #[inline]
1010 pub fn insert_char(&mut self, c: char) -> bool {
1011 if self.has_selection() {
1012 self.value
1013 .remove_str_range(self.value.selection())
1014 .expect("valid_selection");
1015 }
1016 if c == '\n' {
1017 return false;
1018 } else {
1019 self.value
1020 .insert_char(self.value.cursor(), c)
1021 .expect("valid_cursor");
1022 }
1023 self.scroll_cursor_to_visible();
1024 true
1025 }
1026
1027 #[inline]
1029 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1030 let t = t.as_ref();
1031 if self.has_selection() {
1032 self.value
1033 .remove_str_range(self.value.selection())
1034 .expect("valid_selection");
1035 }
1036 self.value
1037 .insert_str(self.value.cursor(), t)
1038 .expect("valid_cursor");
1039 self.scroll_cursor_to_visible();
1040 true
1041 }
1042
1043 #[inline]
1045 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1046 self.try_delete_range(range).expect("valid_range")
1047 }
1048
1049 #[inline]
1051 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1052 if !range.is_empty() {
1053 self.value
1054 .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1055 self.scroll_cursor_to_visible();
1056 Ok(true)
1057 } else {
1058 Ok(false)
1059 }
1060 }
1061}
1062
1063impl TextInputState {
1064 #[inline]
1066 pub fn delete_next_char(&mut self) -> bool {
1067 if self.has_selection() {
1068 self.delete_range(self.selection())
1069 } else {
1070 let pos = self.value.cursor();
1071 let r = remove_next_char(&mut self.value, pos).expect("valid_cursor");
1072 self.scroll_cursor_to_visible();
1073 r
1074 }
1075 }
1076
1077 #[inline]
1079 pub fn delete_prev_char(&mut self) -> bool {
1080 if self.value.has_selection() {
1081 self.delete_range(self.selection())
1082 } else {
1083 let pos = self.value.cursor();
1084 let r = remove_prev_char(&mut self.value, pos).expect("valid_cursor");
1085 self.scroll_cursor_to_visible();
1086 r
1087 }
1088 }
1089
1090 pub fn next_word_start(&self, pos: upos_type) -> upos_type {
1092 self.try_next_word_start(pos).expect("valid_pos")
1093 }
1094
1095 pub fn try_next_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1097 next_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1098 }
1099
1100 pub fn next_word_end(&self, pos: upos_type) -> upos_type {
1103 self.try_next_word_end(pos).expect("valid_pos")
1104 }
1105
1106 pub fn try_next_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1109 next_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1110 }
1111
1112 pub fn prev_word_start(&self, pos: upos_type) -> upos_type {
1116 self.try_prev_word_start(pos).expect("valid_pos")
1117 }
1118
1119 pub fn try_prev_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1123 prev_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1124 }
1125
1126 pub fn prev_word_end(&self, pos: upos_type) -> upos_type {
1130 self.try_prev_word_end(pos).expect("valid_pos")
1131 }
1132
1133 pub fn try_prev_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1137 prev_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1138 }
1139
1140 pub fn is_word_boundary(&self, pos: upos_type) -> bool {
1142 self.try_is_word_boundary(pos).expect("valid_pos")
1143 }
1144
1145 pub fn try_is_word_boundary(&self, pos: upos_type) -> Result<bool, TextError> {
1147 is_word_boundary(&self.value, TextPosition::new(pos, 0))
1148 }
1149
1150 pub fn word_start(&self, pos: upos_type) -> upos_type {
1152 self.try_word_start(pos).expect("valid_pos")
1153 }
1154
1155 pub fn try_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1157 word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1158 }
1159
1160 pub fn word_end(&self, pos: upos_type) -> upos_type {
1162 self.try_word_end(pos).expect("valid_pos")
1163 }
1164
1165 pub fn try_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1167 word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1168 }
1169
1170 #[inline]
1172 pub fn delete_next_word(&mut self) -> bool {
1173 if self.has_selection() {
1174 self.delete_range(self.selection())
1175 } else {
1176 let cursor = self.cursor();
1177
1178 let start = self.next_word_start(cursor);
1179 if start != cursor {
1180 self.delete_range(cursor..start)
1181 } else {
1182 let end = self.next_word_end(cursor);
1183 self.delete_range(cursor..end)
1184 }
1185 }
1186 }
1187
1188 #[inline]
1190 pub fn delete_prev_word(&mut self) -> bool {
1191 if self.has_selection() {
1192 self.delete_range(self.selection())
1193 } else {
1194 let cursor = self.cursor();
1195
1196 let end = self.prev_word_end(cursor);
1197 if end != cursor {
1198 self.delete_range(end..cursor)
1199 } else {
1200 let start = self.prev_word_start(cursor);
1201 self.delete_range(start..cursor)
1202 }
1203 }
1204 }
1205
1206 #[inline]
1208 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1209 let c = min(self.cursor() + 1, self.len());
1210 self.set_cursor(c, extend_selection)
1211 }
1212
1213 #[inline]
1215 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1216 let c = self.cursor().saturating_sub(1);
1217 self.set_cursor(c, extend_selection)
1218 }
1219
1220 #[inline]
1222 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1223 self.set_cursor(0, extend_selection)
1224 }
1225
1226 #[inline]
1228 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1229 self.set_cursor(self.len(), extend_selection)
1230 }
1231
1232 #[inline]
1233 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1234 let cursor = self.cursor();
1235 let end = self.next_word_end(cursor);
1236 self.set_cursor(end, extend_selection)
1237 }
1238
1239 #[inline]
1240 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1241 let cursor = self.cursor();
1242 let start = self.prev_word_start(cursor);
1243 self.set_cursor(start, extend_selection)
1244 }
1245}
1246
1247impl HasScreenCursor for TextInputState {
1248 #[inline]
1250 fn screen_cursor(&self) -> Option<(u16, u16)> {
1251 if self.is_focused() {
1252 if self.has_selection() {
1253 None
1254 } else {
1255 let cx = self.cursor();
1256 let ox = self.offset();
1257
1258 if cx < ox {
1259 None
1260 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1261 None
1262 } else {
1263 self.col_to_screen(cx)
1264 .map(|sc| (self.inner.x + sc, self.inner.y))
1265 }
1266 }
1267 } else {
1268 None
1269 }
1270 }
1271}
1272
1273impl RelocatableState for TextInputState {
1274 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1275 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1277 self.area = relocate_area(self.area, shift, clip);
1278 self.inner = relocate_area(self.inner, shift, clip);
1279 }
1280}
1281
1282impl TextInputState {
1283 fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1284 let (text_wrap, left_margin, right_margin, word_margin) = (
1285 TextWrap2::Shift,
1286 self.offset() as upos_type,
1287 self.offset() as upos_type + self.rendered.width as upos_type,
1288 self.offset() as upos_type + self.rendered.width as upos_type,
1289 );
1290 self.value
1291 .glyphs2(
1292 self.rendered,
1293 0,
1294 0..1,
1295 0, text_wrap,
1297 false,
1298 left_margin,
1299 right_margin,
1300 word_margin,
1301 )
1302 .expect("valid-row")
1303 }
1304
1305 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1308 let ox = self.offset();
1309
1310 let scx = scx + self.dark_offset.0 as i16;
1311
1312 if scx < 0 {
1313 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1314 } else if scx as u16 >= self.rendered.width {
1315 min(ox + scx as upos_type, self.len())
1316 } else {
1317 let scx = scx as u16;
1318
1319 let line = self.glyphs2();
1320
1321 let mut col = ox;
1322 for g in line {
1323 if g.contains_screen_x(scx) {
1324 break;
1325 }
1326 col = g.pos().x + 1;
1327 }
1328 col
1329 }
1330 }
1331
1332 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1335 let ox = self.offset();
1336
1337 if pos < ox {
1338 return None;
1339 }
1340
1341 let line = self.glyphs2();
1342 let mut screen_x = 0;
1343 for g in line {
1344 if g.pos().x == pos {
1345 break;
1346 }
1347 screen_x = g.screen_pos().0 + g.screen_width();
1348 }
1349
1350 if screen_x >= self.dark_offset.0 {
1351 Some(screen_x - self.dark_offset.0)
1352 } else {
1353 None
1354 }
1355 }
1356
1357 #[inline]
1361 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1362 let scx = cursor;
1363
1364 let cx = self.screen_to_col(scx);
1365
1366 self.set_cursor(cx, extend_selection)
1367 }
1368
1369 pub fn set_screen_cursor_words(&mut self, screen_cursor: i16, extend_selection: bool) -> bool {
1376 let anchor = self.anchor();
1377
1378 let cx = self.screen_to_col(screen_cursor);
1379 let cursor = cx;
1380
1381 let cursor = if cursor < anchor {
1382 self.word_start(cursor)
1383 } else {
1384 self.word_end(cursor)
1385 };
1386
1387 if !self.is_word_boundary(anchor) {
1389 if cursor < anchor {
1390 self.set_cursor(self.word_end(anchor), false);
1391 } else {
1392 self.set_cursor(self.word_start(anchor), false);
1393 }
1394 }
1395
1396 self.set_cursor(cursor, extend_selection)
1397 }
1398
1399 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1401 self.set_offset(self.offset.saturating_sub(delta));
1402 true
1403 }
1404
1405 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1407 self.set_offset(self.offset + delta);
1408 true
1409 }
1410
1411 pub fn scroll_cursor_to_visible(&mut self) {
1413 self.scroll_to_cursor = true;
1414 }
1415}
1416
1417impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextInputState {
1418 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1419 fn tc(r: bool) -> TextOutcome {
1421 if r {
1422 TextOutcome::TextChanged
1423 } else {
1424 TextOutcome::Unchanged
1425 }
1426 }
1427 fn overwrite(state: &mut TextInputState) {
1428 if state.overwrite {
1429 state.overwrite = false;
1430 state.clear();
1431 }
1432 }
1433 fn clear_overwrite(state: &mut TextInputState) {
1434 state.overwrite = false;
1435 }
1436
1437 if self.lost_focus() {
1439 match self.on_focus_lost {
1440 TextFocusLost::None => {}
1441 TextFocusLost::Position0 => {
1442 self.move_to_line_start(false);
1443 }
1445 }
1446 }
1447 if self.gained_focus() {
1448 match self.on_focus_gained {
1449 TextFocusGained::None => {}
1450 TextFocusGained::Overwrite => {
1451 self.overwrite = true;
1452 }
1453 TextFocusGained::SelectAll => {
1454 self.select_all();
1455 }
1457 }
1458 }
1459
1460 let mut r = if self.is_focused() {
1461 match event {
1462 ct_event!(key press c)
1463 | ct_event!(key press SHIFT-c)
1464 | ct_event!(key press CONTROL_ALT-c) => {
1465 overwrite(self);
1466 tc(self.insert_char(*c))
1467 }
1468 ct_event!(keycode press Backspace) => {
1469 clear_overwrite(self);
1470 tc(self.delete_prev_char())
1471 }
1472 ct_event!(keycode press Delete) => {
1473 clear_overwrite(self);
1474 tc(self.delete_next_char())
1475 }
1476 ct_event!(keycode press CONTROL-Backspace)
1477 | ct_event!(keycode press ALT-Backspace) => {
1478 clear_overwrite(self);
1479 tc(self.delete_prev_word())
1480 }
1481 ct_event!(keycode press CONTROL-Delete) => {
1482 clear_overwrite(self);
1483 tc(self.delete_next_word())
1484 }
1485 ct_event!(key press CONTROL-'x') => {
1486 clear_overwrite(self);
1487 tc(self.cut_to_clip())
1488 }
1489 ct_event!(key press CONTROL-'v') => {
1490 overwrite(self);
1491 tc(self.paste_from_clip())
1492 }
1493 ct_event!(key press CONTROL-'d') => {
1494 clear_overwrite(self);
1495 tc(self.clear())
1496 }
1497 ct_event!(key press CONTROL-'z') => {
1498 clear_overwrite(self);
1499 tc(self.undo())
1500 }
1501 ct_event!(key press CONTROL_SHIFT-'Z') => {
1502 clear_overwrite(self);
1503 tc(self.redo())
1504 }
1505
1506 ct_event!(key release _)
1507 | ct_event!(key release SHIFT-_)
1508 | ct_event!(key release CONTROL_ALT-_)
1509 | ct_event!(keycode release Tab)
1510 | ct_event!(keycode release Backspace)
1511 | ct_event!(keycode release Delete)
1512 | ct_event!(keycode release CONTROL-Backspace)
1513 | ct_event!(keycode release ALT-Backspace)
1514 | ct_event!(keycode release CONTROL-Delete)
1515 | ct_event!(key release CONTROL-'x')
1516 | ct_event!(key release CONTROL-'v')
1517 | ct_event!(key release CONTROL-'d')
1518 | ct_event!(key release CONTROL-'y')
1519 | ct_event!(key release CONTROL-'z')
1520 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1521
1522 _ => TextOutcome::Continue,
1523 }
1524 } else {
1525 TextOutcome::Continue
1526 };
1527 if r == TextOutcome::Continue {
1528 r = self.handle(event, ReadOnly);
1529 }
1530 r
1531 }
1532}
1533
1534impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextInputState {
1535 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1536 fn clear_overwrite(state: &mut TextInputState) {
1537 state.overwrite = false;
1538 }
1539
1540 let mut r = if self.is_focused() {
1541 match event {
1542 ct_event!(keycode press Left) => {
1543 clear_overwrite(self);
1544 self.move_left(false).into()
1545 }
1546 ct_event!(keycode press Right) => {
1547 clear_overwrite(self);
1548 self.move_right(false).into()
1549 }
1550 ct_event!(keycode press CONTROL-Left) => {
1551 clear_overwrite(self);
1552 self.move_to_prev_word(false).into()
1553 }
1554 ct_event!(keycode press CONTROL-Right) => {
1555 clear_overwrite(self);
1556 self.move_to_next_word(false).into()
1557 }
1558 ct_event!(keycode press Home) => {
1559 clear_overwrite(self);
1560 self.move_to_line_start(false).into()
1561 }
1562 ct_event!(keycode press End) => {
1563 clear_overwrite(self);
1564 self.move_to_line_end(false).into()
1565 }
1566 ct_event!(keycode press SHIFT-Left) => {
1567 clear_overwrite(self);
1568 self.move_left(true).into()
1569 }
1570 ct_event!(keycode press SHIFT-Right) => {
1571 clear_overwrite(self);
1572 self.move_right(true).into()
1573 }
1574 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1575 clear_overwrite(self);
1576 self.move_to_prev_word(true).into()
1577 }
1578 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1579 clear_overwrite(self);
1580 self.move_to_next_word(true).into()
1581 }
1582 ct_event!(keycode press SHIFT-Home) => {
1583 clear_overwrite(self);
1584 self.move_to_line_start(true).into()
1585 }
1586 ct_event!(keycode press SHIFT-End) => {
1587 clear_overwrite(self);
1588 self.move_to_line_end(true).into()
1589 }
1590 ct_event!(keycode press ALT-Left) => {
1591 clear_overwrite(self);
1592 self.scroll_left(1).into()
1593 }
1594 ct_event!(keycode press ALT-Right) => {
1595 clear_overwrite(self);
1596 self.scroll_right(1).into()
1597 }
1598 ct_event!(key press CONTROL-'a') => {
1599 clear_overwrite(self);
1600 self.select_all().into()
1601 }
1602 ct_event!(key press CONTROL-'c') => {
1603 clear_overwrite(self);
1604 self.copy_to_clip().into()
1605 }
1606
1607 ct_event!(keycode release Left)
1608 | ct_event!(keycode release Right)
1609 | ct_event!(keycode release CONTROL-Left)
1610 | ct_event!(keycode release CONTROL-Right)
1611 | ct_event!(keycode release Home)
1612 | ct_event!(keycode release End)
1613 | ct_event!(keycode release SHIFT-Left)
1614 | ct_event!(keycode release SHIFT-Right)
1615 | ct_event!(keycode release CONTROL_SHIFT-Left)
1616 | ct_event!(keycode release CONTROL_SHIFT-Right)
1617 | ct_event!(keycode release SHIFT-Home)
1618 | ct_event!(keycode release SHIFT-End)
1619 | ct_event!(key release CONTROL-'a')
1620 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1621
1622 _ => TextOutcome::Continue,
1623 }
1624 } else {
1625 TextOutcome::Continue
1626 };
1627
1628 if r == TextOutcome::Continue {
1629 r = self.handle(event, MouseOnly);
1630 }
1631 r
1632 }
1633}
1634
1635impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextInputState {
1636 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1637 fn clear_overwrite(state: &mut TextInputState) {
1638 state.overwrite = false;
1639 }
1640
1641 match event {
1642 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1643 let c = (m.column as i16) - (self.inner.x as i16);
1644 clear_overwrite(self);
1645 self.set_screen_cursor(c, true).into()
1646 }
1647 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1648 let cx = m.column as i16 - self.inner.x as i16;
1649 clear_overwrite(self);
1650 self.set_screen_cursor_words(cx, true).into()
1651 }
1652 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1653 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1654 let start = self.word_start(tx);
1655 let end = self.word_end(tx);
1656 clear_overwrite(self);
1657 self.set_selection(start, end).into()
1658 }
1659 ct_event!(mouse down Left for column,row) => {
1660 if self.gained_focus() {
1661 TextOutcome::Unchanged
1664 } else if self.inner.contains((*column, *row).into()) {
1665 let c = (column - self.inner.x) as i16;
1666 clear_overwrite(self);
1667 self.set_screen_cursor(c, false).into()
1668 } else {
1669 TextOutcome::Continue
1670 }
1671 }
1672 ct_event!(mouse down CONTROL-Left for column,row) => {
1673 if self.inner.contains((*column, *row).into()) {
1674 let cx = (column - self.inner.x) as i16;
1675 clear_overwrite(self);
1676 self.set_screen_cursor(cx, true).into()
1677 } else {
1678 TextOutcome::Continue
1679 }
1680 }
1681 ct_event!(mouse down ALT-Left for column,row) => {
1682 if self.inner.contains((*column, *row).into()) {
1683 let cx = (column - self.inner.x) as i16;
1684 clear_overwrite(self);
1685 self.set_screen_cursor_words(cx, true).into()
1686 } else {
1687 TextOutcome::Continue
1688 }
1689 }
1690 _ => TextOutcome::Continue,
1691 }
1692 }
1693}
1694
1695pub fn handle_events(
1699 state: &mut TextInputState,
1700 focus: bool,
1701 event: &crossterm::event::Event,
1702) -> TextOutcome {
1703 state.focus.set(focus);
1704 state.handle(event, Regular)
1705}
1706
1707pub fn handle_readonly_events(
1711 state: &mut TextInputState,
1712 focus: bool,
1713 event: &crossterm::event::Event,
1714) -> TextOutcome {
1715 state.focus.set(focus);
1716 state.handle(event, ReadOnly)
1717}
1718
1719pub fn handle_mouse_events(
1721 state: &mut TextInputState,
1722 event: &crossterm::event::Event,
1723) -> TextOutcome {
1724 state.handle(event, MouseOnly)
1725}