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