1use crate::_private::NonExhaustive;
73use crate::clipboard::Clipboard;
74use crate::event::{ReadOnly, TextOutcome};
75use crate::glyph2::Glyph2;
76use crate::text_input::TextInputState;
77use crate::text_mask_core::MaskedCore;
78use crate::undo_buffer::{UndoBuffer, UndoEntry};
79#[allow(deprecated)]
80use crate::Glyph;
81use crate::{
82 ipos_type, upos_type, Cursor, Grapheme, HasScreenCursor, TextError, TextFocusGained,
83 TextFocusLost, TextStyle,
84};
85use crossterm::event::KeyModifiers;
86use format_num_pattern::NumberSymbols;
87use rat_event::util::MouseFlags;
88use rat_event::{ct_event, HandleEvent, MouseOnly, Regular};
89use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
90use rat_reloc::{relocate_area, relocate_dark_offset, RelocatableState};
91use ratatui::buffer::Buffer;
92use ratatui::layout::{Rect, Size};
93use ratatui::prelude::BlockExt;
94use ratatui::style::{Style, Stylize};
95use ratatui::widgets::{Block, StatefulWidget, Widget};
96use std::borrow::Cow;
97use std::cmp::min;
98use std::fmt;
99use std::ops::Range;
100
101#[derive(Debug, Default, Clone)]
107pub struct MaskedInput<'a> {
108 compact: bool,
109 block: Option<Block<'a>>,
110 style: Style,
111 focus_style: Option<Style>,
112 select_style: Option<Style>,
113 invalid_style: Option<Style>,
114 text_style: Vec<Style>,
115 on_focus_gained: TextFocusGained,
116 on_focus_lost: TextFocusLost,
117}
118
119#[derive(Debug)]
121pub struct MaskedInputState {
122 pub area: Rect,
125 pub inner: Rect,
128 pub rendered: Size,
131 pub compact: bool,
134
135 pub offset: upos_type,
138 pub dark_offset: (u16, u16),
141 pub scroll_to_cursor: bool,
143
144 pub value: MaskedCore,
146 pub invalid: bool,
149 pub overwrite: bool,
152 pub on_focus_gained: TextFocusGained,
155 pub on_focus_lost: TextFocusLost,
158
159 pub focus: FocusFlag,
162
163 pub mouse: MouseFlags,
166
167 pub non_exhaustive: NonExhaustive,
169}
170
171impl<'a> MaskedInput<'a> {
172 pub fn new() -> Self {
174 Self::default()
175 }
176
177 #[inline]
180 pub fn compact(mut self, show_compact: bool) -> Self {
181 self.compact = show_compact;
182 self
183 }
184
185 #[inline]
187 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
188 if let Some(styles) = styles {
189 self.styles(styles)
190 } else {
191 self
192 }
193 }
194
195 #[inline]
197 pub fn styles(mut self, styles: TextStyle) -> Self {
198 self.style = styles.style;
199 if styles.focus.is_some() {
200 self.focus_style = styles.focus;
201 }
202 if styles.select.is_some() {
203 self.select_style = styles.select;
204 }
205 if styles.invalid.is_some() {
206 self.invalid_style = styles.invalid;
207 }
208 if let Some(of) = styles.on_focus_gained {
209 self.on_focus_gained = of;
210 }
211 if let Some(of) = styles.on_focus_lost {
212 self.on_focus_lost = of;
213 }
214 if let Some(border_style) = styles.border_style {
215 self.block = self.block.map(|v| v.border_style(border_style));
216 }
217 self.block = self.block.map(|v| v.style(self.style));
218 if styles.block.is_some() {
219 self.block = styles.block;
220 }
221 self.block = self.block.map(|v| v.style(self.style));
222 self
223 }
224
225 #[inline]
227 pub fn style(mut self, style: impl Into<Style>) -> Self {
228 self.style = style.into();
229 self.block = self.block.map(|v| v.style(self.style));
230 self
231 }
232
233 #[inline]
235 pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
236 self.focus_style = Some(style.into());
237 self
238 }
239
240 #[inline]
242 pub fn select_style(mut self, style: impl Into<Style>) -> Self {
243 self.select_style = Some(style.into());
244 self
245 }
246
247 #[inline]
250 pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
251 self.invalid_style = Some(style.into());
252 self
253 }
254
255 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
260 self.text_style = styles.into_iter().collect();
261 self
262 }
263
264 #[inline]
266 pub fn block(mut self, block: Block<'a>) -> Self {
267 self.block = Some(block);
268 self.block = self.block.map(|v| v.style(self.style));
269 self
270 }
271
272 #[inline]
274 pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
275 self.on_focus_gained = of;
276 self
277 }
278
279 #[inline]
281 pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
282 self.on_focus_lost = of;
283 self
284 }
285}
286
287impl<'a> StatefulWidget for &MaskedInput<'a> {
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
295impl StatefulWidget for MaskedInput<'_> {
296 type State = MaskedInputState;
297
298 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
299 render_ref(&self, area, buf, state);
300 }
301}
302
303fn render_ref(
304 widget: &MaskedInput<'_>,
305 area: Rect,
306
307 buf: &mut Buffer,
308 state: &mut MaskedInputState,
309) {
310 state.area = area;
311 state.inner = widget.block.inner_if_some(area);
312 state.rendered = state.inner.as_size();
313 state.compact = widget.compact;
314 state.on_focus_gained = widget.on_focus_gained;
315 state.on_focus_lost = widget.on_focus_lost;
316
317 if state.scroll_to_cursor {
318 let c = state.cursor();
319 let o = state.offset();
320 let mut no = if c < o {
321 c
322 } else if c >= o + state.rendered.width as upos_type {
323 c.saturating_sub(state.rendered.width as upos_type)
324 } else {
325 o
326 };
327 if c == no + state.rendered.width as upos_type {
330 no = no.saturating_add(1);
331 }
332 state.set_offset(no);
333 }
334
335 let style = widget.style;
336 let focus_style = if let Some(focus_style) = widget.focus_style {
337 focus_style
338 } else {
339 style
340 };
341 let select_style = if let Some(select_style) = widget.select_style {
342 select_style
343 } else {
344 Style::default().black().on_yellow()
345 };
346 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
347 invalid_style
348 } else {
349 Style::default().red()
350 };
351
352 let (style, select_style) = if state.focus.get() {
353 if state.invalid {
354 (
355 style.patch(focus_style).patch(invalid_style),
356 style
357 .patch(focus_style)
358 .patch(select_style)
359 .patch(invalid_style),
360 )
361 } else {
362 (
363 style.patch(focus_style),
364 style.patch(focus_style).patch(select_style),
365 )
366 }
367 } else {
368 if state.invalid {
369 (style.patch(invalid_style), style.patch(invalid_style))
370 } else {
371 (style, style)
372 }
373 };
374
375 if let Some(block) = &widget.block {
377 block.render(area, buf);
378 } else {
379 buf.set_style(area, style);
380 }
381
382 if state.inner.width == 0 || state.inner.height == 0 {
383 return;
385 }
386
387 let ox = state.offset() as u16;
388 let show_range = {
390 let start = ox as upos_type;
391 let end = min(start + state.inner.width as upos_type, state.len());
392 state.bytes_at_range(start..end)
393 };
394 let selection = state.selection();
395 let mut styles = Vec::new();
396
397 for g in state.glyphs2() {
398 if g.screen_width() > 0 {
399 let mut style = style;
400 styles.clear();
401 state
402 .value
403 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
404 for style_nr in &styles {
405 if let Some(s) = widget.text_style.get(*style_nr) {
406 style = style.patch(*s);
407 }
408 }
409 if selection.contains(&g.pos().x) {
411 style = style.patch(select_style);
412 };
413
414 let screen_pos = g.screen_pos();
416
417 if let Some(cell) =
419 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
420 {
421 cell.set_symbol(g.glyph());
422 cell.set_style(style);
423 }
424 for d in 1..g.screen_width() {
426 if let Some(cell) = buf.cell_mut((
427 state.inner.x + screen_pos.0 + d,
428 state.inner.y + screen_pos.1,
429 )) {
430 cell.reset();
431 cell.set_style(style);
432 }
433 }
434 }
435 }
436}
437
438impl Clone for MaskedInputState {
439 fn clone(&self) -> Self {
440 Self {
441 area: self.area,
442 inner: self.inner,
443 rendered: self.rendered,
444 compact: self.compact,
445 offset: self.offset,
446 dark_offset: self.dark_offset,
447 scroll_to_cursor: self.scroll_to_cursor,
448 value: self.value.clone(),
449 invalid: self.invalid,
450 overwrite: Default::default(),
451 on_focus_gained: Default::default(),
452 on_focus_lost: Default::default(),
453 focus: FocusFlag::named(self.focus.name()),
454 mouse: Default::default(),
455 non_exhaustive: NonExhaustive,
456 }
457 }
458}
459
460impl Default for MaskedInputState {
461 fn default() -> Self {
462 Self {
463 area: Default::default(),
464 inner: Default::default(),
465 rendered: Default::default(),
466 compact: Default::default(),
467 offset: Default::default(),
468 dark_offset: Default::default(),
469 scroll_to_cursor: Default::default(),
470 value: Default::default(),
471 invalid: Default::default(),
472 overwrite: Default::default(),
473 on_focus_gained: Default::default(),
474 on_focus_lost: Default::default(),
475 focus: Default::default(),
476 mouse: Default::default(),
477 non_exhaustive: NonExhaustive,
478 }
479 }
480}
481
482impl HasFocus for MaskedInputState {
483 fn build(&self, builder: &mut FocusBuilder) {
484 builder.leaf_widget(self);
485 }
486
487 fn focus(&self) -> FocusFlag {
488 self.focus.clone()
489 }
490
491 fn area(&self) -> Rect {
492 self.area
493 }
494
495 fn navigable(&self) -> Navigation {
496 let sel = self.selection();
497
498 let has_next = self
499 .value
500 .next_section_range(sel.end)
501 .map(|v| !v.is_empty())
502 .is_some();
503 let has_prev = self
504 .value
505 .prev_section_range(sel.start.saturating_sub(1))
506 .map(|v| !v.is_empty())
507 .is_some();
508
509 if has_next {
510 if has_prev {
511 Navigation::Reach
512 } else {
513 Navigation::ReachLeaveFront
514 }
515 } else {
516 if has_prev {
517 Navigation::ReachLeaveBack
518 } else {
519 Navigation::Regular
520 }
521 }
522 }
523}
524
525impl MaskedInputState {
526 pub fn new() -> Self {
527 Self::default()
528 }
529
530 pub fn named(name: &str) -> Self {
531 Self {
532 focus: FocusFlag::named(name),
533 ..MaskedInputState::default()
534 }
535 }
536
537 #[inline]
539 pub fn with_symbols(mut self, sym: NumberSymbols) -> Self {
540 self.set_num_symbols(sym);
541 self
542 }
543
544 pub fn with_mask<S: AsRef<str>>(mut self, mask: S) -> Result<Self, fmt::Error> {
546 self.value.set_mask(mask.as_ref())?;
547 Ok(self)
548 }
549
550 #[inline]
555 pub fn set_num_symbols(&mut self, sym: NumberSymbols) {
556 self.value.set_num_symbols(sym);
557 }
558
559 #[inline]
590 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 #[allow(deprecated)]
945 #[deprecated(since = "1.1.0", note = "discontinued api")]
946 pub fn glyphs(&self, screen_offset: u16, screen_width: u16) -> impl Iterator<Item = Glyph<'_>> {
947 self.value
948 .glyphs(0..1, screen_offset, screen_width)
949 .expect("valid_row")
950 }
951
952 #[inline]
955 #[allow(deprecated)]
956 #[deprecated(since = "1.1.0", note = "discontinued api")]
957 pub fn condensed_glyphs(
958 &self,
959 screen_offset: u16,
960 screen_width: u16,
961 ) -> impl Iterator<Item = Glyph<'_>> {
962 self.value
963 .condensed_glyphs(0..1, screen_offset, screen_width)
964 .expect("valid_row")
965 }
966
967 #[inline]
969 pub fn text_graphemes(&self, pos: upos_type) -> impl Cursor<Item = Grapheme<'_>> {
970 self.value.text_graphemes(pos).expect("valid_pos")
971 }
972
973 #[inline]
975 pub fn try_text_graphemes(
976 &self,
977 pos: upos_type,
978 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
979 self.value.text_graphemes(pos)
980 }
981
982 #[inline]
984 pub fn graphemes(
985 &self,
986 range: Range<upos_type>,
987 pos: upos_type,
988 ) -> impl Cursor<Item = Grapheme<'_>> {
989 self.value.graphemes(range, pos).expect("valid_args")
990 }
991
992 #[inline]
994 pub fn try_graphemes(
995 &self,
996 range: Range<upos_type>,
997 pos: upos_type,
998 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
999 self.value.graphemes(range, pos)
1000 }
1001
1002 #[inline]
1005 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
1006 self.value.byte_at(pos).expect("valid_pos")
1007 }
1008
1009 #[inline]
1012 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
1013 self.value.byte_at(pos)
1014 }
1015
1016 #[inline]
1018 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
1019 self.value.bytes_at_range(range).expect("valid_range")
1020 }
1021
1022 #[inline]
1024 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
1025 self.value.bytes_at_range(range)
1026 }
1027
1028 #[inline]
1031 pub fn byte_pos(&self, byte: usize) -> upos_type {
1032 self.value.byte_pos(byte).expect("valid_pos")
1033 }
1034
1035 #[inline]
1038 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
1039 self.value.byte_pos(byte)
1040 }
1041
1042 #[inline]
1044 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
1045 self.value.byte_range(bytes).expect("valid_range")
1046 }
1047
1048 #[inline]
1050 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
1051 self.value.byte_range(bytes)
1052 }
1053}
1054
1055impl MaskedInputState {
1056 #[inline]
1058 pub fn clear(&mut self) -> bool {
1059 if self.is_empty() {
1060 false
1061 } else {
1062 self.offset = 0;
1063 self.value.clear();
1064 true
1065 }
1066 }
1067
1068 #[inline]
1074 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1075 self.offset = 0;
1076 self.value.set_text(s);
1077 self.value.set_default_cursor();
1078 }
1079
1080 #[inline]
1082 pub fn insert_char(&mut self, c: char) -> bool {
1083 self.value.begin_undo_seq();
1084 if self.value.has_selection() {
1085 let sel = self.value.selection();
1086 self.value
1087 .remove_range(sel.clone())
1088 .expect("valid_selection");
1089 self.value.set_cursor(sel.start, false);
1090 }
1091 let c0 = self.value.advance_cursor(c);
1092 let c1 = self.value.insert_char(c);
1093 self.value.end_undo_seq();
1094
1095 self.scroll_cursor_to_visible();
1096 c0 || c1
1097 }
1098
1099 #[inline]
1102 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1103 self.try_delete_range(range).expect("valid_range")
1104 }
1105
1106 #[inline]
1109 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1110 self.value.begin_undo_seq();
1111 let r = self.value.remove_range(range.clone())?;
1112 if let Some(pos) = self.value.section_cursor(range.start) {
1113 self.value.set_cursor(pos, false);
1114 }
1115 self.value.end_undo_seq();
1116
1117 self.scroll_cursor_to_visible();
1118 Ok(r)
1119 }
1120}
1121
1122impl MaskedInputState {
1123 #[inline]
1125 pub fn delete_next_char(&mut self) -> bool {
1126 if self.has_selection() {
1127 self.delete_range(self.selection())
1128 } else if self.cursor() == self.len() {
1129 false
1130 } else {
1131 self.value.remove_next();
1132 self.scroll_cursor_to_visible();
1133 true
1134 }
1135 }
1136
1137 #[inline]
1139 pub fn delete_prev_char(&mut self) -> bool {
1140 if self.has_selection() {
1141 self.delete_range(self.selection())
1142 } else if self.cursor() == 0 {
1143 false
1144 } else {
1145 self.value.remove_prev();
1146 self.scroll_cursor_to_visible();
1147 true
1148 }
1149 }
1150
1151 #[inline]
1153 pub fn delete_prev_section(&mut self) -> bool {
1154 if self.has_selection() {
1155 self.delete_range(self.selection())
1156 } else {
1157 if let Some(range) = self.value.prev_section_range(self.cursor()) {
1158 self.delete_range(range)
1159 } else {
1160 false
1161 }
1162 }
1163 }
1164
1165 #[inline]
1167 pub fn delete_next_section(&mut self) -> bool {
1168 if self.has_selection() {
1169 self.delete_range(self.selection())
1170 } else {
1171 if let Some(range) = self.value.next_section_range(self.cursor()) {
1172 self.delete_range(range)
1173 } else {
1174 false
1175 }
1176 }
1177 }
1178
1179 #[inline]
1181 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1182 let c = min(self.cursor() + 1, self.len());
1183 self.set_cursor(c, extend_selection)
1184 }
1185
1186 #[inline]
1188 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1189 let c = self.cursor().saturating_sub(1);
1190 self.set_cursor(c, extend_selection)
1191 }
1192
1193 #[inline]
1195 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1196 if let Some(c) = self.value.section_cursor(self.cursor()) {
1197 if c != self.cursor() {
1198 self.set_cursor(c, extend_selection)
1199 } else {
1200 self.set_cursor(0, extend_selection)
1201 }
1202 } else {
1203 self.set_cursor(0, extend_selection)
1204 }
1205 }
1206
1207 #[inline]
1209 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1210 self.set_cursor(self.len(), extend_selection)
1211 }
1212
1213 #[inline]
1215 pub fn move_to_prev_section(&mut self, extend_selection: bool) -> bool {
1216 if let Some(curr) = self.value.section_range(self.cursor()) {
1217 if self.value.cursor() != curr.start {
1218 return self.value.set_cursor(curr.start, extend_selection);
1219 }
1220 }
1221 if let Some(range) = self.value.prev_section_range(self.cursor()) {
1222 self.value.set_cursor(range.start, extend_selection)
1223 } else {
1224 false
1225 }
1226 }
1227
1228 #[inline]
1230 pub fn move_to_next_section(&mut self, extend_selection: bool) -> bool {
1231 if let Some(curr) = self.value.section_range(self.cursor()) {
1232 if self.value.cursor() != curr.end {
1233 return self.value.set_cursor(curr.end, extend_selection);
1234 }
1235 }
1236 if let Some(range) = self.value.next_section_range(self.cursor()) {
1237 self.value.set_cursor(range.end, extend_selection)
1238 } else {
1239 false
1240 }
1241 }
1242
1243 #[inline]
1245 pub fn select_current_section(&mut self) -> bool {
1246 let selection = self.selection();
1247
1248 if let Some(next) = self.value.section_range(selection.start.saturating_sub(1)) {
1249 if !next.is_empty() {
1250 self.set_selection(next.start, next.end)
1251 } else {
1252 false
1253 }
1254 } else {
1255 false
1256 }
1257 }
1258
1259 #[inline]
1261 pub fn select_next_section(&mut self) -> bool {
1262 let selection = self.selection();
1263
1264 if let Some(next) = self.value.next_section_range(selection.start) {
1265 if !next.is_empty() {
1266 self.set_selection(next.start, next.end)
1267 } else {
1268 false
1269 }
1270 } else {
1271 false
1272 }
1273 }
1274
1275 #[inline]
1277 pub fn select_prev_section(&mut self) -> bool {
1278 let selection = self.selection();
1279
1280 if let Some(next) = self
1281 .value
1282 .prev_section_range(selection.start.saturating_sub(1))
1283 {
1284 if !next.is_empty() {
1285 self.set_selection(next.start, next.end)
1286 } else {
1287 false
1288 }
1289 } else {
1290 false
1291 }
1292 }
1293}
1294
1295impl HasScreenCursor for MaskedInputState {
1296 #[inline]
1298 fn screen_cursor(&self) -> Option<(u16, u16)> {
1299 if self.is_focused() {
1300 if self.has_selection() {
1301 None
1302 } else {
1303 let cx = self.cursor();
1304 let ox = self.offset();
1305
1306 if cx < ox {
1307 None
1308 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1309 None
1310 } else {
1311 self.col_to_screen(cx)
1312 .map(|sc| (self.inner.x + sc, self.inner.y))
1313 }
1314 }
1315 } else {
1316 None
1317 }
1318 }
1319}
1320
1321impl RelocatableState for MaskedInputState {
1322 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1323 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1325 self.area = relocate_area(self.area, shift, clip);
1326 self.inner = relocate_area(self.inner, shift, clip);
1327 }
1328}
1329
1330impl MaskedInputState {
1331 fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1332 self.value
1333 .glyphs2(
1334 self.offset(),
1335 self.offset() + self.rendered.width as upos_type,
1336 self.compact && !self.is_focused(),
1337 )
1338 .expect("valid-rows")
1339 }
1340
1341 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1344 let ox = self.offset();
1345
1346 let scx = scx + self.dark_offset.0 as i16;
1347
1348 if scx < 0 {
1349 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1350 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
1351 min(ox + scx as upos_type, self.len())
1352 } else {
1353 let scx = scx as u16;
1354
1355 let line = self.glyphs2();
1356
1357 let mut col = ox;
1358 for g in line {
1359 if g.contains_screen_x(scx) {
1360 break;
1361 }
1362 col = g.pos().x + 1;
1363 }
1364 col
1365 }
1366 }
1367
1368 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1371 let ox = self.offset();
1372
1373 if pos < ox {
1374 return None;
1375 }
1376
1377 let line = self.glyphs2();
1378 let mut screen_x = 0;
1379 for g in line {
1380 if g.pos().x == pos {
1381 break;
1382 }
1383 screen_x = g.screen_pos().0 + g.screen_width();
1384 }
1385
1386 if screen_x >= self.dark_offset.0 {
1387 Some(screen_x - self.dark_offset.0)
1388 } else {
1389 None
1390 }
1391 }
1392
1393 #[inline]
1397 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1398 let scx = cursor;
1399
1400 let cx = self.screen_to_col(scx);
1401
1402 self.set_cursor(cx, extend_selection)
1403 }
1404
1405 pub fn set_screen_cursor_sections(
1412 &mut self,
1413 screen_cursor: i16,
1414 extend_selection: bool,
1415 ) -> bool {
1416 let anchor = self.anchor();
1417 let cursor = self.screen_to_col(screen_cursor);
1418
1419 let Some(range) = self.value.section_range(cursor) else {
1420 return false;
1421 };
1422
1423 let cursor = if cursor < anchor {
1424 range.start
1425 } else {
1426 range.end
1427 };
1428
1429 if !self.value.is_section_boundary(anchor) {
1431 if let Some(range) = self.value.section_range(anchor) {
1432 if cursor < anchor {
1433 self.set_cursor(range.end, false);
1434 } else {
1435 self.set_cursor(range.start, false);
1436 }
1437 };
1438 }
1439
1440 self.set_cursor(cursor, extend_selection)
1441 }
1442
1443 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1445 self.set_offset(self.offset.saturating_sub(delta));
1446 true
1447 }
1448
1449 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1451 self.set_offset(self.offset + delta);
1452 true
1453 }
1454
1455 pub fn scroll_cursor_to_visible(&mut self) {
1457 self.scroll_to_cursor = true;
1458 }
1459}
1460
1461impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for MaskedInputState {
1462 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1463 fn tc(r: bool) -> TextOutcome {
1465 if r {
1466 TextOutcome::TextChanged
1467 } else {
1468 TextOutcome::Unchanged
1469 }
1470 }
1471 fn overwrite(state: &mut MaskedInputState) {
1472 if state.overwrite {
1473 state.overwrite = false;
1474 state.clear();
1475 }
1476 }
1477 fn clear_overwrite(state: &mut MaskedInputState) {
1478 state.overwrite = false;
1479 }
1480
1481 if self.lost_focus() {
1483 match self.on_focus_lost {
1484 TextFocusLost::None => {}
1485 TextFocusLost::Position0 => {
1486 self.set_default_cursor();
1487 self.scroll_cursor_to_visible();
1488 }
1490 }
1491 }
1492 if self.gained_focus() {
1493 match self.on_focus_gained {
1494 TextFocusGained::None => {}
1495 TextFocusGained::Overwrite => {
1496 self.overwrite = true;
1497 }
1498 TextFocusGained::SelectAll => {
1499 self.select_all();
1500 }
1502 }
1503 }
1504
1505 let mut r = if self.is_focused() {
1506 match event {
1507 ct_event!(key press c)
1508 | ct_event!(key press SHIFT-c)
1509 | ct_event!(key press CONTROL_ALT-c) => {
1510 overwrite(self);
1511 tc(self.insert_char(*c))
1512 }
1513 ct_event!(keycode press Backspace) => {
1514 clear_overwrite(self);
1515 tc(self.delete_prev_char())
1516 }
1517 ct_event!(keycode press Delete) => {
1518 clear_overwrite(self);
1519 tc(self.delete_next_char())
1520 }
1521 ct_event!(keycode press CONTROL-Backspace)
1522 | ct_event!(keycode press ALT-Backspace) => {
1523 clear_overwrite(self);
1524 tc(self.delete_prev_section())
1525 }
1526 ct_event!(keycode press CONTROL-Delete) => {
1527 clear_overwrite(self);
1528 tc(self.delete_next_section())
1529 }
1530 ct_event!(key press CONTROL-'x') => {
1531 clear_overwrite(self);
1532 tc(self.cut_to_clip())
1533 }
1534 ct_event!(key press CONTROL-'v') => {
1535 clear_overwrite(self);
1536 tc(self.paste_from_clip())
1537 }
1538 ct_event!(key press CONTROL-'d') => {
1539 clear_overwrite(self);
1540 tc(self.clear())
1541 }
1542 ct_event!(key press CONTROL-'z') => {
1543 clear_overwrite(self);
1544 tc(self.undo())
1545 }
1546 ct_event!(key press CONTROL_SHIFT-'Z') => {
1547 clear_overwrite(self);
1548 tc(self.redo())
1549 }
1550
1551 ct_event!(key release _)
1552 | ct_event!(key release SHIFT-_)
1553 | ct_event!(key release CONTROL_ALT-_)
1554 | ct_event!(keycode release Backspace)
1555 | ct_event!(keycode release Delete)
1556 | ct_event!(keycode release CONTROL-Backspace)
1557 | ct_event!(keycode release ALT-Backspace)
1558 | ct_event!(keycode release CONTROL-Delete)
1559 | ct_event!(key release CONTROL-'x')
1560 | ct_event!(key release CONTROL-'v')
1561 | ct_event!(key release CONTROL-'d')
1562 | ct_event!(key release CONTROL-'z')
1563 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1564
1565 _ => TextOutcome::Continue,
1566 }
1567 } else {
1568 TextOutcome::Continue
1569 };
1570
1571 if r == TextOutcome::Continue {
1572 r = self.handle(event, ReadOnly);
1573 }
1574 r
1575 }
1576}
1577
1578impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for MaskedInputState {
1579 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1580 fn clear_overwrite(state: &mut MaskedInputState) {
1581 state.overwrite = false;
1582 }
1583
1584 let mut r = if self.is_focused() {
1585 match event {
1586 ct_event!(keycode press Left) => {
1587 clear_overwrite(self);
1588 self.move_left(false).into()
1589 }
1590 ct_event!(keycode press Right) => {
1591 clear_overwrite(self);
1592 self.move_right(false).into()
1593 }
1594 ct_event!(keycode press CONTROL-Left) => {
1595 clear_overwrite(self);
1596 self.move_to_prev_section(false).into()
1597 }
1598 ct_event!(keycode press CONTROL-Right) => {
1599 clear_overwrite(self);
1600 self.move_to_next_section(false).into()
1601 }
1602 ct_event!(keycode press Home) => {
1603 clear_overwrite(self);
1604 self.move_to_line_start(false).into()
1605 }
1606 ct_event!(keycode press End) => {
1607 clear_overwrite(self);
1608 self.move_to_line_end(false).into()
1609 }
1610 ct_event!(keycode press SHIFT-Left) => {
1611 clear_overwrite(self);
1612 self.move_left(true).into()
1613 }
1614 ct_event!(keycode press SHIFT-Right) => {
1615 clear_overwrite(self);
1616 self.move_right(true).into()
1617 }
1618 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1619 clear_overwrite(self);
1620 self.move_to_prev_section(true).into()
1621 }
1622 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1623 clear_overwrite(self);
1624 self.move_to_next_section(true).into()
1625 }
1626 ct_event!(keycode press SHIFT-Home) => {
1627 clear_overwrite(self);
1628 self.move_to_line_start(true).into()
1629 }
1630 ct_event!(keycode press SHIFT-End) => {
1631 clear_overwrite(self);
1632 self.move_to_line_end(true).into()
1633 }
1634 ct_event!(keycode press Tab) => {
1635 if !self.focus.gained() {
1637 clear_overwrite(self);
1638 self.select_next_section().into()
1639 } else {
1640 TextOutcome::Unchanged
1641 }
1642 }
1643 ct_event!(keycode press SHIFT-BackTab) => {
1644 if !self.focus.gained() {
1646 clear_overwrite(self);
1647 self.select_prev_section().into()
1648 } else {
1649 TextOutcome::Unchanged
1650 }
1651 }
1652 ct_event!(key press CONTROL-'a') => {
1653 clear_overwrite(self);
1654 self.select_all().into()
1655 }
1656 ct_event!(key press CONTROL-'c') => {
1657 clear_overwrite(self);
1658 self.copy_to_clip().into()
1659 }
1660
1661 ct_event!(keycode release Left)
1662 | ct_event!(keycode release Right)
1663 | ct_event!(keycode release CONTROL-Left)
1664 | ct_event!(keycode release CONTROL-Right)
1665 | ct_event!(keycode release Home)
1666 | ct_event!(keycode release End)
1667 | ct_event!(keycode release SHIFT-Left)
1668 | ct_event!(keycode release SHIFT-Right)
1669 | ct_event!(keycode release CONTROL_SHIFT-Left)
1670 | ct_event!(keycode release CONTROL_SHIFT-Right)
1671 | ct_event!(keycode release SHIFT-Home)
1672 | ct_event!(keycode release SHIFT-End)
1673 | ct_event!(key release CONTROL-'a')
1674 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1675
1676 _ => TextOutcome::Continue,
1677 }
1678 } else {
1679 TextOutcome::Continue
1680 };
1681
1682 if r == TextOutcome::Continue {
1683 r = self.handle(event, MouseOnly);
1684 }
1685 r
1686 }
1687}
1688
1689impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for MaskedInputState {
1690 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1691 fn clear_overwrite(state: &mut MaskedInputState) {
1692 state.overwrite = false;
1693 }
1694
1695 match event {
1696 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1697 let c = (m.column as i16) - (self.inner.x as i16);
1698 clear_overwrite(self);
1699 self.set_screen_cursor(c, true).into()
1700 }
1701 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1702 let cx = m.column as i16 - self.inner.x as i16;
1703 clear_overwrite(self);
1704 self.set_screen_cursor_sections(cx, true).into()
1705 }
1706 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1707 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1708 clear_overwrite(self);
1709 if let Some(range) = self.value.section_range(tx) {
1710 self.set_selection(range.start, range.end).into()
1711 } else {
1712 TextOutcome::Unchanged
1713 }
1714 }
1715 ct_event!(mouse down Left for column,row) => {
1716 if self.gained_focus() {
1717 TextOutcome::Unchanged
1720 } else if self.inner.contains((*column, *row).into()) {
1721 let c = (column - self.inner.x) as i16;
1722 clear_overwrite(self);
1723 self.set_screen_cursor(c, false).into()
1724 } else {
1725 TextOutcome::Continue
1726 }
1727 }
1728 ct_event!(mouse down CONTROL-Left for column,row) => {
1729 if self.inner.contains((*column, *row).into()) {
1730 let cx = (column - self.inner.x) as i16;
1731 clear_overwrite(self);
1732 self.set_screen_cursor(cx, true).into()
1733 } else {
1734 TextOutcome::Continue
1735 }
1736 }
1737 ct_event!(mouse down ALT-Left for column,row) => {
1738 if self.inner.contains((*column, *row).into()) {
1739 let cx = (column - self.inner.x) as i16;
1740 clear_overwrite(self);
1741 self.set_screen_cursor_sections(cx, true).into()
1742 } else {
1743 TextOutcome::Continue
1744 }
1745 }
1746 _ => TextOutcome::Continue,
1747 }
1748 }
1749}
1750
1751pub fn handle_events(
1755 state: &mut MaskedInputState,
1756 focus: bool,
1757 event: &crossterm::event::Event,
1758) -> TextOutcome {
1759 state.focus.set(focus);
1760 state.handle(event, Regular)
1761}
1762
1763pub fn handle_readonly_events(
1767 state: &mut TextInputState,
1768 focus: bool,
1769 event: &crossterm::event::Event,
1770) -> TextOutcome {
1771 state.focus.set(focus);
1772 state.handle(event, ReadOnly)
1773}
1774
1775pub fn handle_mouse_events(
1777 state: &mut MaskedInputState,
1778 event: &crossterm::event::Event,
1779) -> TextOutcome {
1780 state.handle(event, MouseOnly)
1781}