1pub mod mask_op;
73pub(crate) mod mask_token;
74pub(crate) mod masked_graphemes;
75
76use crate::_private::NonExhaustive;
77use crate::clipboard::{Clipboard, global_clipboard};
78use crate::core::{TextCore, TextString};
79use crate::event::{ReadOnly, TextOutcome};
80use crate::glyph2::{Glyph2, GlyphIter2, TextWrap2};
81use crate::text_input::TextInputState;
82use crate::text_input_mask::mask_token::{EditDirection, Mask, MaskToken};
83use crate::text_input_mask::masked_graphemes::MaskedGraphemes;
84use crate::text_store::TextStore;
85use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
86use crate::{
87 Grapheme, HasScreenCursor, TextError, TextFocusGained, TextFocusLost, TextPosition, TextRange,
88 TextStyle, TextTab, ipos_type, upos_type,
89};
90use crossterm::event::KeyModifiers;
91use format_num_pattern::NumberSymbols;
92use rat_event::util::MouseFlags;
93use rat_event::{HandleEvent, MouseOnly, Regular, ct_event};
94use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
95use rat_reloc::{RelocatableState, relocate_dark_offset};
96use ratatui::buffer::Buffer;
97use ratatui::layout::{Rect, Size};
98use ratatui::prelude::BlockExt;
99use ratatui::style::{Style, Stylize};
100use ratatui::widgets::{Block, StatefulWidget, Widget};
101use std::borrow::Cow;
102use std::cell::Cell;
103use std::cmp::min;
104use std::collections::HashMap;
105use std::fmt;
106use std::iter::once;
107use std::ops::Range;
108use std::rc::Rc;
109use std::str::FromStr;
110use unicode_segmentation::UnicodeSegmentation;
111
112#[derive(Debug, Default, Clone)]
118pub struct MaskedInput<'a> {
119 compact: bool,
120 block: Option<Block<'a>>,
121 style: Style,
122 focus_style: Option<Style>,
123 select_style: Option<Style>,
124 invalid_style: Option<Style>,
125 text_style: HashMap<usize, Style>,
126 on_focus_gained: TextFocusGained,
127 on_focus_lost: TextFocusLost,
128 on_tab: TextTab,
129}
130
131#[derive(Debug)]
133pub struct MaskedInputState {
134 pub area: Rect,
137 pub inner: Rect,
140 pub rendered: Size,
143 pub compact: bool,
146
147 pub offset: upos_type,
150 pub dark_offset: (u16, u16),
153 pub scroll_to_cursor: Rc<Cell<bool>>,
155
156 pub value: TextCore<TextString>,
158 pub sym: Option<NumberSymbols>,
161 pub mask: Vec<MaskToken>,
164
165 pub invalid: bool,
168 pub overwrite: Rc<Cell<bool>>,
172 pub on_focus_gained: Rc<Cell<TextFocusGained>>,
175 pub on_focus_lost: Rc<Cell<TextFocusLost>>,
178 pub on_tab: TextTab,
181
182 pub focus: FocusFlag,
185
186 pub mouse: MouseFlags,
189
190 pub non_exhaustive: NonExhaustive,
192}
193
194impl<'a> MaskedInput<'a> {
195 pub fn new() -> Self {
197 Self::default()
198 }
199
200 #[inline]
203 pub fn compact(mut self, show_compact: bool) -> Self {
204 self.compact = show_compact;
205 self
206 }
207
208 #[inline]
210 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
211 if let Some(styles) = styles {
212 self.styles(styles)
213 } else {
214 self
215 }
216 }
217
218 #[inline]
220 pub fn styles(mut self, styles: TextStyle) -> Self {
221 self.style = styles.style;
222 if styles.focus.is_some() {
223 self.focus_style = styles.focus;
224 }
225 if styles.select.is_some() {
226 self.select_style = styles.select;
227 }
228 if styles.invalid.is_some() {
229 self.invalid_style = styles.invalid;
230 }
231 if let Some(of) = styles.on_focus_gained {
232 self.on_focus_gained = of;
233 }
234 if let Some(of) = styles.on_focus_lost {
235 self.on_focus_lost = of;
236 }
237 if let Some(ot) = styles.on_tab {
238 self.on_tab = ot;
239 }
240 self.block = self.block.map(|v| v.style(self.style));
241 if let Some(border_style) = styles.border_style {
242 self.block = self.block.map(|v| v.border_style(border_style));
243 }
244 if styles.block.is_some() {
245 self.block = styles.block;
246 }
247 self
248 }
249
250 #[inline]
252 pub fn style(mut self, style: impl Into<Style>) -> Self {
253 self.style = style.into();
254 self.block = self.block.map(|v| v.style(self.style));
255 self
256 }
257
258 #[inline]
260 pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
261 self.focus_style = Some(style.into());
262 self
263 }
264
265 #[inline]
267 pub fn select_style(mut self, style: impl Into<Style>) -> Self {
268 self.select_style = Some(style.into());
269 self
270 }
271
272 #[inline]
275 pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
276 self.invalid_style = Some(style.into());
277 self
278 }
279
280 pub fn text_style_idx(mut self, idx: usize, style: Style) -> Self {
285 self.text_style.insert(idx, style);
286 self
287 }
288
289 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
294 for (i, s) in styles.into_iter().enumerate() {
295 self.text_style.insert(i, s);
296 }
297 self
298 }
299
300 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
305 for (i, s) in styles.into_iter() {
306 self.text_style.insert(i, s.into());
307 }
308 self
309 }
310
311 #[inline]
313 pub fn block(mut self, block: Block<'a>) -> Self {
314 self.block = Some(block);
315 self.block = self.block.map(|v| v.style(self.style));
316 self
317 }
318
319 #[inline]
321 pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
322 self.on_focus_gained = of;
323 self
324 }
325
326 #[inline]
328 pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
329 self.on_focus_lost = of;
330 self
331 }
332
333 #[inline]
335 pub fn on_tab(mut self, ot: TextTab) -> Self {
336 self.on_tab = ot;
337 self
338 }
339
340 pub fn width(&self, state: &MaskedInputState) -> u16 {
342 state.mask.len() as u16 + 1
343 }
344
345 pub fn height(&self) -> u16 {
347 1
348 }
349}
350
351impl<'a> StatefulWidget for &MaskedInput<'a> {
352 type State = MaskedInputState;
353
354 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
355 render_ref(self, area, buf, state);
356 }
357}
358
359impl StatefulWidget for MaskedInput<'_> {
360 type State = MaskedInputState;
361
362 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
363 render_ref(&self, area, buf, state);
364 }
365}
366
367fn render_ref(
368 widget: &MaskedInput<'_>,
369 area: Rect,
370
371 buf: &mut Buffer,
372 state: &mut MaskedInputState,
373) {
374 state.area = area;
375 state.inner = widget.block.inner_if_some(area);
376 state.rendered = state.inner.as_size();
377 state.compact = widget.compact;
378 state.on_focus_gained.set(widget.on_focus_gained);
379 state.on_focus_lost.set(widget.on_focus_lost);
380 state.on_tab = widget.on_tab;
381
382 if state.scroll_to_cursor.get() {
383 let c = state.cursor();
384 let o = state.offset();
385 let mut no = if c < o {
386 c
387 } else if c >= o + state.rendered.width as upos_type {
388 c.saturating_sub(state.rendered.width as upos_type)
389 } else {
390 o
391 };
392 if c == no + state.rendered.width as upos_type {
395 no = no.saturating_add(1);
396 }
397 state.set_offset(no);
398 }
399
400 let style = widget.style;
401 let focus_style = if let Some(focus_style) = widget.focus_style {
402 focus_style
403 } else {
404 style
405 };
406 let select_style = if let Some(select_style) = widget.select_style {
407 select_style
408 } else {
409 Style::default().black().on_yellow()
410 };
411 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
412 invalid_style
413 } else {
414 Style::default().red()
415 };
416
417 let (style, select_style) = if state.focus.get() {
418 if state.invalid {
419 (
420 style.patch(focus_style).patch(invalid_style),
421 style
422 .patch(focus_style)
423 .patch(select_style)
424 .patch(invalid_style),
425 )
426 } else {
427 (
428 style.patch(focus_style),
429 style.patch(focus_style).patch(select_style),
430 )
431 }
432 } else {
433 if state.invalid {
434 (
435 style.patch(invalid_style),
436 style.patch(select_style).patch(invalid_style),
437 )
438 } else {
439 (style, style.patch(select_style))
440 }
441 };
442
443 if let Some(block) = &widget.block {
445 block.render(area, buf);
446 } else {
447 buf.set_style(area, style);
448 }
449
450 if state.inner.width == 0 || state.inner.height == 0 {
451 return;
453 }
454
455 let ox = state.offset() as u16;
456 let show_range = {
458 let start = ox as upos_type;
459 let end = min(start + state.inner.width as upos_type, state.len());
460 state.bytes_at_range(start..end)
461 };
462 let selection = state.selection();
463 let mut styles = Vec::new();
464
465 for g in state.glyphs2() {
466 if g.screen_width() > 0 {
467 let mut style = style;
468 styles.clear();
469 state
470 .value
471 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
472 for style_nr in &styles {
473 if let Some(s) = widget.text_style.get(style_nr) {
474 style = style.patch(*s);
475 }
476 }
477 if selection.contains(&g.pos().x) {
479 style = style.patch(select_style);
480 };
481
482 let screen_pos = g.screen_pos();
484
485 if let Some(cell) =
487 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
488 {
489 cell.set_symbol(g.glyph());
490 cell.set_style(style);
491 }
492 for d in 1..g.screen_width() {
494 if let Some(cell) = buf.cell_mut((
495 state.inner.x + screen_pos.0 + d,
496 state.inner.y + screen_pos.1,
497 )) {
498 cell.reset();
499 cell.set_style(style);
500 }
501 }
502 }
503 }
504}
505
506impl Clone for MaskedInputState {
507 fn clone(&self) -> Self {
508 Self {
509 area: self.area,
510 inner: self.inner,
511 rendered: self.rendered,
512 compact: self.compact,
513 offset: self.offset,
514 dark_offset: self.dark_offset,
515 scroll_to_cursor: Rc::new(Cell::new(self.scroll_to_cursor.get())),
516 value: self.value.clone(),
517 sym: self.sym,
518 mask: self.mask.clone(),
519 invalid: self.invalid,
520 overwrite: Rc::new(Cell::new(self.overwrite.get())),
521 on_focus_gained: Rc::new(Cell::new(self.on_focus_gained.get())),
522 on_focus_lost: Rc::new(Cell::new(self.on_focus_lost.get())),
523 on_tab: self.on_tab,
524 focus: self.focus_cb(self.focus.new_instance()),
525 mouse: Default::default(),
526 non_exhaustive: NonExhaustive,
527 }
528 }
529}
530
531impl Default for MaskedInputState {
532 fn default() -> Self {
533 let core = TextCore::new(
534 Some(Box::new(UndoVec::new(99))),
535 Some(Box::new(global_clipboard())),
536 );
537
538 let mut z = Self {
539 area: Default::default(),
540 inner: Default::default(),
541 rendered: Default::default(),
542 compact: Default::default(),
543 offset: Default::default(),
544 dark_offset: Default::default(),
545 scroll_to_cursor: Default::default(),
546 value: core,
547 sym: None,
548 mask: Default::default(),
549 invalid: Default::default(),
550 overwrite: Default::default(),
551 on_focus_gained: Default::default(),
552 on_focus_lost: Default::default(),
553 on_tab: Default::default(),
554 focus: Default::default(),
555 mouse: Default::default(),
556 non_exhaustive: NonExhaustive,
557 };
558 z.focus = z.focus_cb(FocusFlag::default());
559 z
560 }
561}
562
563impl MaskedInputState {
564 fn focus_cb(&self, flag: FocusFlag) -> FocusFlag {
565 let on_focus_lost = self.on_focus_lost.clone();
566 let cursor = self.value.shared_cursor();
567 let scroll_cursor_to_visible = self.scroll_to_cursor.clone();
568 flag.on_lost(move || match on_focus_lost.get() {
569 TextFocusLost::None => {}
570 TextFocusLost::Position0 => {
571 scroll_cursor_to_visible.set(true);
572 let mut new_cursor = cursor.get();
573 new_cursor.cursor.x = 0;
574 new_cursor.anchor.x = 0;
575 cursor.set(new_cursor);
576 }
577 });
578 let on_focus_gained = self.on_focus_gained.clone();
579 let overwrite = self.overwrite.clone();
580 let cursor = self.value.shared_cursor();
581 let scroll_cursor_to_visible = self.scroll_to_cursor.clone();
582 flag.on_gained(move || match on_focus_gained.get() {
583 TextFocusGained::None => {}
584 TextFocusGained::Overwrite => {
585 overwrite.set(true);
586 }
587 TextFocusGained::SelectAll => {
588 scroll_cursor_to_visible.set(true);
589 let mut new_cursor = cursor.get();
590 new_cursor.anchor = TextPosition::new(0, 0);
591 new_cursor.cursor = TextPosition::new(0, 1);
592 cursor.set(new_cursor);
593 }
594 });
595
596 flag
597 }
598}
599
600impl HasFocus for MaskedInputState {
601 fn build(&self, builder: &mut FocusBuilder) {
602 builder.leaf_widget(self);
603 }
604
605 fn focus(&self) -> FocusFlag {
606 self.focus.clone()
607 }
608
609 fn area(&self) -> Rect {
610 self.area
611 }
612
613 fn navigable(&self) -> Navigation {
614 if self.on_tab == TextTab::MoveToNextSection {
615 let sel = self.selection();
616
617 let has_next = self
618 .next_section_range(sel.end)
619 .map(|v| !v.is_empty())
620 .is_some();
621 let has_prev = self
622 .prev_section_range(sel.start.saturating_sub(1))
623 .map(|v| !v.is_empty())
624 .is_some();
625
626 if has_next {
627 if has_prev {
628 Navigation::Reach
629 } else {
630 Navigation::ReachLeaveFront
631 }
632 } else {
633 if has_prev {
634 Navigation::ReachLeaveBack
635 } else {
636 Navigation::Regular
637 }
638 }
639 } else {
640 Navigation::Regular
641 }
642 }
643}
644
645impl MaskedInputState {
646 pub fn new() -> Self {
647 Self::default()
648 }
649
650 pub fn named(name: &str) -> Self {
651 let mut z = Self::default();
652 z.focus = z.focus.with_name(name);
653 z
654 }
655
656 #[inline]
658 pub fn with_symbols(mut self, sym: NumberSymbols) -> Self {
659 self.set_num_symbols(sym);
660 self
661 }
662
663 pub fn with_mask<S: AsRef<str>>(mut self, mask: S) -> Result<Self, fmt::Error> {
665 self.set_mask(mask.as_ref())?;
666 Ok(self)
667 }
668
669 #[inline]
674 pub fn set_num_symbols(&mut self, sym: NumberSymbols) {
675 self.sym = Some(sym);
676 }
677
678 fn dec_sep(&self) -> char {
679 if let Some(sym) = &self.sym {
680 sym.decimal_sep
681 } else {
682 '.'
683 }
684 }
685
686 fn grp_sep(&self) -> char {
687 if let Some(sym) = &self.sym {
688 sym.decimal_grp.unwrap_or(' ')
692 } else {
693 ','
694 }
695 }
696
697 fn neg_sym(&self) -> char {
698 if let Some(sym) = &self.sym {
699 sym.negative_sym
700 } else {
701 '-'
702 }
703 }
704
705 fn pos_sym(&self) -> char {
706 if let Some(sym) = &self.sym {
707 sym.positive_sym
708 } else {
709 ' '
710 }
711 }
712
713 #[inline]
744 pub fn set_mask<S: AsRef<str>>(&mut self, s: S) -> Result<(), fmt::Error> {
745 self.mask = Self::parse_mask(s.as_ref())?;
746 self.clear();
747 Ok(())
748 }
749
750 #[allow(clippy::needless_range_loop)]
751 fn parse_mask(mask_str: &str) -> Result<Vec<MaskToken>, fmt::Error> {
752 let mut out = Vec::<MaskToken>::new();
753
754 let mut start_sub = 0;
755 let mut start_sec = 0;
756 let mut sec_id = 0;
757 let mut last_mask = Mask::None;
758 let mut dec_dir = EditDirection::Rtol;
759 let mut esc = false;
760 let mut idx = 0;
761 for m in mask_str.graphemes(true).chain(once("")) {
762 let mask = if esc {
763 esc = false;
764 Mask::Separator(Box::from(m))
765 } else {
766 match m {
767 "0" => Mask::Digit0(dec_dir),
768 "9" => Mask::Digit(dec_dir),
769 "#" => Mask::Numeric(dec_dir),
770 "." => Mask::DecimalSep,
771 "," => Mask::GroupingSep,
772 "-" => Mask::Sign,
773 "+" => Mask::Plus,
774 "h" => Mask::Hex,
775 "H" => Mask::Hex0,
776 "o" => Mask::Oct,
777 "O" => Mask::Oct0,
778 "d" => Mask::Dec,
779 "D" => Mask::Dec0,
780 "l" => Mask::Letter,
781 "a" => Mask::LetterOrDigit,
782 "c" => Mask::LetterDigitSpace,
783 "_" => Mask::AnyChar,
784 "" => Mask::None,
785 " " => Mask::Separator(Box::from(m)),
786 "\\" => {
787 esc = true;
788 continue;
789 }
790 _ => return Err(fmt::Error),
791 }
792 };
793
794 match mask {
795 Mask::Digit0(_)
796 | Mask::Digit(_)
797 | Mask::Numeric(_)
798 | Mask::GroupingSep
799 | Mask::Sign
800 | Mask::Plus => {
801 }
803 Mask::DecimalSep => {
804 dec_dir = EditDirection::Ltor;
805 }
806 Mask::Hex0
807 | Mask::Hex
808 | Mask::Oct0
809 | Mask::Oct
810 | Mask::Dec0
811 | Mask::Dec
812 | Mask::Letter
813 | Mask::LetterOrDigit
814 | Mask::LetterDigitSpace
815 | Mask::AnyChar
816 | Mask::Separator(_) => {
817 dec_dir = EditDirection::Rtol
819 }
820 Mask::None => {
821 }
823 }
824
825 if matches!(mask, Mask::Separator(_)) || mask.section() != last_mask.section() {
826 for j in start_sec..idx {
827 out[j].sec_id = sec_id;
828 out[j].sec_start = start_sec as upos_type;
829 out[j].sec_end = idx as upos_type;
830 }
831 sec_id += 1;
832 start_sec = idx;
833 }
834 if matches!(mask, Mask::Separator(_)) || mask.sub_section() != last_mask.sub_section() {
835 for j in start_sub..idx {
836 out[j].sub_start = start_sub as upos_type;
837 out[j].sub_end = idx as upos_type;
838 }
839 start_sub = idx;
840 }
841
842 let tok = MaskToken {
843 sec_id: 0,
844 sec_start: 0,
845 sec_end: 0,
846 sub_start: 0,
847 sub_end: 0,
848 peek_left: last_mask,
849 right: mask.clone(),
850 edit: mask.edit_value().into(),
851 };
852 out.push(tok);
853
854 idx += 1;
855 last_mask = mask;
856 }
857 for j in start_sec..out.len() {
858 out[j].sec_id = sec_id;
859 out[j].sec_start = start_sec as upos_type;
860 out[j].sec_end = mask_str.graphemes(true).count() as upos_type;
861 }
862 for j in start_sub..out.len() {
863 out[j].sub_start = start_sub as upos_type;
864 out[j].sub_end = mask_str.graphemes(true).count() as upos_type;
865 }
866
867 Ok(out)
868 }
869
870 #[inline]
872 pub fn mask(&self) -> String {
873 use std::fmt::Write;
874
875 let mut buf = String::new();
876 for t in self.mask.iter() {
877 _ = write!(buf, "{}", t.right);
878 }
879 buf
880 }
881
882 #[inline]
884 pub fn set_invalid(&mut self, invalid: bool) {
885 self.invalid = invalid;
886 }
887
888 #[inline]
890 pub fn get_invalid(&self) -> bool {
891 self.invalid
892 }
893
894 #[inline]
898 pub fn set_overwrite(&mut self, overwrite: bool) {
899 self.overwrite.set(overwrite);
900 }
901
902 #[inline]
904 pub fn overwrite(&self) -> bool {
905 self.overwrite.get()
906 }
907}
908
909impl MaskedInputState {
910 #[inline]
913 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
914 self.value.set_clipboard(clip.map(|v| {
915 let r: Box<dyn Clipboard> = Box::new(v);
916 r
917 }));
918 }
919
920 #[inline]
923 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
924 self.value.clipboard()
925 }
926
927 #[inline]
929 pub fn copy_to_clip(&mut self) -> bool {
930 let Some(clip) = self.value.clipboard() else {
931 return false;
932 };
933
934 _ = clip.set_string(self.selected_text().as_ref());
935
936 true
937 }
938
939 #[inline]
941 pub fn cut_to_clip(&mut self) -> bool {
942 let Some(clip) = self.value.clipboard() else {
943 return false;
944 };
945
946 match clip.set_string(self.selected_text().as_ref()) {
947 Ok(_) => self.delete_range(self.selection()),
948 Err(_) => true,
949 }
950 }
951
952 #[inline]
954 pub fn paste_from_clip(&mut self) -> bool {
955 let Some(clip) = self.value.clipboard() else {
956 return false;
957 };
958
959 if let Ok(text) = clip.get_string() {
960 for c in text.chars() {
961 self.insert_char(c);
962 }
963 true
964 } else {
965 false
966 }
967 }
968}
969
970impl MaskedInputState {
971 #[inline]
973 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
974 self.value.set_undo_buffer(undo.map(|v| {
975 let r: Box<dyn UndoBuffer> = Box::new(v);
976 r
977 }));
978 }
979
980 #[inline]
982 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
983 self.value.undo_buffer()
984 }
985
986 #[inline]
988 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
989 self.value.undo_buffer_mut()
990 }
991
992 #[inline]
994 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
995 self.value.recent_replay_log()
996 }
997
998 #[inline]
1000 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
1001 self.value.replay_log(replay)
1002 }
1003
1004 #[inline]
1006 pub fn begin_undo_seq(&mut self) {
1007 self.value.begin_undo_seq();
1008 }
1009
1010 #[inline]
1012 pub fn end_undo_seq(&mut self) {
1013 self.value.end_undo_seq();
1014 }
1015
1016 #[inline]
1018 pub fn undo(&mut self) -> bool {
1019 self.value.undo()
1020 }
1021
1022 #[inline]
1024 pub fn redo(&mut self) -> bool {
1025 self.value.redo()
1026 }
1027}
1028
1029impl MaskedInputState {
1030 #[inline]
1032 pub fn clear_styles(&mut self) {
1033 self.value.set_styles(Vec::default());
1034 }
1035
1036 #[inline]
1038 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
1039 self.value.set_styles(styles);
1040 }
1041
1042 #[inline]
1045 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
1046 self.value.add_style(range, style);
1047 }
1048
1049 #[inline]
1052 pub fn add_range_style(
1053 &mut self,
1054 range: Range<upos_type>,
1055 style: usize,
1056 ) -> Result<(), TextError> {
1057 let r = self
1058 .value
1059 .bytes_at_range(TextRange::from((range.start, 0)..(range.end, 0)))?;
1060 self.value.add_style(r, style);
1061 Ok(())
1062 }
1063
1064 #[inline]
1066 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
1067 self.value.remove_style(range, style);
1068 }
1069
1070 #[inline]
1072 pub fn remove_range_style(
1073 &mut self,
1074 range: Range<upos_type>,
1075 style: usize,
1076 ) -> Result<(), TextError> {
1077 let r = self
1078 .value
1079 .bytes_at_range(TextRange::from((range.start, 0)..(range.end, 0)))?;
1080 self.value.remove_style(r, style);
1081 Ok(())
1082 }
1083
1084 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
1086 self.value.styles_in(range, buf)
1087 }
1088
1089 #[inline]
1091 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
1092 self.value.styles_at(byte_pos, buf)
1093 }
1094
1095 #[inline]
1098 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
1099 self.value.styles_at_match(byte_pos, style)
1100 }
1101
1102 #[inline]
1104 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
1105 self.value.styles()
1106 }
1107}
1108
1109impl MaskedInputState {
1110 #[inline]
1112 pub fn offset(&self) -> upos_type {
1113 self.offset
1114 }
1115
1116 #[inline]
1118 pub fn set_offset(&mut self, offset: upos_type) {
1119 self.scroll_to_cursor.set(false);
1120 self.offset = offset;
1121 }
1122
1123 #[inline]
1125 pub fn cursor(&self) -> upos_type {
1126 self.value.cursor().x
1127 }
1128
1129 #[inline]
1132 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
1133 self.scroll_cursor_to_visible();
1134 self.value
1135 .set_cursor(TextPosition::new(cursor, 0), extend_selection)
1136 }
1137
1138 fn number_cursor(&self, range: Range<upos_type>) -> upos_type {
1140 for (i, t) in self.mask[range.start as usize..range.end as usize]
1141 .iter()
1142 .enumerate()
1143 .rev()
1144 {
1145 match t.right {
1146 Mask::Digit(EditDirection::Rtol)
1147 | Mask::Digit0(EditDirection::Rtol)
1148 | Mask::Numeric(EditDirection::Rtol) => {
1149 return range.start + i as upos_type + 1;
1150 }
1151 _ => {}
1152 }
1153 }
1154 range.start
1155 }
1156
1157 pub fn section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
1160 if cursor as usize >= self.mask.len() {
1161 return None;
1162 }
1163
1164 let mask = &self.mask[cursor as usize];
1165
1166 if mask.right.is_number() {
1167 Some(self.number_cursor(mask.sec_start..mask.sec_end))
1168 } else if mask.right.is_separator() {
1169 None
1170 } else if mask.right.is_none() {
1171 None
1172 } else {
1173 Some(mask.sec_start)
1174 }
1175 }
1176
1177 pub fn next_section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
1179 if cursor as usize >= self.mask.len() {
1180 return None;
1181 }
1182
1183 let mut mask = &self.mask[cursor as usize];
1184 let mut next;
1185 loop {
1186 if mask.right.is_none() {
1187 return None;
1188 }
1189
1190 next = mask.sec_end;
1191 mask = &self.mask[next as usize];
1192
1193 if mask.right.is_number() {
1194 return Some(self.number_cursor(mask.sec_start..mask.sec_end));
1195 } else if mask.right.is_separator() {
1196 continue;
1197 } else if mask.right.is_none() {
1198 return None;
1199 } else {
1200 return Some(mask.sec_start);
1201 }
1202 }
1203 }
1204
1205 pub fn prev_section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
1207 if cursor as usize >= self.mask.len() {
1208 return None;
1209 }
1210
1211 let mut prev = self.mask[cursor as usize].sec_start;
1212 let mut mask = &self.mask[prev as usize];
1213
1214 loop {
1215 if mask.peek_left.is_none() {
1216 return None;
1217 }
1218
1219 prev = self.mask[mask.sec_start as usize - 1].sec_start;
1220 mask = &self.mask[prev as usize];
1221
1222 if mask.right.is_number() {
1223 return Some(self.number_cursor(mask.sec_start..mask.sec_end));
1224 } else if mask.right.is_separator() {
1225 continue;
1226 } else {
1227 return Some(mask.sec_start);
1228 }
1229 }
1230 }
1231
1232 pub fn is_section_boundary(&self, pos: upos_type) -> bool {
1234 if pos == 0 {
1235 return false;
1236 }
1237 if pos as usize >= self.mask.len() {
1238 return false;
1239 }
1240 let prev = &self.mask[pos as usize - 1];
1241 let mask = &self.mask[pos as usize];
1242 prev.sec_id != mask.sec_id
1243 }
1244
1245 pub fn section_id(&self, cursor: upos_type) -> u16 {
1247 let mask = &self.mask[cursor as usize];
1248 if mask.peek_left.is_rtol() && (mask.right.is_ltor() || mask.right.is_none()) {
1249 return self.mask[cursor.saturating_sub(1) as usize].sec_id;
1250 } else {
1251 mask.sec_id
1252 }
1253 }
1254
1255 pub fn section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
1258 if cursor as usize >= self.mask.len() {
1259 return None;
1260 }
1261
1262 let mask = &self.mask[cursor as usize];
1263 if mask.right.is_number() {
1264 Some(mask.sec_start..mask.sec_end)
1265 } else if mask.right.is_separator() {
1266 None
1267 } else if mask.right.is_none() {
1268 None
1269 } else {
1270 Some(mask.sec_start..mask.sec_end)
1271 }
1272 }
1273
1274 pub fn next_section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
1276 if cursor as usize >= self.mask.len() {
1277 return None;
1278 }
1279
1280 let mut mask = &self.mask[cursor as usize];
1281 let mut next;
1282 loop {
1283 if mask.right.is_none() {
1284 return None;
1285 }
1286
1287 next = mask.sec_end;
1288 mask = &self.mask[next as usize];
1289
1290 if mask.right.is_number() {
1291 return Some(mask.sec_start..mask.sec_end);
1292 } else if mask.right.is_separator() {
1293 continue;
1294 } else if mask.right.is_none() {
1295 return None;
1296 } else {
1297 return Some(mask.sec_start..mask.sec_end);
1298 }
1299 }
1300 }
1301
1302 pub fn prev_section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
1304 if cursor as usize >= self.mask.len() {
1305 return None;
1306 }
1307
1308 let mut prev = self.mask[cursor as usize].sec_start;
1309 let mut mask = &self.mask[prev as usize];
1310 loop {
1311 if mask.peek_left.is_none() {
1312 return None;
1313 }
1314
1315 prev = self.mask[mask.sec_start as usize - 1].sec_start;
1316 mask = &self.mask[prev as usize];
1317
1318 if mask.right.is_number() {
1319 return Some(mask.sec_start..mask.sec_end);
1320 } else if mask.right.is_separator() {
1321 continue;
1322 } else {
1323 return Some(mask.sec_start..mask.sec_end);
1324 }
1325 }
1326 }
1327
1328 #[inline]
1332 pub fn set_default_cursor(&mut self) {
1333 self.scroll_cursor_to_visible();
1334 if let Some(pos) = self.section_cursor(0) {
1335 self.value.set_cursor(TextPosition::new(pos, 0), false);
1336 } else if let Some(pos) = self.next_section_cursor(0) {
1337 self.value.set_cursor(TextPosition::new(pos, 0), false);
1338 } else {
1339 self.value.set_cursor(TextPosition::new(0, 0), false);
1340 }
1341 }
1342
1343 #[inline]
1345 pub fn anchor(&self) -> upos_type {
1346 self.value.anchor().x
1347 }
1348
1349 #[inline]
1351 pub fn has_selection(&self) -> bool {
1352 self.value.has_selection()
1353 }
1354
1355 #[inline]
1357 pub fn selection(&self) -> Range<upos_type> {
1358 let mut v = self.value.selection();
1359 if v.start == TextPosition::new(0, 1) {
1360 v.start = TextPosition::new(self.line_width(), 0);
1361 }
1362 if v.end == TextPosition::new(0, 1) {
1363 v.end = TextPosition::new(self.line_width(), 0);
1364 }
1365 v.start.x..v.end.x
1366 }
1367
1368 #[inline]
1371 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
1372 self.scroll_cursor_to_visible();
1373 self.value
1374 .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
1375 }
1376
1377 #[inline]
1380 pub fn select_all(&mut self) -> bool {
1381 self.scroll_cursor_to_visible();
1382 if let Some(section) = self.section_range(self.cursor()) {
1383 if self.selection() == section {
1384 self.value.select_all()
1385 } else {
1386 self.value.set_selection(
1387 TextPosition::new(section.start, 0),
1388 TextPosition::new(section.end, 0),
1389 )
1390 }
1391 } else {
1392 self.value.select_all()
1393 }
1394 }
1395
1396 #[inline]
1398 pub fn selected_text(&self) -> &str {
1399 match self
1400 .value
1401 .str_slice(self.value.selection())
1402 .expect("valid_range")
1403 {
1404 Cow::Borrowed(v) => v,
1405 Cow::Owned(_) => {
1406 unreachable!()
1407 }
1408 }
1409 }
1410}
1411
1412impl MaskedInputState {
1413 #[inline]
1415 pub fn is_empty(&self) -> bool {
1416 self.value.text().as_str() == self.default_value()
1417 }
1418
1419 #[inline]
1421 pub fn text(&self) -> &str {
1422 self.value.text().as_str()
1423 }
1424
1425 #[inline]
1427 pub fn value<T: FromStr>(&self) -> Result<T, <T as FromStr>::Err> {
1428 self.value.text().as_str().parse::<T>()
1429 }
1430
1431 #[inline]
1437 pub fn section_value<T: FromStr>(&self, section: u16) -> Result<T, <T as FromStr>::Err> {
1438 self.section_text(section).trim().parse::<T>()
1439 }
1440
1441 #[inline]
1447 pub fn set_section_value<T: ToString>(&mut self, section: u16, value: T) {
1448 let mut len = None;
1449 let mut align_left = true;
1450 for mask in &self.mask {
1451 if mask.sec_id == section {
1452 len = Some((mask.sec_end - mask.sec_start) as usize);
1453 if mask.right.is_rtol() {
1454 align_left = false;
1455 } else {
1456 align_left = true;
1457 }
1458 break;
1459 }
1460 }
1461 if let Some(len) = len {
1462 let txt = if align_left {
1463 format!("{:1$}", value.to_string(), len)
1464 } else {
1465 format!("{:>1$}", value.to_string(), len)
1466 };
1467 self.set_section_text(section, txt);
1468 } else {
1469 panic!("invalid section {}", section);
1470 }
1471 }
1472
1473 #[inline]
1479 pub fn section_text(&self, section: u16) -> &str {
1480 for v in &self.mask {
1481 if v.sec_id == section {
1482 match self.str_slice(v.sec_start..v.sec_end) {
1483 Cow::Borrowed(s) => return s,
1484 Cow::Owned(_) => {
1485 unreachable!("should not be owned")
1486 }
1487 };
1488 }
1489 }
1490 panic!("invalid section {}", section);
1491 }
1492
1493 pub fn set_section_text<S: Into<String>>(&mut self, section: u16, txt: S) {
1496 let mut txt = txt.into();
1497 for v in &self.mask {
1498 if v.sec_id == section {
1499 let len = (v.sec_end - v.sec_start) as usize;
1500 while txt.graphemes(true).count() > len {
1501 txt.pop();
1502 }
1503 while txt.graphemes(true).count() < len {
1504 txt.push(' ');
1505 }
1506 assert_eq!(txt.graphemes(true).count(), len);
1507
1508 self.value.begin_undo_seq();
1509 self.value
1510 .remove_str_range(TextRange::from(
1511 TextPosition::new(v.sec_start, 0)..TextPosition::new(v.sec_end, 0),
1512 ))
1513 .expect("valid-range");
1514 self.value
1515 .insert_str(TextPosition::new(v.sec_start, 0), txt.as_str())
1516 .expect("valid-range");
1517 self.value.end_undo_seq();
1518 return;
1519 }
1520 }
1521 panic!("invalid section {}", section);
1522 }
1523
1524 #[inline]
1526 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1527 self.value.str_slice_byte(range).expect("valid_range")
1528 }
1529
1530 #[inline]
1532 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1533 self.value.str_slice_byte(range)
1534 }
1535
1536 #[inline]
1538 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
1539 self.value
1540 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
1541 .expect("valid_range")
1542 }
1543
1544 #[inline]
1546 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
1547 self.value
1548 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
1549 }
1550
1551 #[inline]
1553 pub fn len(&self) -> upos_type {
1554 self.value.line_width(0).expect("valid_row")
1555 }
1556
1557 #[inline]
1559 pub fn len_bytes(&self) -> usize {
1560 self.value.len_bytes()
1561 }
1562
1563 #[inline]
1565 pub fn line_width(&self) -> upos_type {
1566 self.value.line_width(0).expect("valid_row")
1567 }
1568
1569 #[inline]
1571 pub fn grapheme_at(&self, pos: upos_type) -> Result<Option<Grapheme<'_>>, TextError> {
1572 self.value.grapheme_at(TextPosition::new(pos, 0))
1573 }
1574
1575 #[inline]
1577 pub fn text_graphemes(&self, pos: upos_type) -> <TextString as TextStore>::GraphemeIter<'_> {
1578 self.try_text_graphemes(pos).expect("valid_pos")
1579 }
1580
1581 #[inline]
1583 pub fn try_text_graphemes(
1584 &self,
1585 pos: upos_type,
1586 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
1587 self.value.text_graphemes(TextPosition::new(pos, 0))
1588 }
1589
1590 #[inline]
1592 pub fn graphemes(
1593 &self,
1594 range: Range<upos_type>,
1595 pos: upos_type,
1596 ) -> <TextString as TextStore>::GraphemeIter<'_> {
1597 self.try_graphemes(range, pos).expect("valid_args")
1598 }
1599
1600 #[inline]
1602 pub fn try_graphemes(
1603 &self,
1604 range: Range<upos_type>,
1605 pos: upos_type,
1606 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
1607 self.value.graphemes(
1608 TextRange::new((range.start, 0), (range.end, 0)),
1609 TextPosition::new(pos, 0),
1610 )
1611 }
1612
1613 #[inline]
1616 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
1617 self.try_byte_at(pos).expect("valid_pos")
1618 }
1619
1620 #[inline]
1623 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
1624 self.value.byte_at(TextPosition::new(pos, 0))
1625 }
1626
1627 #[inline]
1629 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
1630 self.try_bytes_at_range(range).expect("valid_range")
1631 }
1632
1633 #[inline]
1635 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
1636 self.value
1637 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
1638 }
1639
1640 #[inline]
1643 pub fn byte_pos(&self, byte: usize) -> upos_type {
1644 self.try_byte_pos(byte).expect("valid_pos")
1645 }
1646
1647 #[inline]
1650 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
1651 Ok(self.value.byte_pos(byte)?.x)
1652 }
1653
1654 #[inline]
1656 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
1657 self.try_byte_range(bytes).expect("valid_range")
1658 }
1659
1660 #[inline]
1662 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
1663 let r = self.value.byte_range(bytes)?;
1664 Ok(r.start.x..r.end.x)
1665 }
1666}
1667
1668impl MaskedInputState {
1669 #[inline]
1671 fn default_value(&self) -> String {
1672 MaskToken::empty_section(&self.mask)
1673 }
1674
1675 #[inline]
1677 pub fn clear(&mut self) -> bool {
1678 if self.is_empty() {
1679 false
1680 } else {
1681 self.offset = 0;
1682 self.value
1683 .set_text(TextString::new_string(self.default_value()));
1684 self.set_default_cursor();
1685 true
1686 }
1687 }
1688
1689 #[inline]
1693 pub fn set_value<S: ToString>(&mut self, s: S) {
1694 self.set_text(s.to_string());
1695 }
1696
1697 #[inline]
1703 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1704 self.offset = 0;
1705 let mut text = s.into();
1706 while text.graphemes(true).count() > self.mask.len().saturating_sub(1) {
1707 text.pop();
1708 }
1709 while text.graphemes(true).count() < self.mask.len().saturating_sub(1) {
1710 text.push(' ');
1711 }
1712 let len = text.graphemes(true).count();
1713
1714 assert_eq!(len, self.mask.len().saturating_sub(1));
1715
1716 self.value.set_text(TextString::new_string(text));
1717 self.set_default_cursor();
1718 }
1719
1720 #[inline]
1722 pub fn insert_char(&mut self, c: char) -> bool {
1723 self.begin_undo_seq();
1724 if self.has_selection() {
1725 let sel = self.selection();
1726 mask_op::remove_range(self, sel.clone()).expect("valid_selection");
1727 self.set_cursor(sel.start, false);
1728 }
1729 let c0 = mask_op::advance_cursor(self, c);
1730 let c1 = mask_op::insert_char(self, c);
1731 self.end_undo_seq();
1732
1733 self.scroll_cursor_to_visible();
1734 c0 || c1
1735 }
1736
1737 #[inline]
1740 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1741 self.try_delete_range(range).expect("valid_range")
1742 }
1743
1744 #[inline]
1747 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1748 self.value.begin_undo_seq();
1749 let r = mask_op::remove_range(self, range.clone())?;
1750 if let Some(pos) = self.section_cursor(range.start) {
1751 self.set_cursor(pos, false);
1752 }
1753 self.value.end_undo_seq();
1754
1755 self.scroll_cursor_to_visible();
1756 Ok(r)
1757 }
1758}
1759
1760impl MaskedInputState {
1761 #[inline]
1763 pub fn delete_next_char(&mut self) -> bool {
1764 if self.has_selection() {
1765 self.delete_range(self.selection())
1766 } else if self.cursor() == self.len() {
1767 false
1768 } else {
1769 mask_op::remove_next(self);
1770 self.scroll_cursor_to_visible();
1771 true
1772 }
1773 }
1774
1775 #[inline]
1777 pub fn delete_prev_char(&mut self) -> bool {
1778 if self.has_selection() {
1779 self.delete_range(self.selection())
1780 } else if self.cursor() == 0 {
1781 false
1782 } else {
1783 mask_op::remove_prev(self);
1784 self.scroll_cursor_to_visible();
1785 true
1786 }
1787 }
1788
1789 #[inline]
1791 pub fn delete_prev_section(&mut self) -> bool {
1792 if self.has_selection() {
1793 self.delete_range(self.selection())
1794 } else {
1795 if let Some(range) = self.prev_section_range(self.cursor()) {
1796 self.delete_range(range)
1797 } else {
1798 false
1799 }
1800 }
1801 }
1802
1803 #[inline]
1805 pub fn delete_next_section(&mut self) -> bool {
1806 if self.has_selection() {
1807 self.delete_range(self.selection())
1808 } else {
1809 if let Some(range) = self.next_section_range(self.cursor()) {
1810 self.delete_range(range)
1811 } else {
1812 false
1813 }
1814 }
1815 }
1816
1817 #[inline]
1819 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1820 let c = min(self.cursor() + 1, self.len());
1821 self.set_cursor(c, extend_selection)
1822 }
1823
1824 #[inline]
1826 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1827 let c = self.cursor().saturating_sub(1);
1828 self.set_cursor(c, extend_selection)
1829 }
1830
1831 #[inline]
1833 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1834 if let Some(c) = self.section_cursor(self.cursor()) {
1835 if c != self.cursor() {
1836 self.set_cursor(c, extend_selection)
1837 } else {
1838 self.set_cursor(0, extend_selection)
1839 }
1840 } else {
1841 self.set_cursor(0, extend_selection)
1842 }
1843 }
1844
1845 #[inline]
1847 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1848 self.set_cursor(self.len(), extend_selection)
1849 }
1850
1851 #[inline]
1853 pub fn move_to_prev_section(&mut self, extend_selection: bool) -> bool {
1854 if let Some(curr) = self.section_range(self.cursor()) {
1855 if self.cursor() != curr.start {
1856 return self.set_cursor(curr.start, extend_selection);
1857 }
1858 }
1859 if let Some(range) = self.prev_section_range(self.cursor()) {
1860 self.set_cursor(range.start, extend_selection)
1861 } else {
1862 false
1863 }
1864 }
1865
1866 #[inline]
1868 pub fn move_to_next_section(&mut self, extend_selection: bool) -> bool {
1869 if let Some(curr) = self.section_range(self.cursor()) {
1870 if self.cursor() != curr.end {
1871 return self.set_cursor(curr.end, extend_selection);
1872 }
1873 }
1874 if let Some(range) = self.next_section_range(self.cursor()) {
1875 self.set_cursor(range.end, extend_selection)
1876 } else {
1877 false
1878 }
1879 }
1880
1881 #[inline]
1883 pub fn select_current_section(&mut self) -> bool {
1884 let selection = self.selection();
1885
1886 if let Some(next) = self.section_range(selection.start.saturating_sub(1)) {
1887 if !next.is_empty() {
1888 self.set_selection(next.start, next.end)
1889 } else {
1890 false
1891 }
1892 } else {
1893 false
1894 }
1895 }
1896
1897 #[inline]
1899 pub fn select_next_section(&mut self) -> bool {
1900 let selection = self.selection();
1901
1902 if let Some(next) = self.next_section_range(selection.start) {
1903 if !next.is_empty() {
1904 self.set_selection(next.start, next.end)
1905 } else {
1906 false
1907 }
1908 } else {
1909 false
1910 }
1911 }
1912
1913 #[inline]
1915 pub fn select_prev_section(&mut self) -> bool {
1916 let selection = self.selection();
1917
1918 if let Some(next) = self.prev_section_range(selection.start.saturating_sub(1)) {
1919 if !next.is_empty() {
1920 self.set_selection(next.start, next.end)
1921 } else {
1922 false
1923 }
1924 } else {
1925 false
1926 }
1927 }
1928}
1929
1930impl HasScreenCursor for MaskedInputState {
1931 #[inline]
1933 fn screen_cursor(&self) -> Option<(u16, u16)> {
1934 if self.is_focused() {
1935 if self.has_selection() {
1936 None
1937 } else {
1938 let cx = self.cursor();
1939 let ox = self.offset();
1940
1941 if cx < ox {
1942 None
1943 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1944 None
1945 } else {
1946 self.col_to_screen(cx)
1947 .map(|sc| (self.inner.x + sc, self.inner.y))
1948 }
1949 }
1950 } else {
1951 None
1952 }
1953 }
1954}
1955
1956impl RelocatableState for MaskedInputState {
1957 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1958 self.area.relocate(shift, clip);
1959 self.inner.relocate(shift, clip);
1960 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1962 }
1963}
1964
1965impl MaskedInputState {
1966 fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1967 let left_margin = self.offset();
1968 let right_margin = self.offset() + self.rendered.width as upos_type;
1969 let compact = self.compact && !self.is_focused();
1970
1971 let grapheme_iter = self
1972 .value
1973 .graphemes(TextRange::new((0, 0), (0, 1)), TextPosition::new(0, 0))
1974 .expect("valid-rows");
1975 let mask_iter = self.mask.iter();
1976
1977 let iter = MaskedGraphemes {
1978 iter_str: grapheme_iter,
1979 iter_mask: mask_iter,
1980 compact,
1981 sym_neg: self.neg_sym().to_string(),
1982 sym_dec: self.dec_sep().to_string(),
1983 sym_grp: self.grp_sep().to_string(),
1984 sym_pos: self.pos_sym().to_string(),
1985 byte_pos: 0,
1986 };
1987
1988 let mut it = GlyphIter2::new(TextPosition::new(0, 0), 0, iter, Default::default());
1989 it.set_tabs(0 );
1990 it.set_show_ctrl(self.value.glyph_ctrl());
1991 it.set_lf_breaks(false);
1992 it.set_text_wrap(TextWrap2::Shift);
1993 it.set_left_margin(left_margin);
1994 it.set_right_margin(right_margin);
1995 it.set_word_margin(right_margin);
1996 it.prepare().expect("valid-rows");
1997
1998 Box::new(it)
1999 }
2000
2001 pub fn screen_to_col(&self, scx: i16) -> upos_type {
2004 let ox = self.offset();
2005
2006 let scx = scx + self.dark_offset.0 as i16;
2007
2008 if scx < 0 {
2009 ox.saturating_sub((scx as ipos_type).unsigned_abs())
2010 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
2011 min(ox + scx as upos_type, self.len())
2012 } else {
2013 let scx = scx as u16;
2014
2015 let line = self.glyphs2();
2016
2017 let mut col = ox;
2018 for g in line {
2019 if g.contains_screen_x(scx) {
2020 break;
2021 }
2022 col = g.pos().x + 1;
2023 }
2024 col
2025 }
2026 }
2027
2028 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
2031 let ox = self.offset();
2032
2033 if pos < ox {
2034 return None;
2035 }
2036
2037 let line = self.glyphs2();
2038 let mut screen_x = 0;
2039 for g in line {
2040 if g.pos().x == pos {
2041 break;
2042 }
2043 screen_x = g.screen_pos().0 + g.screen_width();
2044 }
2045
2046 if screen_x >= self.dark_offset.0 {
2047 Some(screen_x - self.dark_offset.0)
2048 } else {
2049 None
2050 }
2051 }
2052
2053 #[inline]
2057 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
2058 let scx = cursor;
2059
2060 let cx = self.screen_to_col(scx);
2061
2062 self.set_cursor(cx, extend_selection)
2063 }
2064
2065 pub fn set_screen_cursor_sections(
2072 &mut self,
2073 screen_cursor: i16,
2074 extend_selection: bool,
2075 ) -> bool {
2076 let anchor = self.anchor();
2077 let cursor = self.screen_to_col(screen_cursor);
2078
2079 let Some(range) = self.section_range(cursor) else {
2080 return false;
2081 };
2082
2083 let cursor = if cursor < anchor {
2084 range.start
2085 } else {
2086 range.end
2087 };
2088
2089 if !self.is_section_boundary(anchor) {
2091 if let Some(range) = self.section_range(anchor) {
2092 if cursor < anchor {
2093 self.set_cursor(range.end, false);
2094 } else {
2095 self.set_cursor(range.start, false);
2096 }
2097 };
2098 }
2099
2100 self.set_cursor(cursor, extend_selection)
2101 }
2102
2103 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
2105 self.set_offset(self.offset.saturating_sub(delta));
2106 true
2107 }
2108
2109 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
2111 self.set_offset(self.offset + delta);
2112 true
2113 }
2114
2115 pub fn scroll_cursor_to_visible(&mut self) {
2117 self.scroll_to_cursor.set(true);
2118 }
2119}
2120
2121impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for MaskedInputState {
2122 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2123 fn tc(r: bool) -> TextOutcome {
2125 if r {
2126 TextOutcome::TextChanged
2127 } else {
2128 TextOutcome::Unchanged
2129 }
2130 }
2131 fn overwrite(state: &mut MaskedInputState) {
2132 if state.overwrite.get() {
2133 state.overwrite.set(false);
2134 state.clear();
2135 }
2136 }
2137 fn clear_overwrite(state: &mut MaskedInputState) {
2138 state.overwrite.set(false);
2139 }
2140
2141 let mut r = if self.is_focused() {
2142 match event {
2143 ct_event!(key press c)
2144 | ct_event!(key press SHIFT-c)
2145 | ct_event!(key press CONTROL_ALT-c) => {
2146 overwrite(self);
2147 tc(self.insert_char(*c))
2148 }
2149 ct_event!(keycode press Backspace) | ct_event!(keycode press SHIFT-Backspace) => {
2150 clear_overwrite(self);
2151 tc(self.delete_prev_char())
2152 }
2153 ct_event!(keycode press Delete) | ct_event!(keycode press SHIFT-Delete) => {
2154 clear_overwrite(self);
2155 tc(self.delete_next_char())
2156 }
2157 ct_event!(keycode press CONTROL-Backspace)
2158 | ct_event!(keycode press ALT-Backspace) => {
2159 clear_overwrite(self);
2160 tc(self.delete_prev_section())
2161 }
2162 ct_event!(keycode press CONTROL-Delete) => {
2163 clear_overwrite(self);
2164 tc(self.delete_next_section())
2165 }
2166 ct_event!(key press CONTROL-'x') => {
2167 clear_overwrite(self);
2168 tc(self.cut_to_clip())
2169 }
2170 ct_event!(key press CONTROL-'v') => {
2171 clear_overwrite(self);
2172 tc(self.paste_from_clip())
2173 }
2174 ct_event!(key press CONTROL-'d') => {
2175 clear_overwrite(self);
2176 tc(self.clear())
2177 }
2178 ct_event!(key press CONTROL-'z') => {
2179 clear_overwrite(self);
2180 tc(self.undo())
2181 }
2182 ct_event!(key press CONTROL_SHIFT-'Z') => {
2183 clear_overwrite(self);
2184 tc(self.redo())
2185 }
2186 ct_event!(keycode press Tab) => {
2187 if self.on_tab == TextTab::MoveToNextSection {
2188 if !self.focus.gained() {
2190 clear_overwrite(self);
2191 self.select_next_section().into()
2192 } else {
2193 TextOutcome::Unchanged
2194 }
2195 } else {
2196 TextOutcome::Continue
2197 }
2198 }
2199 ct_event!(keycode press SHIFT-BackTab) => {
2200 if self.on_tab == TextTab::MoveToNextSection {
2201 if !self.focus.gained() {
2203 clear_overwrite(self);
2204 self.select_prev_section().into()
2205 } else {
2206 TextOutcome::Unchanged
2207 }
2208 } else {
2209 TextOutcome::Continue
2210 }
2211 }
2212 ct_event!(keycode press ALT-Left) => {
2213 clear_overwrite(self);
2214 self.select_prev_section().into()
2215 }
2216 ct_event!(keycode press ALT-Right) => {
2217 clear_overwrite(self);
2218 self.select_next_section().into()
2219 }
2220
2221 ct_event!(key release _)
2222 | ct_event!(key release SHIFT-_)
2223 | ct_event!(key release CONTROL_ALT-_)
2224 | ct_event!(keycode release Backspace)
2225 | ct_event!(keycode release Delete)
2226 | ct_event!(keycode release CONTROL-Backspace)
2227 | ct_event!(keycode release ALT-Backspace)
2228 | ct_event!(keycode release CONTROL-Delete)
2229 | ct_event!(key release CONTROL-'x')
2230 | ct_event!(key release CONTROL-'v')
2231 | ct_event!(key release CONTROL-'d')
2232 | ct_event!(key release CONTROL-'z')
2233 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2234
2235 _ => TextOutcome::Continue,
2236 }
2237 } else {
2238 TextOutcome::Continue
2239 };
2240
2241 if r == TextOutcome::Continue {
2242 r = self.handle(event, ReadOnly);
2243 }
2244 r
2245 }
2246}
2247
2248impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for MaskedInputState {
2249 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2250 fn clear_overwrite(state: &mut MaskedInputState) {
2251 state.overwrite.set(false);
2252 }
2253
2254 let mut r = if self.is_focused() {
2255 match event {
2256 ct_event!(keycode press Left) => {
2257 clear_overwrite(self);
2258 self.move_left(false).into()
2259 }
2260 ct_event!(keycode press Right) => {
2261 clear_overwrite(self);
2262 self.move_right(false).into()
2263 }
2264 ct_event!(keycode press CONTROL-Left) => {
2265 clear_overwrite(self);
2266 self.move_to_prev_section(false).into()
2267 }
2268 ct_event!(keycode press CONTROL-Right) => {
2269 clear_overwrite(self);
2270 self.move_to_next_section(false).into()
2271 }
2272 ct_event!(keycode press Home) => {
2273 clear_overwrite(self);
2274 self.move_to_line_start(false).into()
2275 }
2276 ct_event!(keycode press End) => {
2277 clear_overwrite(self);
2278 self.move_to_line_end(false).into()
2279 }
2280 ct_event!(keycode press SHIFT-Left) => {
2281 clear_overwrite(self);
2282 self.move_left(true).into()
2283 }
2284 ct_event!(keycode press SHIFT-Right) => {
2285 clear_overwrite(self);
2286 self.move_right(true).into()
2287 }
2288 ct_event!(keycode press CONTROL_SHIFT-Left) => {
2289 clear_overwrite(self);
2290 self.move_to_prev_section(true).into()
2291 }
2292 ct_event!(keycode press CONTROL_SHIFT-Right) => {
2293 clear_overwrite(self);
2294 self.move_to_next_section(true).into()
2295 }
2296 ct_event!(keycode press SHIFT-Home) => {
2297 clear_overwrite(self);
2298 self.move_to_line_start(true).into()
2299 }
2300 ct_event!(keycode press SHIFT-End) => {
2301 clear_overwrite(self);
2302 self.move_to_line_end(true).into()
2303 }
2304 ct_event!(key press CONTROL-'a') => {
2305 clear_overwrite(self);
2306 self.select_all().into()
2307 }
2308 ct_event!(key press CONTROL-'c') => {
2309 clear_overwrite(self);
2310 self.copy_to_clip().into()
2311 }
2312
2313 ct_event!(keycode release Left)
2314 | ct_event!(keycode release Right)
2315 | ct_event!(keycode release CONTROL-Left)
2316 | ct_event!(keycode release CONTROL-Right)
2317 | ct_event!(keycode release Home)
2318 | ct_event!(keycode release End)
2319 | ct_event!(keycode release SHIFT-Left)
2320 | ct_event!(keycode release SHIFT-Right)
2321 | ct_event!(keycode release CONTROL_SHIFT-Left)
2322 | ct_event!(keycode release CONTROL_SHIFT-Right)
2323 | ct_event!(keycode release SHIFT-Home)
2324 | ct_event!(keycode release SHIFT-End)
2325 | ct_event!(key release CONTROL-'a')
2326 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2327
2328 _ => TextOutcome::Continue,
2329 }
2330 } else {
2331 TextOutcome::Continue
2332 };
2333
2334 if r == TextOutcome::Continue {
2335 r = self.handle(event, MouseOnly);
2336 }
2337 r
2338 }
2339}
2340
2341impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for MaskedInputState {
2342 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
2343 fn clear_overwrite(state: &mut MaskedInputState) {
2344 state.overwrite.set(false);
2345 }
2346
2347 match event {
2348 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2349 let c = (m.column as i16) - (self.inner.x as i16);
2350 clear_overwrite(self);
2351 self.set_screen_cursor(c, true).into()
2352 }
2353 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2354 let cx = m.column as i16 - self.inner.x as i16;
2355 clear_overwrite(self);
2356 self.set_screen_cursor_sections(cx, true).into()
2357 }
2358 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2359 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
2360 clear_overwrite(self);
2361 if let Some(range) = self.section_range(tx) {
2362 self.set_selection(range.start, range.end).into()
2363 } else {
2364 TextOutcome::Unchanged
2365 }
2366 }
2367 ct_event!(mouse down Left for column,row) => {
2368 if self.gained_focus() {
2369 TextOutcome::Unchanged
2372 } else if self.inner.contains((*column, *row).into()) {
2373 let c = (column - self.inner.x) as i16;
2374 clear_overwrite(self);
2375 self.set_screen_cursor(c, false).into()
2376 } else {
2377 TextOutcome::Continue
2378 }
2379 }
2380 ct_event!(mouse down CONTROL-Left for column,row) => {
2381 if self.inner.contains((*column, *row).into()) {
2382 let cx = (column - self.inner.x) as i16;
2383 clear_overwrite(self);
2384 self.set_screen_cursor(cx, true).into()
2385 } else {
2386 TextOutcome::Continue
2387 }
2388 }
2389 ct_event!(mouse down ALT-Left for column,row) => {
2390 if self.inner.contains((*column, *row).into()) {
2391 let cx = (column - self.inner.x) as i16;
2392 clear_overwrite(self);
2393 self.set_screen_cursor_sections(cx, true).into()
2394 } else {
2395 TextOutcome::Continue
2396 }
2397 }
2398 _ => TextOutcome::Continue,
2399 }
2400 }
2401}
2402
2403pub fn handle_events(
2407 state: &mut MaskedInputState,
2408 focus: bool,
2409 event: &crossterm::event::Event,
2410) -> TextOutcome {
2411 state.focus.set(focus);
2412 state.handle(event, Regular)
2413}
2414
2415pub fn handle_readonly_events(
2419 state: &mut TextInputState,
2420 focus: bool,
2421 event: &crossterm::event::Event,
2422) -> TextOutcome {
2423 state.focus.set(focus);
2424 state.handle(event, ReadOnly)
2425}
2426
2427pub fn handle_mouse_events(
2429 state: &mut MaskedInputState,
2430 event: &crossterm::event::Event,
2431) -> TextOutcome {
2432 state.handle(event, MouseOnly)
2433}