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};
92use ratatui::widgets::{Block, StatefulWidget, Widget};
93use std::borrow::Cow;
94use std::cmp::min;
95use std::fmt;
96use std::ops::Range;
97
98#[derive(Debug, Default, Clone)]
104pub struct MaskedInput<'a> {
105 compact: bool,
106 block: Option<Block<'a>>,
107 style: Style,
108 focus_style: Option<Style>,
109 select_style: Option<Style>,
110 invalid_style: Option<Style>,
111 text_style: Vec<Style>,
112 on_focus_gained: TextFocusGained,
113 on_focus_lost: TextFocusLost,
114}
115
116#[derive(Debug)]
118pub struct MaskedInputState {
119 pub area: Rect,
122 pub inner: Rect,
125
126 pub offset: upos_type,
129 pub dark_offset: (u16, u16),
132 pub scroll_to_cursor: bool,
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
278impl<'a> StatefulWidget for &MaskedInput<'a> {
279 type State = MaskedInputState;
280
281 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
282 render_ref(self, area, buf, state);
283 }
284}
285
286impl StatefulWidget for MaskedInput<'_> {
287 type State = MaskedInputState;
288
289 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
290 render_ref(&self, area, buf, state);
291 }
292}
293
294fn render_ref(
295 widget: &MaskedInput<'_>,
296 area: Rect,
297
298 buf: &mut Buffer,
299 state: &mut MaskedInputState,
300) {
301 state.area = area;
302 state.inner = widget.block.inner_if_some(area);
303 state.on_focus_gained = widget.on_focus_gained;
304 state.on_focus_lost = widget.on_focus_lost;
305
306 if state.scroll_to_cursor {
307 let c = state.cursor();
308 let o = state.offset();
309 let mut no = if c < o {
310 c
311 } else if c >= o + (state.inner.width + state.dark_offset.0) as upos_type {
312 c.saturating_sub((state.inner.width + state.dark_offset.0) as upos_type)
313 } else {
314 o
315 };
316 if c == no + (state.inner.width + state.dark_offset.0) as upos_type {
319 no = no.saturating_add(1);
320 }
321 state.set_offset(no);
322 }
323
324 let inner = state.inner;
325
326 let style = widget.style;
327 let focus_style = if let Some(focus_style) = widget.focus_style {
328 focus_style
329 } else {
330 style
331 };
332 let select_style = if let Some(select_style) = widget.select_style {
333 select_style
334 } else {
335 Style::default().black().on_yellow()
336 };
337 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
338 invalid_style
339 } else {
340 Style::default().red()
341 };
342
343 let (style, select_style) = if state.focus.get() {
344 if state.invalid {
345 (
346 style.patch(focus_style).patch(invalid_style),
347 style
348 .patch(focus_style)
349 .patch(select_style)
350 .patch(invalid_style),
351 )
352 } else {
353 (
354 style.patch(focus_style),
355 style.patch(focus_style).patch(select_style),
356 )
357 }
358 } else {
359 if state.invalid {
360 (style.patch(invalid_style), style.patch(invalid_style))
361 } else {
362 (style, style)
363 }
364 };
365
366 if let Some(block) = &widget.block {
368 block.render(area, buf);
369 } else {
370 buf.set_style(area, style);
371 }
372
373 if inner.width == 0 || inner.height == 0 {
374 return;
376 }
377
378 let ox = state.offset() as u16;
379 let show_range = {
381 let start = ox as upos_type;
382 let end = min(start + inner.width as upos_type, state.len());
383 state.bytes_at_range(start..end)
384 };
385 let selection = state.selection();
386 let mut styles = Vec::new();
387
388 let mut glyph_iter_regular;
389 let mut glyph_iter_cond;
390 let glyph_iter: &mut dyn Iterator<Item = Glyph<'_>>;
391 if state.is_focused() || !widget.compact {
392 glyph_iter_regular = state
393 .value
394 .glyphs(0..1, ox, inner.width)
395 .expect("valid_offset");
396 glyph_iter = &mut glyph_iter_regular;
397 } else {
398 glyph_iter_cond = state
399 .value
400 .condensed_glyphs(0..1, ox, inner.width)
401 .expect("valid_offset");
402 glyph_iter = &mut glyph_iter_cond;
403 }
404
405 for g in glyph_iter {
406 if g.screen_width() > 0 {
407 let mut style = style;
408 styles.clear();
409 state
410 .value
411 .styles_at_page(show_range.clone(), g.text_bytes().start, &mut styles);
412 for style_nr in &styles {
413 if let Some(s) = widget.text_style.get(*style_nr) {
414 style = style.patch(*s);
415 }
416 }
417 if selection.contains(&g.pos().x) {
419 style = style.patch(select_style);
420 };
421
422 let screen_pos = g.screen_pos();
424
425 if let Some(cell) = buf.cell_mut((inner.x + screen_pos.0, inner.y + screen_pos.1)) {
427 cell.set_symbol(g.glyph());
428 cell.set_style(style);
429 }
430 for d in 1..g.screen_width() {
432 if let Some(cell) =
433 buf.cell_mut((inner.x + screen_pos.0 + d, inner.y + screen_pos.1))
434 {
435 cell.reset();
436 cell.set_style(style);
437 }
438 }
439 }
440 }
441}
442
443impl Clone for MaskedInputState {
444 fn clone(&self) -> Self {
445 Self {
446 area: self.area,
447 inner: self.inner,
448 offset: self.offset,
449 dark_offset: self.dark_offset,
450 scroll_to_cursor: self.scroll_to_cursor,
451 value: self.value.clone(),
452 invalid: self.invalid,
453 overwrite: Default::default(),
454 on_focus_gained: Default::default(),
455 on_focus_lost: Default::default(),
456 focus: FocusFlag::named(self.focus.name()),
457 mouse: Default::default(),
458 non_exhaustive: NonExhaustive,
459 }
460 }
461}
462
463impl Default for MaskedInputState {
464 fn default() -> Self {
465 Self {
466 area: Default::default(),
467 inner: Default::default(),
468 offset: Default::default(),
469 dark_offset: Default::default(),
470 scroll_to_cursor: Default::default(),
471 value: Default::default(),
472 invalid: Default::default(),
473 overwrite: Default::default(),
474 on_focus_gained: Default::default(),
475 on_focus_lost: Default::default(),
476 focus: Default::default(),
477 mouse: Default::default(),
478 non_exhaustive: NonExhaustive,
479 }
480 }
481}
482
483impl HasFocus for MaskedInputState {
484 fn build(&self, builder: &mut FocusBuilder) {
485 builder.leaf_widget(self);
486 }
487
488 fn focus(&self) -> FocusFlag {
489 self.focus.clone()
490 }
491
492 fn area(&self) -> Rect {
493 self.area
494 }
495
496 fn navigable(&self) -> Navigation {
497 let sel = self.selection();
498
499 let has_next = self
500 .value
501 .next_section_range(sel.end)
502 .map(|v| !v.is_empty())
503 .is_some();
504 let has_prev = self
505 .value
506 .prev_section_range(sel.start.saturating_sub(1))
507 .map(|v| !v.is_empty())
508 .is_some();
509
510 if has_next {
511 if has_prev {
512 Navigation::Reach
513 } else {
514 Navigation::ReachLeaveFront
515 }
516 } else {
517 if has_prev {
518 Navigation::ReachLeaveBack
519 } else {
520 Navigation::Regular
521 }
522 }
523 }
524}
525
526impl MaskedInputState {
527 pub fn new() -> Self {
528 Self::default()
529 }
530
531 pub fn named(name: &str) -> Self {
532 Self {
533 focus: FocusFlag::named(name),
534 ..MaskedInputState::default()
535 }
536 }
537
538 #[inline]
540 pub fn with_symbols(mut self, sym: NumberSymbols) -> Self {
541 self.set_num_symbols(sym);
542 self
543 }
544
545 pub fn with_mask<S: AsRef<str>>(mut self, mask: S) -> Result<Self, fmt::Error> {
547 self.value.set_mask(mask.as_ref())?;
548 Ok(self)
549 }
550
551 #[inline]
556 pub fn set_num_symbols(&mut self, sym: NumberSymbols) {
557 self.value.set_num_symbols(sym);
558 }
559
560 #[inline]
591 pub fn set_mask<S: AsRef<str>>(&mut self, s: S) -> Result<(), fmt::Error> {
592 self.value.set_mask(s)
593 }
594
595 #[inline]
597 pub fn mask(&self) -> String {
598 self.value.mask()
599 }
600
601 #[inline]
603 pub fn set_invalid(&mut self, invalid: bool) {
604 self.invalid = invalid;
605 }
606
607 #[inline]
609 pub fn get_invalid(&self) -> bool {
610 self.invalid
611 }
612
613 #[inline]
617 pub fn set_overwrite(&mut self, overwrite: bool) {
618 self.overwrite = overwrite;
619 }
620
621 #[inline]
623 pub fn overwrite(&self) -> bool {
624 self.overwrite
625 }
626}
627
628impl MaskedInputState {
629 #[inline]
632 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
633 match clip {
634 None => self.value.set_clipboard(None),
635 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
636 }
637 }
638
639 #[inline]
642 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
643 self.value.clipboard()
644 }
645
646 #[inline]
648 pub fn copy_to_clip(&mut self) -> bool {
649 let Some(clip) = self.value.clipboard() else {
650 return false;
651 };
652
653 _ = clip.set_string(self.selected_text().as_ref());
654
655 true
656 }
657
658 #[inline]
660 pub fn cut_to_clip(&mut self) -> bool {
661 let Some(clip) = self.value.clipboard() else {
662 return false;
663 };
664
665 match clip.set_string(self.selected_text().as_ref()) {
666 Ok(_) => self.delete_range(self.selection()),
667 Err(_) => true,
668 }
669 }
670
671 #[inline]
673 pub fn paste_from_clip(&mut self) -> bool {
674 let Some(clip) = self.value.clipboard() else {
675 return false;
676 };
677
678 if let Ok(text) = clip.get_string() {
679 for c in text.chars() {
680 self.insert_char(c);
681 }
682 true
683 } else {
684 false
685 }
686 }
687}
688
689impl MaskedInputState {
690 #[inline]
692 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
693 match undo {
694 None => self.value.set_undo_buffer(None),
695 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
696 }
697 }
698
699 #[inline]
701 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
702 self.value.undo_buffer()
703 }
704
705 #[inline]
707 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
708 self.value.undo_buffer_mut()
709 }
710
711 #[inline]
713 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
714 self.value.recent_replay_log()
715 }
716
717 #[inline]
719 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
720 self.value.replay_log(replay)
721 }
722
723 #[inline]
725 pub fn undo(&mut self) -> bool {
726 self.value.undo()
727 }
728
729 #[inline]
731 pub fn redo(&mut self) -> bool {
732 self.value.redo()
733 }
734}
735
736impl MaskedInputState {
737 #[inline]
739 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
740 self.value.set_styles(styles);
741 }
742
743 #[inline]
746 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
747 self.value.add_style(range, style);
748 }
749
750 #[inline]
753 pub fn add_range_style(
754 &mut self,
755 range: Range<upos_type>,
756 style: usize,
757 ) -> Result<(), TextError> {
758 let r = self.value.bytes_at_range(range)?;
759 self.value.add_style(r, style);
760 Ok(())
761 }
762
763 #[inline]
765 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
766 self.value.remove_style(range, style);
767 }
768
769 #[inline]
771 pub fn remove_range_style(
772 &mut self,
773 range: Range<upos_type>,
774 style: usize,
775 ) -> Result<(), TextError> {
776 let r = self.value.bytes_at_range(range)?;
777 self.value.remove_style(r, style);
778 Ok(())
779 }
780
781 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
783 self.value.styles_in(range, buf)
784 }
785
786 #[inline]
788 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
789 self.value.styles_at(byte_pos, buf)
790 }
791
792 #[inline]
795 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
796 self.value.style_match(byte_pos, style)
797 }
798
799 #[inline]
801 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
802 self.value.styles()
803 }
804}
805
806impl MaskedInputState {
807 #[inline]
809 pub fn offset(&self) -> upos_type {
810 self.offset
811 }
812
813 #[inline]
815 pub fn set_offset(&mut self, offset: upos_type) {
816 self.scroll_to_cursor = false;
817 self.offset = offset;
818 }
819
820 #[inline]
822 pub fn cursor(&self) -> upos_type {
823 self.value.cursor()
824 }
825
826 #[inline]
829 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
830 self.scroll_cursor_to_visible();
831 self.value.set_cursor(cursor, extend_selection)
832 }
833
834 #[inline]
838 pub fn set_default_cursor(&mut self) {
839 self.scroll_cursor_to_visible();
840 self.value.set_default_cursor();
841 }
842
843 #[inline]
845 pub fn anchor(&self) -> upos_type {
846 self.value.anchor()
847 }
848
849 #[inline]
851 pub fn has_selection(&self) -> bool {
852 self.value.has_selection()
853 }
854
855 #[inline]
857 pub fn selection(&self) -> Range<upos_type> {
858 self.value.selection()
859 }
860
861 #[inline]
864 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
865 self.scroll_cursor_to_visible();
866 self.value.set_selection(anchor, cursor)
867 }
868
869 #[inline]
872 pub fn select_all(&mut self) -> bool {
873 self.scroll_cursor_to_visible();
874 if let Some(section) = self.value.section_range(self.cursor()) {
875 if self.selection() == section {
876 self.value.select_all()
877 } else {
878 self.value.set_selection(section.start, section.end)
879 }
880 } else {
881 self.value.select_all()
882 }
883 }
884
885 #[inline]
887 pub fn selected_text(&self) -> &str {
888 self.value.selected_text()
889 }
890}
891
892impl MaskedInputState {
893 #[inline]
895 pub fn is_empty(&self) -> bool {
896 self.value.is_empty()
897 }
898
899 #[inline]
901 pub fn text(&self) -> &str {
902 self.value.text()
903 }
904
905 #[inline]
907 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
908 self.value.str_slice_byte(range).expect("valid_range")
909 }
910
911 #[inline]
913 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
914 self.value.str_slice_byte(range)
915 }
916
917 #[inline]
919 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
920 self.value.str_slice(range).expect("valid_range")
921 }
922
923 #[inline]
925 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
926 self.value.str_slice(range)
927 }
928
929 #[inline]
931 pub fn len(&self) -> upos_type {
932 self.value.line_width()
933 }
934
935 #[inline]
937 pub fn line_width(&self) -> upos_type {
938 self.value.line_width()
939 }
940
941 #[inline]
944 pub fn glyphs(&self, screen_offset: u16, screen_width: u16) -> impl Iterator<Item = Glyph<'_>> {
945 self.value
946 .glyphs(0..1, screen_offset, screen_width)
947 .expect("valid_row")
948 }
949
950 #[inline]
953 pub fn condensed_glyphs(
954 &self,
955 screen_offset: u16,
956 screen_width: u16,
957 ) -> impl Iterator<Item = Glyph<'_>> {
958 self.value
959 .condensed_glyphs(0..1, screen_offset, screen_width)
960 .expect("valid_row")
961 }
962
963 #[inline]
965 pub fn text_graphemes(&self, pos: upos_type) -> impl Cursor<Item = Grapheme<'_>> {
966 self.value.text_graphemes(pos).expect("valid_pos")
967 }
968
969 #[inline]
971 pub fn try_text_graphemes(
972 &self,
973 pos: upos_type,
974 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
975 self.value.text_graphemes(pos)
976 }
977
978 #[inline]
980 pub fn graphemes(
981 &self,
982 range: Range<upos_type>,
983 pos: upos_type,
984 ) -> impl Cursor<Item = Grapheme<'_>> {
985 self.value.graphemes(range, pos).expect("valid_args")
986 }
987
988 #[inline]
990 pub fn try_graphemes(
991 &self,
992 range: Range<upos_type>,
993 pos: upos_type,
994 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
995 self.value.graphemes(range, pos)
996 }
997
998 #[inline]
1001 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
1002 self.value.byte_at(pos).expect("valid_pos")
1003 }
1004
1005 #[inline]
1008 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
1009 self.value.byte_at(pos)
1010 }
1011
1012 #[inline]
1014 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
1015 self.value.bytes_at_range(range).expect("valid_range")
1016 }
1017
1018 #[inline]
1020 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
1021 self.value.bytes_at_range(range)
1022 }
1023
1024 #[inline]
1027 pub fn byte_pos(&self, byte: usize) -> upos_type {
1028 self.value.byte_pos(byte).expect("valid_pos")
1029 }
1030
1031 #[inline]
1034 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
1035 self.value.byte_pos(byte)
1036 }
1037
1038 #[inline]
1040 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
1041 self.value.byte_range(bytes).expect("valid_range")
1042 }
1043
1044 #[inline]
1046 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
1047 self.value.byte_range(bytes)
1048 }
1049}
1050
1051impl MaskedInputState {
1052 #[inline]
1054 pub fn clear(&mut self) -> bool {
1055 if self.is_empty() {
1056 false
1057 } else {
1058 self.offset = 0;
1059 self.value.clear();
1060 true
1061 }
1062 }
1063
1064 #[inline]
1070 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1071 self.offset = 0;
1072 self.value.set_text(s);
1073 self.value.set_default_cursor();
1074 }
1075
1076 #[inline]
1078 pub fn insert_char(&mut self, c: char) -> bool {
1079 self.value.begin_undo_seq();
1080 if self.value.has_selection() {
1081 let sel = self.value.selection();
1082 self.value
1083 .remove_range(sel.clone())
1084 .expect("valid_selection");
1085 self.value.set_cursor(sel.start, false);
1086 }
1087 let c0 = self.value.advance_cursor(c);
1088 let c1 = self.value.insert_char(c);
1089 self.value.end_undo_seq();
1090
1091 self.scroll_cursor_to_visible();
1092 c0 || c1
1093 }
1094
1095 #[inline]
1098 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1099 self.try_delete_range(range).expect("valid_range")
1100 }
1101
1102 #[inline]
1105 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1106 self.value.begin_undo_seq();
1107 let r = self.value.remove_range(range.clone())?;
1108 if let Some(pos) = self.value.section_cursor(range.start) {
1109 self.value.set_cursor(pos, false);
1110 }
1111 self.value.end_undo_seq();
1112
1113 self.scroll_cursor_to_visible();
1114 Ok(r)
1115 }
1116}
1117
1118impl MaskedInputState {
1119 #[inline]
1121 pub fn delete_next_char(&mut self) -> bool {
1122 if self.has_selection() {
1123 self.delete_range(self.selection())
1124 } else if self.cursor() == self.len() {
1125 false
1126 } else {
1127 self.value.remove_next();
1128 self.scroll_cursor_to_visible();
1129 true
1130 }
1131 }
1132
1133 #[inline]
1135 pub fn delete_prev_char(&mut self) -> bool {
1136 if self.has_selection() {
1137 self.delete_range(self.selection())
1138 } else if self.cursor() == 0 {
1139 false
1140 } else {
1141 self.value.remove_prev();
1142 self.scroll_cursor_to_visible();
1143 true
1144 }
1145 }
1146
1147 #[inline]
1149 pub fn delete_prev_section(&mut self) -> bool {
1150 if self.has_selection() {
1151 self.delete_range(self.selection())
1152 } else {
1153 if let Some(range) = self.value.prev_section_range(self.cursor()) {
1154 self.delete_range(range)
1155 } else {
1156 false
1157 }
1158 }
1159 }
1160
1161 #[inline]
1163 pub fn delete_next_section(&mut self) -> bool {
1164 if self.has_selection() {
1165 self.delete_range(self.selection())
1166 } else {
1167 if let Some(range) = self.value.next_section_range(self.cursor()) {
1168 self.delete_range(range)
1169 } else {
1170 false
1171 }
1172 }
1173 }
1174
1175 #[inline]
1177 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1178 let c = min(self.cursor() + 1, self.len());
1179 self.set_cursor(c, extend_selection)
1180 }
1181
1182 #[inline]
1184 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1185 let c = self.cursor().saturating_sub(1);
1186 self.set_cursor(c, extend_selection)
1187 }
1188
1189 #[inline]
1191 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1192 if let Some(c) = self.value.section_cursor(self.cursor()) {
1193 if c != self.cursor() {
1194 self.set_cursor(c, extend_selection)
1195 } else {
1196 self.set_cursor(0, extend_selection)
1197 }
1198 } else {
1199 self.set_cursor(0, extend_selection)
1200 }
1201 }
1202
1203 #[inline]
1205 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1206 self.set_cursor(self.len(), extend_selection)
1207 }
1208
1209 #[inline]
1211 pub fn move_to_prev_section(&mut self, extend_selection: bool) -> bool {
1212 if let Some(curr) = self.value.section_range(self.cursor()) {
1213 if self.value.cursor() != curr.start {
1214 return self.value.set_cursor(curr.start, extend_selection);
1215 }
1216 }
1217 if let Some(range) = self.value.prev_section_range(self.cursor()) {
1218 self.value.set_cursor(range.start, extend_selection)
1219 } else {
1220 false
1221 }
1222 }
1223
1224 #[inline]
1226 pub fn move_to_next_section(&mut self, extend_selection: bool) -> bool {
1227 if let Some(curr) = self.value.section_range(self.cursor()) {
1228 if self.value.cursor() != curr.end {
1229 return self.value.set_cursor(curr.end, extend_selection);
1230 }
1231 }
1232 if let Some(range) = self.value.next_section_range(self.cursor()) {
1233 self.value.set_cursor(range.end, extend_selection)
1234 } else {
1235 false
1236 }
1237 }
1238
1239 #[inline]
1241 pub fn select_current_section(&mut self) -> bool {
1242 let selection = self.selection();
1243
1244 if let Some(next) = self.value.section_range(selection.start.saturating_sub(1)) {
1245 if !next.is_empty() {
1246 self.set_selection(next.start, next.end)
1247 } else {
1248 false
1249 }
1250 } else {
1251 false
1252 }
1253 }
1254
1255 #[inline]
1257 pub fn select_next_section(&mut self) -> bool {
1258 let selection = self.selection();
1259
1260 if let Some(next) = self.value.next_section_range(selection.start) {
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 #[inline]
1273 pub fn select_prev_section(&mut self) -> bool {
1274 let selection = self.selection();
1275
1276 if let Some(next) = self
1277 .value
1278 .prev_section_range(selection.start.saturating_sub(1))
1279 {
1280 if !next.is_empty() {
1281 self.set_selection(next.start, next.end)
1282 } else {
1283 false
1284 }
1285 } else {
1286 false
1287 }
1288 }
1289}
1290
1291impl HasScreenCursor for MaskedInputState {
1292 #[inline]
1294 fn screen_cursor(&self) -> Option<(u16, u16)> {
1295 if self.is_focused() {
1296 if self.has_selection() {
1297 None
1298 } else {
1299 let cx = self.cursor();
1300 let ox = self.offset();
1301
1302 if cx < ox {
1303 None
1304 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1305 None
1306 } else {
1307 self.col_to_screen(cx)
1308 .map(|sc| (self.inner.x + sc, self.inner.y))
1309 }
1310 }
1311 } else {
1312 None
1313 }
1314 }
1315}
1316
1317impl RelocatableState for MaskedInputState {
1318 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1319 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1321 self.area = relocate_area(self.area, shift, clip);
1322 self.inner = relocate_area(self.inner, shift, clip);
1323 }
1324}
1325
1326impl MaskedInputState {
1327 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1330 let ox = self.offset();
1331
1332 let scx = scx + self.dark_offset.0 as i16;
1333
1334 if scx < 0 {
1335 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1336 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
1337 min(ox + scx as upos_type, self.len())
1338 } else {
1339 let scx = scx as u16;
1340
1341 let line = self.glyphs(ox as u16, self.inner.width + self.dark_offset.0);
1342
1343 let mut col = ox;
1344 for g in line {
1345 if scx < g.screen_pos().0 + g.screen_width() {
1346 break;
1347 }
1348 col = g.pos().x + 1;
1349 }
1350 col
1351 }
1352 }
1353
1354 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1357 let ox = self.offset();
1358
1359 if pos < ox {
1360 return None;
1361 }
1362
1363 let line = self.glyphs(ox as u16, self.inner.width + self.dark_offset.0);
1364 let mut screen_x = 0;
1365 for g in line {
1366 if g.pos().x == pos {
1367 break;
1368 }
1369 screen_x = g.screen_pos().0 + g.screen_width();
1370 }
1371
1372 if screen_x >= self.dark_offset.0 {
1373 Some(screen_x - self.dark_offset.0)
1374 } else {
1375 None
1376 }
1377 }
1378
1379 #[inline]
1383 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1384 let scx = cursor;
1385
1386 let cx = self.screen_to_col(scx);
1387
1388 self.set_cursor(cx, extend_selection)
1389 }
1390
1391 pub fn set_screen_cursor_sections(
1398 &mut self,
1399 screen_cursor: i16,
1400 extend_selection: bool,
1401 ) -> bool {
1402 let anchor = self.anchor();
1403 let cursor = self.screen_to_col(screen_cursor);
1404
1405 let Some(range) = self.value.section_range(cursor) else {
1406 return false;
1407 };
1408
1409 let cursor = if cursor < anchor {
1410 range.start
1411 } else {
1412 range.end
1413 };
1414
1415 if !self.value.is_section_boundary(anchor) {
1417 if let Some(range) = self.value.section_range(anchor) {
1418 if cursor < anchor {
1419 self.set_cursor(range.end, false);
1420 } else {
1421 self.set_cursor(range.start, false);
1422 }
1423 };
1424 }
1425
1426 self.set_cursor(cursor, extend_selection)
1427 }
1428
1429 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1431 self.set_offset(self.offset.saturating_sub(delta));
1432 true
1433 }
1434
1435 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1437 self.set_offset(self.offset + delta);
1438 true
1439 }
1440
1441 pub fn scroll_cursor_to_visible(&mut self) {
1443 self.scroll_to_cursor = true;
1444 }
1445}
1446
1447impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for MaskedInputState {
1448 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1449 fn tc(r: bool) -> TextOutcome {
1451 if r {
1452 TextOutcome::TextChanged
1453 } else {
1454 TextOutcome::Unchanged
1455 }
1456 }
1457 fn overwrite(state: &mut MaskedInputState) {
1458 if state.overwrite {
1459 state.overwrite = false;
1460 state.clear();
1461 }
1462 }
1463 fn clear_overwrite(state: &mut MaskedInputState) {
1464 state.overwrite = false;
1465 }
1466
1467 if self.lost_focus() {
1469 match self.on_focus_lost {
1470 TextFocusLost::None => {}
1471 TextFocusLost::Position0 => {
1472 self.set_default_cursor();
1473 self.scroll_cursor_to_visible();
1474 }
1476 }
1477 }
1478 if self.gained_focus() {
1479 match self.on_focus_gained {
1480 TextFocusGained::None => {}
1481 TextFocusGained::Overwrite => {
1482 self.overwrite = true;
1483 }
1484 TextFocusGained::SelectAll => {
1485 self.select_all();
1486 }
1488 }
1489 }
1490
1491 let mut r = if self.is_focused() {
1492 match event {
1493 ct_event!(key press c)
1494 | ct_event!(key press SHIFT-c)
1495 | ct_event!(key press CONTROL_ALT-c) => {
1496 overwrite(self);
1497 tc(self.insert_char(*c))
1498 }
1499 ct_event!(keycode press Backspace) => {
1500 clear_overwrite(self);
1501 tc(self.delete_prev_char())
1502 }
1503 ct_event!(keycode press Delete) => {
1504 clear_overwrite(self);
1505 tc(self.delete_next_char())
1506 }
1507 ct_event!(keycode press CONTROL-Backspace)
1508 | ct_event!(keycode press ALT-Backspace) => {
1509 clear_overwrite(self);
1510 tc(self.delete_prev_section())
1511 }
1512 ct_event!(keycode press CONTROL-Delete) => {
1513 clear_overwrite(self);
1514 tc(self.delete_next_section())
1515 }
1516 ct_event!(key press CONTROL-'x') => {
1517 clear_overwrite(self);
1518 tc(self.cut_to_clip())
1519 }
1520 ct_event!(key press CONTROL-'v') => {
1521 clear_overwrite(self);
1522 tc(self.paste_from_clip())
1523 }
1524 ct_event!(key press CONTROL-'d') => {
1525 clear_overwrite(self);
1526 tc(self.clear())
1527 }
1528 ct_event!(key press CONTROL-'z') => {
1529 clear_overwrite(self);
1530 tc(self.undo())
1531 }
1532 ct_event!(key press CONTROL_SHIFT-'Z') => {
1533 clear_overwrite(self);
1534 tc(self.redo())
1535 }
1536
1537 ct_event!(key release _)
1538 | ct_event!(key release SHIFT-_)
1539 | ct_event!(key release CONTROL_ALT-_)
1540 | ct_event!(keycode release Backspace)
1541 | ct_event!(keycode release Delete)
1542 | ct_event!(keycode release CONTROL-Backspace)
1543 | ct_event!(keycode release ALT-Backspace)
1544 | ct_event!(keycode release CONTROL-Delete)
1545 | ct_event!(key release CONTROL-'x')
1546 | ct_event!(key release CONTROL-'v')
1547 | ct_event!(key release CONTROL-'d')
1548 | ct_event!(key release CONTROL-'z')
1549 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1550
1551 _ => TextOutcome::Continue,
1552 }
1553 } else {
1554 TextOutcome::Continue
1555 };
1556
1557 if r == TextOutcome::Continue {
1558 r = self.handle(event, ReadOnly);
1559 }
1560 r
1561 }
1562}
1563
1564impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for MaskedInputState {
1565 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1566 fn clear_overwrite(state: &mut MaskedInputState) {
1567 state.overwrite = false;
1568 }
1569
1570 let mut r = if self.is_focused() {
1571 match event {
1572 ct_event!(keycode press Left) => {
1573 clear_overwrite(self);
1574 self.move_left(false).into()
1575 }
1576 ct_event!(keycode press Right) => {
1577 clear_overwrite(self);
1578 self.move_right(false).into()
1579 }
1580 ct_event!(keycode press CONTROL-Left) => {
1581 clear_overwrite(self);
1582 self.move_to_prev_section(false).into()
1583 }
1584 ct_event!(keycode press CONTROL-Right) => {
1585 clear_overwrite(self);
1586 self.move_to_next_section(false).into()
1587 }
1588 ct_event!(keycode press Home) => {
1589 clear_overwrite(self);
1590 self.move_to_line_start(false).into()
1591 }
1592 ct_event!(keycode press End) => {
1593 clear_overwrite(self);
1594 self.move_to_line_end(false).into()
1595 }
1596 ct_event!(keycode press SHIFT-Left) => {
1597 clear_overwrite(self);
1598 self.move_left(true).into()
1599 }
1600 ct_event!(keycode press SHIFT-Right) => {
1601 clear_overwrite(self);
1602 self.move_right(true).into()
1603 }
1604 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1605 clear_overwrite(self);
1606 self.move_to_prev_section(true).into()
1607 }
1608 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1609 clear_overwrite(self);
1610 self.move_to_next_section(true).into()
1611 }
1612 ct_event!(keycode press SHIFT-Home) => {
1613 clear_overwrite(self);
1614 self.move_to_line_start(true).into()
1615 }
1616 ct_event!(keycode press SHIFT-End) => {
1617 clear_overwrite(self);
1618 self.move_to_line_end(true).into()
1619 }
1620 ct_event!(keycode press Tab) => {
1621 if !self.focus.gained() {
1623 clear_overwrite(self);
1624 self.select_next_section().into()
1625 } else {
1626 TextOutcome::Unchanged
1627 }
1628 }
1629 ct_event!(keycode press SHIFT-BackTab) => {
1630 if !self.focus.gained() {
1632 clear_overwrite(self);
1633 self.select_prev_section().into()
1634 } else {
1635 TextOutcome::Unchanged
1636 }
1637 }
1638 ct_event!(key press CONTROL-'a') => {
1639 clear_overwrite(self);
1640 self.select_all().into()
1641 }
1642 ct_event!(key press CONTROL-'c') => {
1643 clear_overwrite(self);
1644 self.copy_to_clip().into()
1645 }
1646
1647 ct_event!(keycode release Left)
1648 | ct_event!(keycode release Right)
1649 | ct_event!(keycode release CONTROL-Left)
1650 | ct_event!(keycode release CONTROL-Right)
1651 | ct_event!(keycode release Home)
1652 | ct_event!(keycode release End)
1653 | ct_event!(keycode release SHIFT-Left)
1654 | ct_event!(keycode release SHIFT-Right)
1655 | ct_event!(keycode release CONTROL_SHIFT-Left)
1656 | ct_event!(keycode release CONTROL_SHIFT-Right)
1657 | ct_event!(keycode release SHIFT-Home)
1658 | ct_event!(keycode release SHIFT-End)
1659 | ct_event!(key release CONTROL-'a')
1660 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1661
1662 _ => TextOutcome::Continue,
1663 }
1664 } else {
1665 TextOutcome::Continue
1666 };
1667
1668 if r == TextOutcome::Continue {
1669 r = self.handle(event, MouseOnly);
1670 }
1671 r
1672 }
1673}
1674
1675impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for MaskedInputState {
1676 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1677 fn clear_overwrite(state: &mut MaskedInputState) {
1678 state.overwrite = false;
1679 }
1680
1681 match event {
1682 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1683 let c = (m.column as i16) - (self.inner.x as i16);
1684 clear_overwrite(self);
1685 self.set_screen_cursor(c, true).into()
1686 }
1687 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1688 let cx = m.column as i16 - self.inner.x as i16;
1689 clear_overwrite(self);
1690 self.set_screen_cursor_sections(cx, true).into()
1691 }
1692 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1693 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1694 clear_overwrite(self);
1695 if let Some(range) = self.value.section_range(tx) {
1696 self.set_selection(range.start, range.end).into()
1697 } else {
1698 TextOutcome::Unchanged
1699 }
1700 }
1701 ct_event!(mouse down Left for column,row) => {
1702 if self.gained_focus() {
1703 TextOutcome::Unchanged
1706 } else if self.inner.contains((*column, *row).into()) {
1707 let c = (column - self.inner.x) as i16;
1708 clear_overwrite(self);
1709 self.set_screen_cursor(c, false).into()
1710 } else {
1711 TextOutcome::Continue
1712 }
1713 }
1714 ct_event!(mouse down CONTROL-Left for column,row) => {
1715 if self.inner.contains((*column, *row).into()) {
1716 let cx = (column - self.inner.x) as i16;
1717 clear_overwrite(self);
1718 self.set_screen_cursor(cx, true).into()
1719 } else {
1720 TextOutcome::Continue
1721 }
1722 }
1723 ct_event!(mouse down ALT-Left for column,row) => {
1724 if self.inner.contains((*column, *row).into()) {
1725 let cx = (column - self.inner.x) as i16;
1726 clear_overwrite(self);
1727 self.set_screen_cursor_sections(cx, true).into()
1728 } else {
1729 TextOutcome::Continue
1730 }
1731 }
1732 _ => TextOutcome::Continue,
1733 }
1734 }
1735}
1736
1737pub fn handle_events(
1741 state: &mut MaskedInputState,
1742 focus: bool,
1743 event: &crossterm::event::Event,
1744) -> TextOutcome {
1745 state.focus.set(focus);
1746 state.handle(event, Regular)
1747}
1748
1749pub fn handle_readonly_events(
1753 state: &mut TextInputState,
1754 focus: bool,
1755 event: &crossterm::event::Event,
1756) -> TextOutcome {
1757 state.focus.set(focus);
1758 state.handle(event, ReadOnly)
1759}
1760
1761pub fn handle_mouse_events(
1763 state: &mut MaskedInputState,
1764 event: &crossterm::event::Event,
1765) -> TextOutcome {
1766 state.handle(event, MouseOnly)
1767}