1use crate::_private::NonExhaustive;
73use crate::clipboard::Clipboard;
74use crate::event::{ReadOnly, TextOutcome};
75use crate::text_input::TextInputState;
76use crate::text_mask_core::MaskedCore;
77use crate::undo_buffer::{UndoBuffer, UndoEntry};
78use crate::{
79 ipos_type, upos_type, Cursor, Glyph, Grapheme, HasScreenCursor, TextError, TextFocusGained,
80 TextFocusLost, TextStyle,
81};
82use crossterm::event::KeyModifiers;
83use format_num_pattern::NumberSymbols;
84use rat_event::util::MouseFlags;
85use rat_event::{ct_event, HandleEvent, MouseOnly, Regular};
86use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
87use rat_reloc::{relocate_area, relocate_dark_offset, RelocatableState};
88use ratatui::buffer::Buffer;
89use ratatui::layout::Rect;
90use ratatui::prelude::BlockExt;
91use ratatui::style::{Style, Stylize};
92#[cfg(feature = "unstable-widget-ref")]
93use ratatui::widgets::StatefulWidgetRef;
94use ratatui::widgets::{Block, StatefulWidget, Widget};
95use std::borrow::Cow;
96use std::cmp::min;
97use std::fmt;
98use std::ops::Range;
99
100#[derive(Debug, Default, Clone)]
106pub struct MaskedInput<'a> {
107 compact: bool,
108 block: Option<Block<'a>>,
109 style: Style,
110 focus_style: Option<Style>,
111 select_style: Option<Style>,
112 invalid_style: Option<Style>,
113 text_style: Vec<Style>,
114 on_focus_gained: TextFocusGained,
115 on_focus_lost: TextFocusLost,
116}
117
118#[derive(Debug)]
120pub struct MaskedInputState {
121 pub area: Rect,
124 pub inner: Rect,
127
128 pub offset: upos_type,
131 pub dark_offset: (u16, u16),
134
135 pub value: MaskedCore,
137 pub invalid: bool,
140 pub overwrite: bool,
143 pub on_focus_gained: TextFocusGained,
146 pub on_focus_lost: TextFocusLost,
149
150 pub focus: FocusFlag,
153
154 pub mouse: MouseFlags,
157
158 pub non_exhaustive: NonExhaustive,
160}
161
162impl<'a> MaskedInput<'a> {
163 pub fn new() -> Self {
165 Self::default()
166 }
167
168 #[inline]
171 pub fn compact(mut self, show_compact: bool) -> Self {
172 self.compact = show_compact;
173 self
174 }
175
176 #[inline]
178 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
179 if let Some(styles) = styles {
180 self.styles(styles)
181 } else {
182 self
183 }
184 }
185
186 #[inline]
188 pub fn styles(mut self, styles: TextStyle) -> Self {
189 self.style = styles.style;
190 if styles.focus.is_some() {
191 self.focus_style = styles.focus;
192 }
193 if styles.select.is_some() {
194 self.select_style = styles.select;
195 }
196 if styles.invalid.is_some() {
197 self.invalid_style = styles.invalid;
198 }
199 if let Some(of) = styles.on_focus_gained {
200 self.on_focus_gained = of;
201 }
202 if let Some(of) = styles.on_focus_lost {
203 self.on_focus_lost = of;
204 }
205 if let Some(border_style) = styles.border_style {
206 self.block = self.block.map(|v| v.border_style(border_style));
207 }
208 self.block = self.block.map(|v| v.style(self.style));
209 if styles.block.is_some() {
210 self.block = styles.block;
211 }
212 self.block = self.block.map(|v| v.style(self.style));
213 self
214 }
215
216 #[inline]
218 pub fn style(mut self, style: impl Into<Style>) -> Self {
219 self.style = style.into();
220 self.block = self.block.map(|v| v.style(self.style));
221 self
222 }
223
224 #[inline]
226 pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
227 self.focus_style = Some(style.into());
228 self
229 }
230
231 #[inline]
233 pub fn select_style(mut self, style: impl Into<Style>) -> Self {
234 self.select_style = Some(style.into());
235 self
236 }
237
238 #[inline]
241 pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
242 self.invalid_style = Some(style.into());
243 self
244 }
245
246 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
251 self.text_style = styles.into_iter().collect();
252 self
253 }
254
255 #[inline]
257 pub fn block(mut self, block: Block<'a>) -> Self {
258 self.block = Some(block);
259 self.block = self.block.map(|v| v.style(self.style));
260 self
261 }
262
263 #[inline]
265 pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
266 self.on_focus_gained = of;
267 self
268 }
269
270 #[inline]
272 pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
273 self.on_focus_lost = of;
274 self
275 }
276}
277
278#[cfg(feature = "unstable-widget-ref")]
279impl<'a> StatefulWidgetRef for MaskedInput<'a> {
280 type State = MaskedInputState;
281
282 fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
283 render_ref(self, area, buf, state);
284 }
285}
286
287impl StatefulWidget for MaskedInput<'_> {
288 type State = MaskedInputState;
289
290 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
291 render_ref(&self, area, buf, state);
292 }
293}
294
295fn render_ref(
296 widget: &MaskedInput<'_>,
297 area: Rect,
298
299 buf: &mut Buffer,
300 state: &mut MaskedInputState,
301) {
302 state.area = area;
303 state.inner = widget.block.inner_if_some(area);
304 state.on_focus_gained = widget.on_focus_gained;
305 state.on_focus_lost = widget.on_focus_lost;
306
307 let inner = state.inner;
308
309 let style = widget.style;
310 let focus_style = if let Some(focus_style) = widget.focus_style {
311 focus_style
312 } else {
313 style
314 };
315 let select_style = if let Some(select_style) = widget.select_style {
316 select_style
317 } else {
318 Style::default().black().on_yellow()
319 };
320 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
321 invalid_style
322 } else {
323 Style::default().red()
324 };
325
326 let (style, select_style) = if state.focus.get() {
327 if state.invalid {
328 (
329 style.patch(focus_style).patch(invalid_style),
330 style
331 .patch(focus_style)
332 .patch(select_style)
333 .patch(invalid_style),
334 )
335 } else {
336 (
337 style.patch(focus_style),
338 style.patch(focus_style).patch(select_style),
339 )
340 }
341 } else {
342 if state.invalid {
343 (style.patch(invalid_style), style.patch(invalid_style))
344 } else {
345 (style, style)
346 }
347 };
348
349 if widget.block.is_some() {
351 widget.block.render(area, buf);
352 } else {
353 buf.set_style(area, style);
354 }
355
356 if inner.width == 0 || inner.height == 0 {
357 return;
359 }
360
361 let ox = state.offset() as u16;
362 let show_range = {
364 let start = ox as upos_type;
365 let end = min(start + inner.width as upos_type, state.len());
366 state.bytes_at_range(start..end)
367 };
368 let selection = state.selection();
369 let mut styles = Vec::new();
370
371 let mut glyph_iter_regular;
372 let mut glyph_iter_cond;
373 let glyph_iter: &mut dyn Iterator<Item = Glyph<'_>>;
374 if state.is_focused() || !widget.compact {
375 glyph_iter_regular = state
376 .value
377 .glyphs(0..1, ox, inner.width)
378 .expect("valid_offset");
379 glyph_iter = &mut glyph_iter_regular;
380 } else {
381 glyph_iter_cond = state
382 .value
383 .condensed_glyphs(0..1, ox, inner.width)
384 .expect("valid_offset");
385 glyph_iter = &mut glyph_iter_cond;
386 }
387
388 for g in glyph_iter {
389 if g.screen_width() > 0 {
390 let mut style = style;
391 styles.clear();
392 state
393 .value
394 .styles_at_page(show_range.clone(), g.text_bytes().start, &mut styles);
395 for style_nr in &styles {
396 if let Some(s) = widget.text_style.get(*style_nr) {
397 style = style.patch(*s);
398 }
399 }
400 if selection.contains(&g.pos().x) {
402 style = style.patch(select_style);
403 };
404
405 let screen_pos = g.screen_pos();
407
408 if let Some(cell) = buf.cell_mut((inner.x + screen_pos.0, inner.y + screen_pos.1)) {
410 cell.set_symbol(g.glyph());
411 cell.set_style(style);
412 }
413 for d in 1..g.screen_width() {
415 if let Some(cell) =
416 buf.cell_mut((inner.x + screen_pos.0 + d, inner.y + screen_pos.1))
417 {
418 cell.reset();
419 cell.set_style(style);
420 }
421 }
422 }
423 }
424}
425
426impl Clone for MaskedInputState {
427 fn clone(&self) -> Self {
428 Self {
429 area: self.area,
430 inner: self.inner,
431 offset: self.offset,
432 dark_offset: self.dark_offset,
433 value: self.value.clone(),
434 invalid: self.invalid,
435 overwrite: Default::default(),
436 on_focus_gained: Default::default(),
437 on_focus_lost: Default::default(),
438 focus: FocusFlag::named(self.focus.name()),
439 mouse: Default::default(),
440 non_exhaustive: NonExhaustive,
441 }
442 }
443}
444
445impl Default for MaskedInputState {
446 fn default() -> Self {
447 Self {
448 area: Default::default(),
449 inner: Default::default(),
450 offset: Default::default(),
451 dark_offset: Default::default(),
452 value: Default::default(),
453 invalid: Default::default(),
454 overwrite: Default::default(),
455 on_focus_gained: Default::default(),
456 on_focus_lost: Default::default(),
457 focus: Default::default(),
458 mouse: Default::default(),
459 non_exhaustive: NonExhaustive,
460 }
461 }
462}
463
464impl HasFocus for MaskedInputState {
465 fn build(&self, builder: &mut FocusBuilder) {
466 builder.leaf_widget(self);
467 }
468
469 fn focus(&self) -> FocusFlag {
470 self.focus.clone()
471 }
472
473 fn area(&self) -> Rect {
474 self.area
475 }
476
477 fn navigable(&self) -> Navigation {
478 let sel = self.selection();
479
480 let has_next = self
481 .value
482 .next_section_range(sel.end)
483 .map(|v| !v.is_empty())
484 .is_some();
485 let has_prev = self
486 .value
487 .prev_section_range(sel.start.saturating_sub(1))
488 .map(|v| !v.is_empty())
489 .is_some();
490
491 if has_next {
492 if has_prev {
493 Navigation::Reach
494 } else {
495 Navigation::ReachLeaveFront
496 }
497 } else {
498 if has_prev {
499 Navigation::ReachLeaveBack
500 } else {
501 Navigation::Regular
502 }
503 }
504 }
505}
506
507impl MaskedInputState {
508 pub fn new() -> Self {
509 Self::default()
510 }
511
512 pub fn named(name: &str) -> Self {
513 Self {
514 focus: FocusFlag::named(name),
515 ..MaskedInputState::default()
516 }
517 }
518
519 #[inline]
521 pub fn with_symbols(mut self, sym: NumberSymbols) -> Self {
522 self.set_num_symbols(sym);
523 self
524 }
525
526 pub fn with_mask<S: AsRef<str>>(mut self, mask: S) -> Result<Self, fmt::Error> {
528 self.value.set_mask(mask.as_ref())?;
529 Ok(self)
530 }
531
532 #[inline]
537 pub fn set_num_symbols(&mut self, sym: NumberSymbols) {
538 self.value.set_num_symbols(sym);
539 }
540
541 #[inline]
572 pub fn set_mask<S: AsRef<str>>(&mut self, s: S) -> Result<(), fmt::Error> {
573 self.value.set_mask(s)
574 }
575
576 #[inline]
578 pub fn mask(&self) -> String {
579 self.value.mask()
580 }
581
582 #[inline]
584 pub fn set_invalid(&mut self, invalid: bool) {
585 self.invalid = invalid;
586 }
587
588 #[inline]
590 pub fn get_invalid(&self) -> bool {
591 self.invalid
592 }
593
594 #[inline]
598 pub fn set_overwrite(&mut self, overwrite: bool) {
599 self.overwrite = overwrite;
600 }
601
602 #[inline]
604 pub fn overwrite(&self) -> bool {
605 self.overwrite
606 }
607}
608
609impl MaskedInputState {
610 #[inline]
613 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
614 match clip {
615 None => self.value.set_clipboard(None),
616 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
617 }
618 }
619
620 #[inline]
623 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
624 self.value.clipboard()
625 }
626
627 #[inline]
629 pub fn copy_to_clip(&mut self) -> bool {
630 let Some(clip) = self.value.clipboard() else {
631 return false;
632 };
633
634 _ = clip.set_string(self.selected_text().as_ref());
635
636 true
637 }
638
639 #[inline]
641 pub fn cut_to_clip(&mut self) -> bool {
642 let Some(clip) = self.value.clipboard() else {
643 return false;
644 };
645
646 match clip.set_string(self.selected_text().as_ref()) {
647 Ok(_) => self.delete_range(self.selection()),
648 Err(_) => true,
649 }
650 }
651
652 #[inline]
654 pub fn paste_from_clip(&mut self) -> bool {
655 let Some(clip) = self.value.clipboard() else {
656 return false;
657 };
658
659 if let Ok(text) = clip.get_string() {
660 for c in text.chars() {
661 self.insert_char(c);
662 }
663 true
664 } else {
665 false
666 }
667 }
668}
669
670impl MaskedInputState {
671 #[inline]
673 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
674 match undo {
675 None => self.value.set_undo_buffer(None),
676 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
677 }
678 }
679
680 #[inline]
682 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
683 self.value.undo_buffer()
684 }
685
686 #[inline]
688 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
689 self.value.undo_buffer_mut()
690 }
691
692 #[inline]
694 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
695 self.value.recent_replay_log()
696 }
697
698 #[inline]
700 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
701 self.value.replay_log(replay)
702 }
703
704 #[inline]
706 pub fn undo(&mut self) -> bool {
707 self.value.undo()
708 }
709
710 #[inline]
712 pub fn redo(&mut self) -> bool {
713 self.value.redo()
714 }
715}
716
717impl MaskedInputState {
718 #[inline]
720 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
721 self.value.set_styles(styles);
722 }
723
724 #[inline]
727 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
728 self.value.add_style(range, style);
729 }
730
731 #[inline]
734 pub fn add_range_style(
735 &mut self,
736 range: Range<upos_type>,
737 style: usize,
738 ) -> Result<(), TextError> {
739 let r = self.value.bytes_at_range(range)?;
740 self.value.add_style(r, style);
741 Ok(())
742 }
743
744 #[inline]
746 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
747 self.value.remove_style(range, style);
748 }
749
750 #[inline]
752 pub fn remove_range_style(
753 &mut self,
754 range: Range<upos_type>,
755 style: usize,
756 ) -> Result<(), TextError> {
757 let r = self.value.bytes_at_range(range)?;
758 self.value.remove_style(r, style);
759 Ok(())
760 }
761
762 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
764 self.value.styles_in(range, buf)
765 }
766
767 #[inline]
769 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
770 self.value.styles_at(byte_pos, buf)
771 }
772
773 #[inline]
776 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
777 self.value.style_match(byte_pos, style)
778 }
779
780 #[inline]
782 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
783 self.value.styles()
784 }
785}
786
787impl MaskedInputState {
788 #[inline]
790 pub fn offset(&self) -> upos_type {
791 self.offset
792 }
793
794 #[inline]
796 pub fn set_offset(&mut self, offset: upos_type) {
797 self.offset = offset;
798 }
799
800 #[inline]
802 pub fn cursor(&self) -> upos_type {
803 self.value.cursor()
804 }
805
806 #[inline]
808 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
809 self.value.set_cursor(cursor, extend_selection)
810 }
811
812 #[inline]
815 pub fn set_default_cursor(&mut self) {
816 self.value.set_default_cursor();
817 }
818
819 #[inline]
821 pub fn anchor(&self) -> upos_type {
822 self.value.anchor()
823 }
824
825 #[inline]
827 pub fn has_selection(&self) -> bool {
828 self.value.has_selection()
829 }
830
831 #[inline]
833 pub fn selection(&self) -> Range<upos_type> {
834 self.value.selection()
835 }
836
837 #[inline]
839 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
840 self.value.set_selection(anchor, cursor)
841 }
842
843 #[inline]
845 pub fn select_all(&mut self) -> bool {
846 if let Some(section) = self.value.section_range(self.cursor()) {
847 if self.selection() == section {
848 self.value.select_all()
849 } else {
850 self.value.set_selection(section.start, section.end)
851 }
852 } else {
853 self.value.select_all()
854 }
855 }
856
857 #[inline]
859 pub fn selected_text(&self) -> &str {
860 self.value.selected_text()
861 }
862}
863
864impl MaskedInputState {
865 #[inline]
867 pub fn is_empty(&self) -> bool {
868 self.value.is_empty()
869 }
870
871 #[inline]
873 pub fn text(&self) -> &str {
874 self.value.text()
875 }
876
877 #[inline]
879 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
880 self.value.str_slice_byte(range).expect("valid_range")
881 }
882
883 #[inline]
885 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
886 self.value.str_slice_byte(range)
887 }
888
889 #[inline]
891 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
892 self.value.str_slice(range).expect("valid_range")
893 }
894
895 #[inline]
897 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
898 self.value.str_slice(range)
899 }
900
901 #[inline]
903 pub fn len(&self) -> upos_type {
904 self.value.line_width()
905 }
906
907 #[inline]
909 pub fn line_width(&self) -> upos_type {
910 self.value.line_width()
911 }
912
913 #[inline]
916 pub fn glyphs(&self, screen_offset: u16, screen_width: u16) -> impl Iterator<Item = Glyph<'_>> {
917 self.value
918 .glyphs(0..1, screen_offset, screen_width)
919 .expect("valid_row")
920 }
921
922 #[inline]
925 pub fn condensed_glyphs(
926 &self,
927 screen_offset: u16,
928 screen_width: u16,
929 ) -> impl Iterator<Item = Glyph<'_>> {
930 self.value
931 .condensed_glyphs(0..1, screen_offset, screen_width)
932 .expect("valid_row")
933 }
934
935 #[inline]
937 pub fn text_graphemes(&self, pos: upos_type) -> impl Cursor<Item = Grapheme<'_>> {
938 self.value.text_graphemes(pos).expect("valid_pos")
939 }
940
941 #[inline]
943 pub fn try_text_graphemes(
944 &self,
945 pos: upos_type,
946 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
947 self.value.text_graphemes(pos)
948 }
949
950 #[inline]
952 pub fn graphemes(
953 &self,
954 range: Range<upos_type>,
955 pos: upos_type,
956 ) -> impl Cursor<Item = Grapheme<'_>> {
957 self.value.graphemes(range, pos).expect("valid_args")
958 }
959
960 #[inline]
962 pub fn try_graphemes(
963 &self,
964 range: Range<upos_type>,
965 pos: upos_type,
966 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
967 self.value.graphemes(range, pos)
968 }
969
970 #[inline]
973 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
974 self.value.byte_at(pos).expect("valid_pos")
975 }
976
977 #[inline]
980 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
981 self.value.byte_at(pos)
982 }
983
984 #[inline]
986 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
987 self.value.bytes_at_range(range).expect("valid_range")
988 }
989
990 #[inline]
992 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
993 self.value.bytes_at_range(range)
994 }
995
996 #[inline]
999 pub fn byte_pos(&self, byte: usize) -> upos_type {
1000 self.value.byte_pos(byte).expect("valid_pos")
1001 }
1002
1003 #[inline]
1006 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
1007 self.value.byte_pos(byte)
1008 }
1009
1010 #[inline]
1012 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
1013 self.value.byte_range(bytes).expect("valid_range")
1014 }
1015
1016 #[inline]
1018 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
1019 self.value.byte_range(bytes)
1020 }
1021}
1022
1023impl MaskedInputState {
1024 #[inline]
1026 pub fn clear(&mut self) -> bool {
1027 if self.is_empty() {
1028 false
1029 } else {
1030 self.offset = 0;
1031 self.value.clear();
1032 true
1033 }
1034 }
1035
1036 #[inline]
1042 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1043 self.offset = 0;
1044 self.value.set_text(s);
1045 self.value.set_default_cursor();
1046 }
1047
1048 #[inline]
1050 pub fn insert_char(&mut self, c: char) -> bool {
1051 self.value.begin_undo_seq();
1052 if self.value.has_selection() {
1053 let sel = self.value.selection();
1054 self.value
1055 .remove_range(sel.clone())
1056 .expect("valid_selection");
1057 self.value.set_cursor(sel.start, false);
1058 }
1059 let c0 = self.value.advance_cursor(c);
1060 let c1 = self.value.insert_char(c);
1061 self.value.end_undo_seq();
1062
1063 self.scroll_cursor_to_visible();
1064 c0 || c1
1065 }
1066
1067 #[inline]
1070 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1071 self.try_delete_range(range).expect("valid_range")
1072 }
1073
1074 #[inline]
1077 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1078 self.value.begin_undo_seq();
1079 let r = self.value.remove_range(range.clone())?;
1080 if let Some(pos) = self.value.section_cursor(range.start) {
1081 self.value.set_cursor(pos, false);
1082 }
1083 self.value.end_undo_seq();
1084
1085 self.scroll_cursor_to_visible();
1086 Ok(r)
1087 }
1088}
1089
1090impl MaskedInputState {
1091 #[inline]
1093 pub fn delete_next_char(&mut self) -> bool {
1094 if self.has_selection() {
1095 self.delete_range(self.selection())
1096 } else if self.cursor() == self.len() {
1097 false
1098 } else {
1099 self.value.remove_next();
1100 self.scroll_cursor_to_visible();
1101 true
1102 }
1103 }
1104
1105 #[inline]
1107 pub fn delete_prev_char(&mut self) -> bool {
1108 if self.has_selection() {
1109 self.delete_range(self.selection())
1110 } else if self.cursor() == 0 {
1111 false
1112 } else {
1113 self.value.remove_prev();
1114 self.scroll_cursor_to_visible();
1115 true
1116 }
1117 }
1118
1119 #[inline]
1121 pub fn delete_prev_section(&mut self) -> bool {
1122 if self.has_selection() {
1123 self.delete_range(self.selection())
1124 } else {
1125 if let Some(range) = self.value.prev_section_range(self.cursor()) {
1126 self.delete_range(range)
1127 } else {
1128 false
1129 }
1130 }
1131 }
1132
1133 #[inline]
1135 pub fn delete_next_section(&mut self) -> bool {
1136 if self.has_selection() {
1137 self.delete_range(self.selection())
1138 } else {
1139 if let Some(range) = self.value.next_section_range(self.cursor()) {
1140 self.delete_range(range)
1141 } else {
1142 false
1143 }
1144 }
1145 }
1146
1147 #[inline]
1149 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1150 let c = min(self.cursor() + 1, self.len());
1151 let c = self.set_cursor(c, extend_selection);
1152 let s = self.scroll_cursor_to_visible();
1153 c || s
1154 }
1155
1156 #[inline]
1158 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1159 let c = self.cursor().saturating_sub(1);
1160 let c = self.set_cursor(c, extend_selection);
1161 let s = self.scroll_cursor_to_visible();
1162 c || s
1163 }
1164
1165 #[inline]
1167 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1168 let c = if let Some(c) = self.value.section_cursor(self.cursor()) {
1169 if c != self.cursor() {
1170 self.set_cursor(c, extend_selection)
1171 } else {
1172 self.set_cursor(0, extend_selection)
1173 }
1174 } else {
1175 self.set_cursor(0, extend_selection)
1176 };
1177 let s = self.scroll_cursor_to_visible();
1178 c || s
1179 }
1180
1181 #[inline]
1183 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1184 let c = self.len();
1185 let c = self.set_cursor(c, extend_selection);
1186 let s = self.scroll_cursor_to_visible();
1187 c || s
1188 }
1189
1190 #[inline]
1192 pub fn move_to_prev_section(&mut self, extend_selection: bool) -> bool {
1193 if let Some(curr) = self.value.section_range(self.cursor()) {
1194 if self.value.cursor() != curr.start {
1195 return self.value.set_cursor(curr.start, extend_selection);
1196 }
1197 }
1198 if let Some(range) = self.value.prev_section_range(self.cursor()) {
1199 self.value.set_cursor(range.start, extend_selection)
1200 } else {
1201 false
1202 }
1203 }
1204
1205 #[inline]
1207 pub fn move_to_next_section(&mut self, extend_selection: bool) -> bool {
1208 if let Some(curr) = self.value.section_range(self.cursor()) {
1209 if self.value.cursor() != curr.end {
1210 return self.value.set_cursor(curr.end, extend_selection);
1211 }
1212 }
1213 if let Some(range) = self.value.next_section_range(self.cursor()) {
1214 self.value.set_cursor(range.end, extend_selection)
1215 } else {
1216 false
1217 }
1218 }
1219
1220 #[inline]
1222 pub fn select_current_section(&mut self) -> bool {
1223 let selection = self.selection();
1224
1225 if let Some(next) = self.value.section_range(selection.start.saturating_sub(1)) {
1226 if !next.is_empty() {
1227 self.set_selection(next.start, next.end)
1228 } else {
1229 false
1230 }
1231 } else {
1232 false
1233 }
1234 }
1235
1236 #[inline]
1238 pub fn select_next_section(&mut self) -> bool {
1239 let selection = self.selection();
1240
1241 if let Some(next) = self.value.next_section_range(selection.start) {
1242 if !next.is_empty() {
1243 self.set_selection(next.start, next.end)
1244 } else {
1245 false
1246 }
1247 } else {
1248 false
1249 }
1250 }
1251
1252 #[inline]
1254 pub fn select_prev_section(&mut self) -> bool {
1255 let selection = self.selection();
1256
1257 if let Some(next) = self
1258 .value
1259 .prev_section_range(selection.start.saturating_sub(1))
1260 {
1261 if !next.is_empty() {
1262 self.set_selection(next.start, next.end)
1263 } else {
1264 false
1265 }
1266 } else {
1267 false
1268 }
1269 }
1270}
1271
1272impl HasScreenCursor for MaskedInputState {
1273 #[inline]
1275 fn screen_cursor(&self) -> Option<(u16, u16)> {
1276 if self.is_focused() {
1277 let cx = self.cursor();
1278 let ox = self.offset();
1279
1280 if cx < ox {
1281 None
1282 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1283 None
1284 } else {
1285 self.col_to_screen(cx)
1286 .map(|sc| (self.inner.x + sc, self.inner.y))
1287 }
1288 } else {
1289 None
1290 }
1291 }
1292}
1293
1294impl RelocatableState for MaskedInputState {
1295 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1296 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1298 self.area = relocate_area(self.area, shift, clip);
1299 self.inner = relocate_area(self.inner, shift, clip);
1300 }
1301}
1302
1303impl MaskedInputState {
1304 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1307 let ox = self.offset();
1308
1309 let scx = scx + self.dark_offset.0 as i16;
1310
1311 if scx < 0 {
1312 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1313 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
1314 min(ox + scx as upos_type, self.len())
1315 } else {
1316 let scx = scx as u16;
1317
1318 let line = self.glyphs(ox as u16, self.inner.width + self.dark_offset.0);
1319
1320 let mut col = ox;
1321 for g in line {
1322 if scx < g.screen_pos().0 + g.screen_width() {
1323 break;
1324 }
1325 col = g.pos().x + 1;
1326 }
1327 col
1328 }
1329 }
1330
1331 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1334 let ox = self.offset();
1335
1336 if pos < ox {
1337 return None;
1338 }
1339
1340 let line = self.glyphs(ox as u16, self.inner.width + self.dark_offset.0);
1341 let mut screen_x = 0;
1342 for g in line {
1343 if g.pos().x == pos {
1344 break;
1345 }
1346 screen_x = g.screen_pos().0 + g.screen_width();
1347 }
1348
1349 if screen_x >= self.dark_offset.0 {
1350 Some(screen_x - self.dark_offset.0)
1351 } else {
1352 None
1353 }
1354 }
1355
1356 #[inline]
1360 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1361 let scx = cursor;
1362
1363 let cx = self.screen_to_col(scx);
1364
1365 let c = self.set_cursor(cx, extend_selection);
1366 let s = self.scroll_cursor_to_visible();
1367 c || s
1368 }
1369
1370 pub fn set_screen_cursor_sections(
1377 &mut self,
1378 screen_cursor: i16,
1379 extend_selection: bool,
1380 ) -> bool {
1381 let anchor = self.anchor();
1382 let cursor = self.screen_to_col(screen_cursor);
1383
1384 let Some(range) = self.value.section_range(cursor) else {
1385 return false;
1386 };
1387
1388 let cursor = if cursor < anchor {
1389 range.start
1390 } else {
1391 range.end
1392 };
1393
1394 if !self.value.is_section_boundary(anchor) {
1396 if let Some(range) = self.value.section_range(anchor) {
1397 if cursor < anchor {
1398 self.set_cursor(range.end, false);
1399 } else {
1400 self.set_cursor(range.start, false);
1401 }
1402 };
1403 }
1404
1405 let c = self.set_cursor(cursor, extend_selection);
1406 let s = self.scroll_cursor_to_visible();
1407 c || s
1408 }
1409
1410 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1412 self.set_offset(self.offset.saturating_sub(delta));
1413 true
1414 }
1415
1416 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1418 self.set_offset(self.offset + delta);
1419 true
1420 }
1421
1422 pub fn scroll_cursor_to_visible(&mut self) -> bool {
1424 let old_offset = self.offset();
1425
1426 let c = self.cursor();
1427 let o = self.offset();
1428
1429 let no = if c < o {
1430 c
1431 } else if c >= o + (self.inner.width + self.dark_offset.0) as upos_type {
1432 c.saturating_sub((self.inner.width + self.dark_offset.0) as upos_type)
1433 } else {
1434 o
1435 };
1436
1437 self.set_offset(no);
1438
1439 self.offset() != old_offset
1440 }
1441}
1442
1443impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for MaskedInputState {
1444 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1445 fn tc(r: bool) -> TextOutcome {
1447 if r {
1448 TextOutcome::TextChanged
1449 } else {
1450 TextOutcome::Unchanged
1451 }
1452 }
1453 fn overwrite(state: &mut MaskedInputState) {
1454 if state.overwrite {
1455 state.overwrite = false;
1456 state.clear();
1457 }
1458 }
1459 fn clear_overwrite(state: &mut MaskedInputState) {
1460 state.overwrite = false;
1461 }
1462
1463 if self.lost_focus() {
1465 match self.on_focus_lost {
1466 TextFocusLost::None => {}
1467 TextFocusLost::Position0 => {
1468 self.set_default_cursor();
1469 self.scroll_cursor_to_visible();
1470 }
1472 }
1473 }
1474 if self.gained_focus() {
1475 match self.on_focus_gained {
1476 TextFocusGained::None => {}
1477 TextFocusGained::Overwrite => {
1478 self.overwrite = true;
1479 }
1480 TextFocusGained::SelectAll => {
1481 self.select_all();
1482 }
1484 }
1485 }
1486
1487 let mut r = if self.is_focused() {
1488 match event {
1489 ct_event!(key press c)
1490 | ct_event!(key press SHIFT-c)
1491 | ct_event!(key press CONTROL_ALT-c) => {
1492 overwrite(self);
1493 tc(self.insert_char(*c))
1494 }
1495 ct_event!(keycode press Backspace) => {
1496 clear_overwrite(self);
1497 tc(self.delete_prev_char())
1498 }
1499 ct_event!(keycode press Delete) => {
1500 clear_overwrite(self);
1501 tc(self.delete_next_char())
1502 }
1503 ct_event!(keycode press CONTROL-Backspace)
1504 | ct_event!(keycode press ALT-Backspace) => {
1505 clear_overwrite(self);
1506 tc(self.delete_prev_section())
1507 }
1508 ct_event!(keycode press CONTROL-Delete) => {
1509 clear_overwrite(self);
1510 tc(self.delete_next_section())
1511 }
1512 ct_event!(key press CONTROL-'x') => {
1513 clear_overwrite(self);
1514 tc(self.cut_to_clip())
1515 }
1516 ct_event!(key press CONTROL-'v') => {
1517 clear_overwrite(self);
1518 tc(self.paste_from_clip())
1519 }
1520 ct_event!(key press CONTROL-'d') => {
1521 clear_overwrite(self);
1522 tc(self.clear())
1523 }
1524 ct_event!(key press CONTROL-'z') => {
1525 clear_overwrite(self);
1526 tc(self.undo())
1527 }
1528 ct_event!(key press CONTROL_SHIFT-'Z') => {
1529 clear_overwrite(self);
1530 tc(self.redo())
1531 }
1532
1533 ct_event!(key release _)
1534 | ct_event!(key release SHIFT-_)
1535 | ct_event!(key release CONTROL_ALT-_)
1536 | ct_event!(keycode release Backspace)
1537 | ct_event!(keycode release Delete)
1538 | ct_event!(keycode release CONTROL-Backspace)
1539 | ct_event!(keycode release ALT-Backspace)
1540 | ct_event!(keycode release CONTROL-Delete)
1541 | ct_event!(key release CONTROL-'x')
1542 | ct_event!(key release CONTROL-'v')
1543 | ct_event!(key release CONTROL-'d')
1544 | ct_event!(key release CONTROL-'z')
1545 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1546
1547 _ => TextOutcome::Continue,
1548 }
1549 } else {
1550 TextOutcome::Continue
1551 };
1552
1553 if r == TextOutcome::Continue {
1554 r = self.handle(event, ReadOnly);
1555 }
1556 r
1557 }
1558}
1559
1560impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for MaskedInputState {
1561 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1562 fn clear_overwrite(state: &mut MaskedInputState) {
1563 state.overwrite = false;
1564 }
1565
1566 let mut r = if self.is_focused() {
1567 match event {
1568 ct_event!(keycode press Left) => {
1569 clear_overwrite(self);
1570 self.move_left(false).into()
1571 }
1572 ct_event!(keycode press Right) => {
1573 clear_overwrite(self);
1574 self.move_right(false).into()
1575 }
1576 ct_event!(keycode press CONTROL-Left) => {
1577 clear_overwrite(self);
1578 self.move_to_prev_section(false).into()
1579 }
1580 ct_event!(keycode press CONTROL-Right) => {
1581 clear_overwrite(self);
1582 self.move_to_next_section(false).into()
1583 }
1584 ct_event!(keycode press Home) => {
1585 clear_overwrite(self);
1586 self.move_to_line_start(false).into()
1587 }
1588 ct_event!(keycode press End) => {
1589 clear_overwrite(self);
1590 self.move_to_line_end(false).into()
1591 }
1592 ct_event!(keycode press SHIFT-Left) => {
1593 clear_overwrite(self);
1594 self.move_left(true).into()
1595 }
1596 ct_event!(keycode press SHIFT-Right) => {
1597 clear_overwrite(self);
1598 self.move_right(true).into()
1599 }
1600 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1601 clear_overwrite(self);
1602 self.move_to_prev_section(true).into()
1603 }
1604 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1605 clear_overwrite(self);
1606 self.move_to_next_section(true).into()
1607 }
1608 ct_event!(keycode press SHIFT-Home) => {
1609 clear_overwrite(self);
1610 self.move_to_line_start(true).into()
1611 }
1612 ct_event!(keycode press SHIFT-End) => {
1613 clear_overwrite(self);
1614 self.move_to_line_end(true).into()
1615 }
1616 ct_event!(keycode press Tab) => {
1617 if !self.focus.gained() {
1619 clear_overwrite(self);
1620 self.select_next_section().into()
1621 } else {
1622 TextOutcome::Unchanged
1623 }
1624 }
1625 ct_event!(keycode press SHIFT-BackTab) => {
1626 if !self.focus.gained() {
1628 clear_overwrite(self);
1629 self.select_prev_section().into()
1630 } else {
1631 TextOutcome::Unchanged
1632 }
1633 }
1634 ct_event!(key press CONTROL-'a') => {
1635 clear_overwrite(self);
1636 self.select_all().into()
1637 }
1638 ct_event!(key press CONTROL-'c') => {
1639 clear_overwrite(self);
1640 self.copy_to_clip().into()
1641 }
1642
1643 ct_event!(keycode release Left)
1644 | ct_event!(keycode release Right)
1645 | ct_event!(keycode release CONTROL-Left)
1646 | ct_event!(keycode release CONTROL-Right)
1647 | ct_event!(keycode release Home)
1648 | ct_event!(keycode release End)
1649 | ct_event!(keycode release SHIFT-Left)
1650 | ct_event!(keycode release SHIFT-Right)
1651 | ct_event!(keycode release CONTROL_SHIFT-Left)
1652 | ct_event!(keycode release CONTROL_SHIFT-Right)
1653 | ct_event!(keycode release SHIFT-Home)
1654 | ct_event!(keycode release SHIFT-End)
1655 | ct_event!(key release CONTROL-'a')
1656 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1657
1658 _ => TextOutcome::Continue,
1659 }
1660 } else {
1661 TextOutcome::Continue
1662 };
1663
1664 if r == TextOutcome::Continue {
1665 r = self.handle(event, MouseOnly);
1666 }
1667 r
1668 }
1669}
1670
1671impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for MaskedInputState {
1672 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1673 fn clear_overwrite(state: &mut MaskedInputState) {
1674 state.overwrite = false;
1675 }
1676
1677 match event {
1678 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1679 let c = (m.column as i16) - (self.inner.x as i16);
1680 clear_overwrite(self);
1681 self.set_screen_cursor(c, true).into()
1682 }
1683 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1684 let cx = m.column as i16 - self.inner.x as i16;
1685 clear_overwrite(self);
1686 self.set_screen_cursor_sections(cx, true).into()
1687 }
1688 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1689 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1690 clear_overwrite(self);
1691 if let Some(range) = self.value.section_range(tx) {
1692 self.set_selection(range.start, range.end).into()
1693 } else {
1694 TextOutcome::Unchanged
1695 }
1696 }
1697 ct_event!(mouse down Left for column,row) => {
1698 if self.gained_focus() {
1699 TextOutcome::Unchanged
1702 } else if self.inner.contains((*column, *row).into()) {
1703 let c = (column - self.inner.x) as i16;
1704 clear_overwrite(self);
1705 self.set_screen_cursor(c, false).into()
1706 } else {
1707 TextOutcome::Continue
1708 }
1709 }
1710 ct_event!(mouse down CONTROL-Left for column,row) => {
1711 if self.inner.contains((*column, *row).into()) {
1712 let cx = (column - self.inner.x) as i16;
1713 clear_overwrite(self);
1714 self.set_screen_cursor(cx, true).into()
1715 } else {
1716 TextOutcome::Continue
1717 }
1718 }
1719 ct_event!(mouse down ALT-Left for column,row) => {
1720 if self.inner.contains((*column, *row).into()) {
1721 let cx = (column - self.inner.x) as i16;
1722 clear_overwrite(self);
1723 self.set_screen_cursor_sections(cx, true).into()
1724 } else {
1725 TextOutcome::Continue
1726 }
1727 }
1728 _ => TextOutcome::Continue,
1729 }
1730 }
1731}
1732
1733pub fn handle_events(
1737 state: &mut MaskedInputState,
1738 focus: bool,
1739 event: &crossterm::event::Event,
1740) -> TextOutcome {
1741 state.focus.set(focus);
1742 state.handle(event, Regular)
1743}
1744
1745pub fn handle_readonly_events(
1749 state: &mut TextInputState,
1750 focus: bool,
1751 event: &crossterm::event::Event,
1752) -> TextOutcome {
1753 state.focus.set(focus);
1754 state.handle(event, ReadOnly)
1755}
1756
1757pub fn handle_mouse_events(
1759 state: &mut MaskedInputState,
1760 event: &crossterm::event::Event,
1761) -> TextOutcome {
1762 state.handle(event, MouseOnly)
1763}