rat_text/
text_input_mask.rs

1//! Text input widget with an input mask.
2//!
3//! * Can do the usual insert/delete/move operations.
4//! * Text selection with keyboard + mouse
5//! * Scrolls with the cursor.
6//! * Modes for focus and valid.
7//! * Localization with [format_num_pattern::NumberSymbols]
8//!
9//! * Accepts an input mask:
10//!   * `0`: can enter digit, display as 0
11//!   * `9`: can enter digit, display as space
12//!   * `#`: digit, plus or minus sign, display as space
13//!   * `-`: sign
14//!   * `+`: sign, positive is '+', negative is '-', not localized.
15//!   * `.` and `,`: decimal and grouping separators
16//!
17//!   * `H`: must enter a hex digit, display as 0
18//!   * `h`: can enter a hex digit, display as space
19//!   * `O`: must enter an octal digit, display as 0
20//!   * `o`: can enter an octal digit, display as space
21//!   * `D`: must enter a decimal digit, display as 0
22//!   * `d`: can enter a decimal digit, display as space
23//!
24//!   * `l`: can enter letter, display as space
25//!   * `a`: can enter letter or digit, display as space
26//!   * `c`: can enter character or space, display as space
27//!   * `_`: anything, display as space
28//!
29//!   * `<space>` separator character move the cursor when entered.
30//!   * `\`: escapes the following character and uses it as a separator.
31//!   * everything else must be escaped
32//!
33//! * Accepts a display overlay used instead of the default chars of the input mask.
34//!
35//! ```rust ignore
36//! use ratatui::widgets::StatefulWidget;
37//! use rat_input::masked_input::{MaskedInput, MaskedInputState};
38//!
39//! let date_focused = false;
40//! let creditcard_focused = true;
41//! let area = Rect::default();
42//! let buf = Buffer::default();
43//!
44//! let mut date_state = MaskedInputState::new();
45//! date_state.set_mask("99\\/99\\/9999")?;
46//!
47//! let w_date = MaskedInput::default();
48//! w_date.render(area, &mut buf, &mut date_state);
49//! if date_focused {
50//!     frame.set_cursor(date_state.cursor.x, date_state.cursor.y);
51//! }
52//!
53//! let mut creditcard_state = MaskedInputState::new();
54//! creditcard_state.set_mask("dddd dddd dddd dddd")?;
55//!
56//! let w_creditcard = MaskedInput::default();
57//! w_creditcard.render(area, &mut buf, &mut creditcard_state);
58//! if creditcard_focused {
59//!     frame.set_cursor(creditcard_state.cursor.x, creditcard_state.cursor.y);
60//! }
61//!
62//! ```
63//!
64//! The visual cursor must be set separately after rendering.
65//! It is accessible as [TextInputState::screen_cursor()] after rendering.
66//!
67//! Event handling by calling the freestanding fn [handle_events].
68//! There's [handle_mouse_events] if you want to override the default key bindings but keep
69//! the mouse behaviour.
70//!
71
72pub 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, 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_area, 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::cmp::min;
103use std::fmt;
104use std::iter::once;
105use std::ops::Range;
106use unicode_segmentation::UnicodeSegmentation;
107
108/// Text input widget with input mask.
109///
110/// # Stateful
111/// This widget implements [`StatefulWidget`], you can use it with
112/// [`MaskedInputState`] to handle common actions.
113#[derive(Debug, Default, Clone)]
114pub struct MaskedInput<'a> {
115    compact: bool,
116    block: Option<Block<'a>>,
117    style: Style,
118    focus_style: Option<Style>,
119    select_style: Option<Style>,
120    invalid_style: Option<Style>,
121    text_style: Vec<Style>,
122    on_focus_gained: TextFocusGained,
123    on_focus_lost: TextFocusLost,
124}
125
126/// State & event-handling.
127#[derive(Debug)]
128pub struct MaskedInputState {
129    /// The whole area with block.
130    /// __read only__ renewed with each render.
131    pub area: Rect,
132    /// Area inside a possible block.
133    /// __read only__ renewed with each render.
134    pub inner: Rect,
135    /// Rendered dimension. This may differ from (inner.width, inner.height)
136    /// if the text area has been relocated.
137    pub rendered: Size,
138    /// Widget has been rendered in compact mode.
139    /// __read only: renewed with each render.
140    pub compact: bool,
141
142    /// Display offset
143    /// __read+write__
144    pub offset: upos_type,
145    /// Dark offset due to clipping.
146    /// __read only__ secondary offset due to clipping.
147    pub dark_offset: (u16, u16),
148    /// __read+write__ use scroll_cursor_to_visible().
149    pub scroll_to_cursor: bool,
150
151    /// Editing core
152    pub value: TextCore<TextString>,
153    /// Editing core
154    pub sym: Option<NumberSymbols>,
155    /// Editing core
156    pub mask: Vec<MaskToken>,
157
158    /// Display as invalid.
159    /// __read+write__
160    pub invalid: bool,
161    /// Any edit will clear the value first.
162    /// This flag will be reset by any edit and navigation.
163    pub overwrite: bool,
164    /// Focus behaviour.
165    /// __read only__
166    pub on_focus_gained: TextFocusGained,
167    /// Focus behaviour.
168    /// __read only__
169    pub on_focus_lost: TextFocusLost,
170
171    /// Current focus state.
172    /// __read+write__
173    pub focus: FocusFlag,
174
175    /// Mouse selection in progress.
176    /// __read+write__
177    pub mouse: MouseFlags,
178
179    /// Construct with `..Default::default()`
180    pub non_exhaustive: NonExhaustive,
181}
182
183impl<'a> MaskedInput<'a> {
184    /// New widget.
185    pub fn new() -> Self {
186        Self::default()
187    }
188
189    /// Show a compact form of the content without unnecessary spaces,
190    /// if this widget is not focused.
191    #[inline]
192    pub fn compact(mut self, show_compact: bool) -> Self {
193        self.compact = show_compact;
194        self
195    }
196
197    /// Set the combined style.
198    #[inline]
199    pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
200        if let Some(styles) = styles {
201            self.styles(styles)
202        } else {
203            self
204        }
205    }
206
207    /// Set the combined style.
208    #[inline]
209    pub fn styles(mut self, styles: TextStyle) -> Self {
210        self.style = styles.style;
211        if styles.focus.is_some() {
212            self.focus_style = styles.focus;
213        }
214        if styles.select.is_some() {
215            self.select_style = styles.select;
216        }
217        if styles.invalid.is_some() {
218            self.invalid_style = styles.invalid;
219        }
220        if let Some(of) = styles.on_focus_gained {
221            self.on_focus_gained = of;
222        }
223        if let Some(of) = styles.on_focus_lost {
224            self.on_focus_lost = of;
225        }
226        if let Some(border_style) = styles.border_style {
227            self.block = self.block.map(|v| v.border_style(border_style));
228        }
229        self.block = self.block.map(|v| v.style(self.style));
230        if styles.block.is_some() {
231            self.block = styles.block;
232        }
233        self.block = self.block.map(|v| v.style(self.style));
234        self
235    }
236
237    /// Base text style.
238    #[inline]
239    pub fn style(mut self, style: impl Into<Style>) -> Self {
240        self.style = style.into();
241        self.block = self.block.map(|v| v.style(self.style));
242        self
243    }
244
245    /// Style when focused.
246    #[inline]
247    pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
248        self.focus_style = Some(style.into());
249        self
250    }
251
252    /// Style for selection
253    #[inline]
254    pub fn select_style(mut self, style: impl Into<Style>) -> Self {
255        self.select_style = Some(style.into());
256        self
257    }
258
259    /// Style for the invalid indicator.
260    /// This is patched onto either base_style or focus_style
261    #[inline]
262    pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
263        self.invalid_style = Some(style.into());
264        self
265    }
266
267    /// List of text-styles.
268    ///
269    /// Use [MaskedInputState::add_style()] to refer a text range to
270    /// one of these styles.
271    pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
272        self.text_style = styles.into_iter().collect();
273        self
274    }
275
276    /// Block.
277    #[inline]
278    pub fn block(mut self, block: Block<'a>) -> Self {
279        self.block = Some(block);
280        self.block = self.block.map(|v| v.style(self.style));
281        self
282    }
283
284    /// Focus behaviour
285    #[inline]
286    pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
287        self.on_focus_gained = of;
288        self
289    }
290
291    /// Focus behaviour
292    #[inline]
293    pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
294        self.on_focus_lost = of;
295        self
296    }
297}
298
299impl<'a> StatefulWidget for &MaskedInput<'a> {
300    type State = MaskedInputState;
301
302    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
303        render_ref(self, area, buf, state);
304    }
305}
306
307impl StatefulWidget for MaskedInput<'_> {
308    type State = MaskedInputState;
309
310    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
311        render_ref(&self, area, buf, state);
312    }
313}
314
315fn render_ref(
316    widget: &MaskedInput<'_>,
317    area: Rect,
318
319    buf: &mut Buffer,
320    state: &mut MaskedInputState,
321) {
322    state.area = area;
323    state.inner = widget.block.inner_if_some(area);
324    state.rendered = state.inner.as_size();
325    state.compact = widget.compact;
326    state.on_focus_gained = widget.on_focus_gained;
327    state.on_focus_lost = widget.on_focus_lost;
328
329    if state.scroll_to_cursor {
330        let c = state.cursor();
331        let o = state.offset();
332        let mut no = if c < o {
333            c
334        } else if c >= o + state.rendered.width as upos_type {
335            c.saturating_sub(state.rendered.width as upos_type)
336        } else {
337            o
338        };
339        // correct by one at right margin. block cursors appear as part of the
340        // right border otherwise.
341        if c == no + state.rendered.width as upos_type {
342            no = no.saturating_add(1);
343        }
344        state.set_offset(no);
345    }
346
347    let style = widget.style;
348    let focus_style = if let Some(focus_style) = widget.focus_style {
349        focus_style
350    } else {
351        style
352    };
353    let select_style = if let Some(select_style) = widget.select_style {
354        select_style
355    } else {
356        Style::default().black().on_yellow()
357    };
358    let invalid_style = if let Some(invalid_style) = widget.invalid_style {
359        invalid_style
360    } else {
361        Style::default().red()
362    };
363
364    let (style, select_style) = if state.focus.get() {
365        if state.invalid {
366            (
367                style.patch(focus_style).patch(invalid_style),
368                style
369                    .patch(focus_style)
370                    .patch(select_style)
371                    .patch(invalid_style),
372            )
373        } else {
374            (
375                style.patch(focus_style),
376                style.patch(focus_style).patch(select_style),
377            )
378        }
379    } else {
380        if state.invalid {
381            (
382                style.patch(invalid_style),
383                style.patch(select_style).patch(invalid_style),
384            )
385        } else {
386            (style, style.patch(select_style))
387        }
388    };
389
390    // set base style
391    if let Some(block) = &widget.block {
392        block.render(area, buf);
393    } else {
394        buf.set_style(area, style);
395    }
396
397    if state.inner.width == 0 || state.inner.height == 0 {
398        // noop
399        return;
400    }
401
402    let ox = state.offset() as u16;
403    // this is just a guess at the display-width
404    let show_range = {
405        let start = ox as upos_type;
406        let end = min(start + state.inner.width as upos_type, state.len());
407        state.bytes_at_range(start..end)
408    };
409    let selection = state.selection();
410    let mut styles = Vec::new();
411
412    for g in state.glyphs2() {
413        if g.screen_width() > 0 {
414            let mut style = style;
415            styles.clear();
416            state
417                .value
418                .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
419            for style_nr in &styles {
420                if let Some(s) = widget.text_style.get(*style_nr) {
421                    style = style.patch(*s);
422                }
423            }
424            // selection
425            if selection.contains(&g.pos().x) {
426                style = style.patch(select_style);
427            };
428
429            // relative screen-pos of the glyph
430            let screen_pos = g.screen_pos();
431
432            // render glyph
433            if let Some(cell) =
434                buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
435            {
436                cell.set_symbol(g.glyph());
437                cell.set_style(style);
438            }
439            // clear the reset of the cells to avoid interferences.
440            for d in 1..g.screen_width() {
441                if let Some(cell) = buf.cell_mut((
442                    state.inner.x + screen_pos.0 + d,
443                    state.inner.y + screen_pos.1,
444                )) {
445                    cell.reset();
446                    cell.set_style(style);
447                }
448            }
449        }
450    }
451}
452
453impl Clone for MaskedInputState {
454    fn clone(&self) -> Self {
455        Self {
456            area: self.area,
457            inner: self.inner,
458            rendered: self.rendered,
459            compact: self.compact,
460            offset: self.offset,
461            dark_offset: self.dark_offset,
462            scroll_to_cursor: self.scroll_to_cursor,
463            value: self.value.clone(),
464            sym: self.sym,
465            mask: self.mask.clone(),
466            invalid: self.invalid,
467            overwrite: Default::default(),
468            on_focus_gained: Default::default(),
469            on_focus_lost: Default::default(),
470            focus: FocusFlag::named(self.focus.name()),
471            mouse: Default::default(),
472            non_exhaustive: NonExhaustive,
473        }
474    }
475}
476
477impl Default for MaskedInputState {
478    fn default() -> Self {
479        let core = TextCore::new(
480            Some(Box::new(UndoVec::new(99))),
481            Some(Box::new(global_clipboard())),
482        );
483
484        Self {
485            area: Default::default(),
486            inner: Default::default(),
487            rendered: Default::default(),
488            compact: Default::default(),
489            offset: Default::default(),
490            dark_offset: Default::default(),
491            scroll_to_cursor: Default::default(),
492            value: core,
493            sym: None,
494            mask: Default::default(),
495            invalid: Default::default(),
496            overwrite: Default::default(),
497            on_focus_gained: Default::default(),
498            on_focus_lost: Default::default(),
499            focus: Default::default(),
500            mouse: Default::default(),
501            non_exhaustive: NonExhaustive,
502        }
503    }
504}
505
506impl HasFocus for MaskedInputState {
507    fn build(&self, builder: &mut FocusBuilder) {
508        builder.leaf_widget(self);
509    }
510
511    fn focus(&self) -> FocusFlag {
512        self.focus.clone()
513    }
514
515    fn area(&self) -> Rect {
516        self.area
517    }
518
519    fn navigable(&self) -> Navigation {
520        let sel = self.selection();
521
522        let has_next = self
523            .next_section_range(sel.end)
524            .map(|v| !v.is_empty())
525            .is_some();
526        let has_prev = self
527            .prev_section_range(sel.start.saturating_sub(1))
528            .map(|v| !v.is_empty())
529            .is_some();
530
531        if has_next {
532            if has_prev {
533                Navigation::Reach
534            } else {
535                Navigation::ReachLeaveFront
536            }
537        } else {
538            if has_prev {
539                Navigation::ReachLeaveBack
540            } else {
541                Navigation::Regular
542            }
543        }
544    }
545}
546
547impl MaskedInputState {
548    pub fn new() -> Self {
549        Self::default()
550    }
551
552    pub fn named(name: &str) -> Self {
553        Self {
554            focus: FocusFlag::named(name),
555            ..MaskedInputState::default()
556        }
557    }
558
559    /// With localized symbols for number formatting.
560    #[inline]
561    pub fn with_symbols(mut self, sym: NumberSymbols) -> Self {
562        self.set_num_symbols(sym);
563        self
564    }
565
566    /// With input mask.
567    pub fn with_mask<S: AsRef<str>>(mut self, mask: S) -> Result<Self, fmt::Error> {
568        self.set_mask(mask.as_ref())?;
569        Ok(self)
570    }
571
572    /// Set symbols for number display.
573    ///
574    /// These are only used for rendering and to map user input.
575    /// The value itself uses ".", "," and "-".
576    #[inline]
577    pub fn set_num_symbols(&mut self, sym: NumberSymbols) {
578        self.sym = Some(sym);
579    }
580
581    fn dec_sep(&self) -> char {
582        if let Some(sym) = &self.sym {
583            sym.decimal_sep
584        } else {
585            '.'
586        }
587    }
588
589    fn grp_sep(&self) -> char {
590        if let Some(sym) = &self.sym {
591            // fallback for empty grp-char.
592            // it would be really ugly, if we couldn't keep
593            //   mask-idx == grapheme-idx
594            sym.decimal_grp.unwrap_or(' ')
595        } else {
596            ','
597        }
598    }
599
600    fn neg_sym(&self) -> char {
601        if let Some(sym) = &self.sym {
602            sym.negative_sym
603        } else {
604            '-'
605        }
606    }
607
608    fn pos_sym(&self) -> char {
609        if let Some(sym) = &self.sym {
610            sym.positive_sym
611        } else {
612            ' '
613        }
614    }
615
616    /// Set the input mask. This overwrites the display mask and the value
617    /// with a default representation of the mask.
618    ///
619    /// The result value contains all punctuation and
620    /// the value given as 'display' below.
621    ///
622    /// * `0`: can enter digit, display as 0
623    /// * `9`: can enter digit, display as space
624    /// * `#`: digit, plus or minus sign, display as space
625    /// * `+`: sign. display '+' for positive
626    /// * `-`: sign. display ' ' for positive
627    /// * `.` and `,`: decimal and grouping separators
628    ///
629    /// * `H`: must enter a hex digit, display as 0
630    /// * `h`: can enter a hex digit, display as space
631    /// * `O`: must enter an octal digit, display as 0
632    /// * `o`: can enter an octal digit, display as space
633    /// * `D`: must enter a decimal digit, display as 0
634    /// * `d`: can enter a decimal digit, display as space
635    ///
636    /// * `l`: can enter letter, display as space
637    /// * `a`: can enter letter or digit, display as space
638    /// * `c`: can enter character or space, display as space
639    /// * `_`: anything, display as space
640    ///
641    /// * `SPACE`: separator character move the cursor when entered.
642    /// * `\`: escapes the following character and uses it as a separator.
643    /// * all other ascii characters a reserved.
644    ///
645    /// Inspired by <https://support.microsoft.com/en-gb/office/control-data-entry-formats-with-input-masks-e125997a-7791-49e5-8672-4a47832de8da>
646    #[inline]
647    pub fn set_mask<S: AsRef<str>>(&mut self, s: S) -> Result<(), fmt::Error> {
648        self.mask = Self::parse_mask(s.as_ref())?;
649        self.clear();
650        Ok(())
651    }
652
653    #[allow(clippy::needless_range_loop)]
654    fn parse_mask(mask_str: &str) -> Result<Vec<MaskToken>, fmt::Error> {
655        let mut out = Vec::<MaskToken>::new();
656
657        let mut start_sub = 0;
658        let mut start_sec = 0;
659        let mut sec_id = 0;
660        let mut last_mask = Mask::None;
661        let mut dec_dir = EditDirection::Rtol;
662        let mut esc = false;
663        let mut idx = 0;
664        for m in mask_str.graphemes(true).chain(once("")) {
665            let mask = if esc {
666                esc = false;
667                Mask::Separator(Box::from(m))
668            } else {
669                match m {
670                    "0" => Mask::Digit0(dec_dir),
671                    "9" => Mask::Digit(dec_dir),
672                    "#" => Mask::Numeric(dec_dir),
673                    "." => Mask::DecimalSep,
674                    "," => Mask::GroupingSep,
675                    "-" => Mask::Sign,
676                    "+" => Mask::Plus,
677                    "h" => Mask::Hex,
678                    "H" => Mask::Hex0,
679                    "o" => Mask::Oct,
680                    "O" => Mask::Oct0,
681                    "d" => Mask::Dec,
682                    "D" => Mask::Dec0,
683                    "l" => Mask::Letter,
684                    "a" => Mask::LetterOrDigit,
685                    "c" => Mask::LetterDigitSpace,
686                    "_" => Mask::AnyChar,
687                    "" => Mask::None,
688                    " " => Mask::Separator(Box::from(m)),
689                    "\\" => {
690                        esc = true;
691                        continue;
692                    }
693                    _ => return Err(fmt::Error),
694                }
695            };
696
697            match mask {
698                Mask::Digit0(_)
699                | Mask::Digit(_)
700                | Mask::Numeric(_)
701                | Mask::GroupingSep
702                | Mask::Sign
703                | Mask::Plus => {
704                    // no change
705                }
706                Mask::DecimalSep => {
707                    dec_dir = EditDirection::Ltor;
708                }
709                Mask::Hex0
710                | Mask::Hex
711                | Mask::Oct0
712                | Mask::Oct
713                | Mask::Dec0
714                | Mask::Dec
715                | Mask::Letter
716                | Mask::LetterOrDigit
717                | Mask::LetterDigitSpace
718                | Mask::AnyChar
719                | Mask::Separator(_) => {
720                    // reset to default number input direction
721                    dec_dir = EditDirection::Rtol
722                }
723                Mask::None => {
724                    // no change, doesn't matter
725                }
726            }
727
728            if matches!(mask, Mask::Separator(_)) || mask.section() != last_mask.section() {
729                for j in start_sec..idx {
730                    out[j].sec_id = sec_id;
731                    out[j].sec_start = start_sec as upos_type;
732                    out[j].sec_end = idx as upos_type;
733                }
734                sec_id += 1;
735                start_sec = idx;
736            }
737            if matches!(mask, Mask::Separator(_)) || mask.sub_section() != last_mask.sub_section() {
738                for j in start_sub..idx {
739                    out[j].sub_start = start_sub as upos_type;
740                    out[j].sub_end = idx as upos_type;
741                }
742                start_sub = idx;
743            }
744
745            let tok = MaskToken {
746                sec_id: 0,
747                sec_start: 0,
748                sec_end: 0,
749                sub_start: 0,
750                sub_end: 0,
751                peek_left: last_mask,
752                right: mask.clone(),
753                edit: mask.edit_value().into(),
754            };
755            out.push(tok);
756
757            idx += 1;
758            last_mask = mask;
759        }
760        for j in start_sec..out.len() {
761            out[j].sec_id = sec_id;
762            out[j].sec_start = start_sec as upos_type;
763            out[j].sec_end = mask_str.graphemes(true).count() as upos_type;
764        }
765        for j in start_sub..out.len() {
766            out[j].sub_start = start_sub as upos_type;
767            out[j].sub_end = mask_str.graphemes(true).count() as upos_type;
768        }
769
770        Ok(out)
771    }
772
773    /// Display mask.
774    #[inline]
775    pub fn mask(&self) -> String {
776        use std::fmt::Write;
777
778        let mut buf = String::new();
779        for t in self.mask.iter() {
780            _ = write!(buf, "{}", t.right);
781        }
782        buf
783    }
784
785    /// Renders the widget in invalid style.
786    #[inline]
787    pub fn set_invalid(&mut self, invalid: bool) {
788        self.invalid = invalid;
789    }
790
791    /// Renders the widget in invalid style.
792    #[inline]
793    pub fn get_invalid(&self) -> bool {
794        self.invalid
795    }
796
797    /// The next edit operation will overwrite the current content
798    /// instead of adding text. Any move operations will cancel
799    /// this overwrite.
800    #[inline]
801    pub fn set_overwrite(&mut self, overwrite: bool) {
802        self.overwrite = overwrite;
803    }
804
805    /// Will the next edit operation overwrite the content?
806    #[inline]
807    pub fn overwrite(&self) -> bool {
808        self.overwrite
809    }
810}
811
812impl MaskedInputState {
813    /// Clipboard used.
814    /// Default is to use the global_clipboard().
815    #[inline]
816    pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
817        self.value.set_clipboard(clip.map(|v| {
818            let r: Box<dyn Clipboard> = Box::new(v);
819            r
820        }));
821    }
822
823    /// Clipboard used.
824    /// Default is to use the global_clipboard().
825    #[inline]
826    pub fn clipboard(&self) -> Option<&dyn Clipboard> {
827        self.value.clipboard()
828    }
829
830    /// Copy to internal buffer
831    #[inline]
832    pub fn copy_to_clip(&mut self) -> bool {
833        let Some(clip) = self.value.clipboard() else {
834            return false;
835        };
836
837        _ = clip.set_string(self.selected_text().as_ref());
838
839        true
840    }
841
842    /// Cut to internal buffer
843    #[inline]
844    pub fn cut_to_clip(&mut self) -> bool {
845        let Some(clip) = self.value.clipboard() else {
846            return false;
847        };
848
849        match clip.set_string(self.selected_text().as_ref()) {
850            Ok(_) => self.delete_range(self.selection()),
851            Err(_) => true,
852        }
853    }
854
855    /// Paste from internal buffer.
856    #[inline]
857    pub fn paste_from_clip(&mut self) -> bool {
858        let Some(clip) = self.value.clipboard() else {
859            return false;
860        };
861
862        if let Ok(text) = clip.get_string() {
863            for c in text.chars() {
864                self.insert_char(c);
865            }
866            true
867        } else {
868            false
869        }
870    }
871}
872
873impl MaskedInputState {
874    /// Set undo buffer.
875    #[inline]
876    pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
877        self.value.set_undo_buffer(undo.map(|v| {
878            let r: Box<dyn UndoBuffer> = Box::new(v);
879            r
880        }));
881    }
882
883    /// Undo
884    #[inline]
885    pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
886        self.value.undo_buffer()
887    }
888
889    /// Undo
890    #[inline]
891    pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
892        self.value.undo_buffer_mut()
893    }
894
895    /// Get all recent replay recordings.
896    #[inline]
897    pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
898        self.value.recent_replay_log()
899    }
900
901    /// Apply the replay recording.
902    #[inline]
903    pub fn replay_log(&mut self, replay: &[UndoEntry]) {
904        self.value.replay_log(replay)
905    }
906
907    /// Begin a sequence of changes that should be undone in one go.
908    #[inline]
909    pub fn begin_undo_seq(&mut self) {
910        self.value.begin_undo_seq();
911    }
912
913    /// End a sequence of changes that should be undone in one go.
914    #[inline]
915    pub fn end_undo_seq(&mut self) {
916        self.value.end_undo_seq();
917    }
918
919    /// Undo operation
920    #[inline]
921    pub fn undo(&mut self) -> bool {
922        self.value.undo()
923    }
924
925    /// Redo operation
926    #[inline]
927    pub fn redo(&mut self) -> bool {
928        self.value.redo()
929    }
930}
931
932impl MaskedInputState {
933    /// Set and replace all styles.
934    #[inline]
935    pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
936        self.value.set_styles(styles);
937    }
938
939    /// Add a style for a byte-range. The style-nr refers to
940    /// one of the styles set with the widget.
941    #[inline]
942    pub fn add_style(&mut self, range: Range<usize>, style: usize) {
943        self.value.add_style(range, style);
944    }
945
946    /// Add a style for a `Range<upos_type>` to denote the cells.
947    /// The style-nr refers to one of the styles set with the widget.
948    #[inline]
949    pub fn add_range_style(
950        &mut self,
951        range: Range<upos_type>,
952        style: usize,
953    ) -> Result<(), TextError> {
954        let r = self
955            .value
956            .bytes_at_range(TextRange::from((range.start, 0)..(range.end, 0)))?;
957        self.value.add_style(r, style);
958        Ok(())
959    }
960
961    /// Remove the exact byte-range and style.
962    #[inline]
963    pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
964        self.value.remove_style(range, style);
965    }
966
967    /// Remove the exact `Range<upos_type>` and style.
968    #[inline]
969    pub fn remove_range_style(
970        &mut self,
971        range: Range<upos_type>,
972        style: usize,
973    ) -> Result<(), TextError> {
974        let r = self
975            .value
976            .bytes_at_range(TextRange::from((range.start, 0)..(range.end, 0)))?;
977        self.value.remove_style(r, style);
978        Ok(())
979    }
980
981    /// Find all styles that touch the given range.
982    pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
983        self.value.styles_in(range, buf)
984    }
985
986    /// All styles active at the given position.
987    #[inline]
988    pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
989        self.value.styles_at(byte_pos, buf)
990    }
991
992    /// Check if the given style applies at the position and
993    /// return the complete range for the style.
994    #[inline]
995    pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
996        self.value.styles_at_match(byte_pos, style)
997    }
998
999    /// List of all styles.
1000    #[inline]
1001    pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
1002        self.value.styles()
1003    }
1004}
1005
1006impl MaskedInputState {
1007    /// Offset shown.
1008    #[inline]
1009    pub fn offset(&self) -> upos_type {
1010        self.offset
1011    }
1012
1013    /// Offset shown. This is corrected if the cursor wouldn't be visible.
1014    #[inline]
1015    pub fn set_offset(&mut self, offset: upos_type) {
1016        self.scroll_to_cursor = false;
1017        self.offset = offset;
1018    }
1019
1020    /// Cursor position.
1021    #[inline]
1022    pub fn cursor(&self) -> upos_type {
1023        self.value.cursor().x
1024    }
1025
1026    /// Set the cursor position.
1027    /// Scrolls the cursor to a visible position.
1028    #[inline]
1029    pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
1030        self.scroll_cursor_to_visible();
1031        self.value
1032            .set_cursor(TextPosition::new(cursor, 0), extend_selection)
1033    }
1034
1035    // find default cursor position for a number range
1036    fn number_cursor(&self, range: Range<upos_type>) -> upos_type {
1037        for (i, t) in self.mask[range.start as usize..range.end as usize]
1038            .iter()
1039            .enumerate()
1040            .rev()
1041        {
1042            match t.right {
1043                Mask::Digit(EditDirection::Rtol)
1044                | Mask::Digit0(EditDirection::Rtol)
1045                | Mask::Numeric(EditDirection::Rtol) => {
1046                    return range.start + i as upos_type + 1;
1047                }
1048                _ => {}
1049            }
1050        }
1051        range.start
1052    }
1053
1054    /// Get the default cursor for the section at the given cursor position,
1055    /// if it is an editable section.
1056    pub fn section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
1057        if cursor as usize >= self.mask.len() {
1058            return None;
1059        }
1060
1061        let mask = &self.mask[cursor as usize];
1062
1063        if mask.right.is_number() {
1064            Some(self.number_cursor(mask.sec_start..mask.sec_end))
1065        } else if mask.right.is_separator() {
1066            None
1067        } else if mask.right.is_none() {
1068            None
1069        } else {
1070            Some(mask.sec_start)
1071        }
1072    }
1073
1074    /// Get the default cursor position for the next editable section.
1075    pub fn next_section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
1076        if cursor as usize >= self.mask.len() {
1077            return None;
1078        }
1079
1080        let mut mask = &self.mask[cursor as usize];
1081        let mut next;
1082        loop {
1083            if mask.right.is_none() {
1084                return None;
1085            }
1086
1087            next = mask.sec_end;
1088            mask = &self.mask[next as usize];
1089
1090            if mask.right.is_number() {
1091                return Some(self.number_cursor(mask.sec_start..mask.sec_end));
1092            } else if mask.right.is_separator() {
1093                continue;
1094            } else if mask.right.is_none() {
1095                return None;
1096            } else {
1097                return Some(mask.sec_start);
1098            }
1099        }
1100    }
1101
1102    /// Get the default cursor position for the next editable section.
1103    pub fn prev_section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
1104        if cursor as usize >= self.mask.len() {
1105            return None;
1106        }
1107
1108        let mut prev = self.mask[cursor as usize].sec_start;
1109        let mut mask = &self.mask[prev as usize];
1110
1111        loop {
1112            if mask.peek_left.is_none() {
1113                return None;
1114            }
1115
1116            prev = self.mask[mask.sec_start as usize - 1].sec_start;
1117            mask = &self.mask[prev as usize];
1118
1119            if mask.right.is_number() {
1120                return Some(self.number_cursor(mask.sec_start..mask.sec_end));
1121            } else if mask.right.is_separator() {
1122                continue;
1123            } else {
1124                return Some(mask.sec_start);
1125            }
1126        }
1127    }
1128
1129    /// Is the position at a word boundary?
1130    pub fn is_section_boundary(&self, pos: upos_type) -> bool {
1131        if pos == 0 {
1132            return false;
1133        }
1134        if pos as usize >= self.mask.len() {
1135            return false;
1136        }
1137        let prev = &self.mask[pos as usize - 1];
1138        let mask = &self.mask[pos as usize];
1139        prev.sec_id != mask.sec_id
1140    }
1141
1142    /// Get the range for the section at the given cursor position,
1143    /// if it is an editable section.
1144    pub fn section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
1145        if cursor as usize >= self.mask.len() {
1146            return None;
1147        }
1148
1149        let mask = &self.mask[cursor as usize];
1150        if mask.right.is_number() {
1151            Some(mask.sec_start..mask.sec_end)
1152        } else if mask.right.is_separator() {
1153            None
1154        } else if mask.right.is_none() {
1155            None
1156        } else {
1157            Some(mask.sec_start..mask.sec_end)
1158        }
1159    }
1160
1161    /// Get the default cursor position for the next editable section.
1162    pub fn next_section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
1163        if cursor as usize >= self.mask.len() {
1164            return None;
1165        }
1166
1167        let mut mask = &self.mask[cursor as usize];
1168        let mut next;
1169        loop {
1170            if mask.right.is_none() {
1171                return None;
1172            }
1173
1174            next = mask.sec_end;
1175            mask = &self.mask[next as usize];
1176
1177            if mask.right.is_number() {
1178                return Some(mask.sec_start..mask.sec_end);
1179            } else if mask.right.is_separator() {
1180                continue;
1181            } else if mask.right.is_none() {
1182                return None;
1183            } else {
1184                return Some(mask.sec_start..mask.sec_end);
1185            }
1186        }
1187    }
1188
1189    /// Get the default cursor position for the next editable section.
1190    pub fn prev_section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
1191        if cursor as usize >= self.mask.len() {
1192            return None;
1193        }
1194
1195        let mut prev = self.mask[cursor as usize].sec_start;
1196        let mut mask = &self.mask[prev as usize];
1197        loop {
1198            if mask.peek_left.is_none() {
1199                return None;
1200            }
1201
1202            prev = self.mask[mask.sec_start as usize - 1].sec_start;
1203            mask = &self.mask[prev as usize];
1204
1205            if mask.right.is_number() {
1206                return Some(mask.sec_start..mask.sec_end);
1207            } else if mask.right.is_separator() {
1208                continue;
1209            } else {
1210                return Some(mask.sec_start..mask.sec_end);
1211            }
1212        }
1213    }
1214
1215    /// Place cursor at the decimal separator, if any.
1216    /// 0 otherwise.
1217    /// Scrolls the cursor to a visible position.  
1218    #[inline]
1219    pub fn set_default_cursor(&mut self) {
1220        self.scroll_cursor_to_visible();
1221        if let Some(pos) = self.section_cursor(0) {
1222            self.value.set_cursor(TextPosition::new(pos, 0), false);
1223        } else if let Some(pos) = self.next_section_cursor(0) {
1224            self.value.set_cursor(TextPosition::new(pos, 0), false);
1225        } else {
1226            self.value.set_cursor(TextPosition::new(0, 0), false);
1227        }
1228    }
1229
1230    /// Selection anchor.
1231    #[inline]
1232    pub fn anchor(&self) -> upos_type {
1233        self.value.anchor().x
1234    }
1235
1236    /// Selection.
1237    #[inline]
1238    pub fn has_selection(&self) -> bool {
1239        self.value.has_selection()
1240    }
1241
1242    /// Selection.
1243    #[inline]
1244    pub fn selection(&self) -> Range<upos_type> {
1245        let mut v = self.value.selection();
1246        if v.start == TextPosition::new(0, 1) {
1247            v.start = TextPosition::new(self.line_width(), 0);
1248        }
1249        if v.end == TextPosition::new(0, 1) {
1250            v.end = TextPosition::new(self.line_width(), 0);
1251        }
1252        v.start.x..v.end.x
1253    }
1254
1255    /// Selection.
1256    /// Scrolls the cursor to a visible position.
1257    #[inline]
1258    pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
1259        self.scroll_cursor_to_visible();
1260        self.value
1261            .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
1262    }
1263
1264    /// Selection.
1265    /// Scrolls the cursor to a visible position.
1266    #[inline]
1267    pub fn select_all(&mut self) -> bool {
1268        self.scroll_cursor_to_visible();
1269        if let Some(section) = self.section_range(self.cursor()) {
1270            if self.selection() == section {
1271                self.value.select_all()
1272            } else {
1273                self.value.set_selection(
1274                    TextPosition::new(section.start, 0),
1275                    TextPosition::new(section.end, 0),
1276                )
1277            }
1278        } else {
1279            self.value.select_all()
1280        }
1281    }
1282
1283    /// Selection.
1284    #[inline]
1285    pub fn selected_text(&self) -> &str {
1286        match self
1287            .value
1288            .str_slice(self.value.selection())
1289            .expect("valid_range")
1290        {
1291            Cow::Borrowed(v) => v,
1292            Cow::Owned(_) => {
1293                unreachable!()
1294            }
1295        }
1296    }
1297}
1298
1299impl MaskedInputState {
1300    /// Empty
1301    #[inline]
1302    pub fn is_empty(&self) -> bool {
1303        self.value.text().as_str() == self.default_value()
1304    }
1305
1306    /// Value with all punctuation and default values according to the mask type.
1307    #[inline]
1308    pub fn text(&self) -> &str {
1309        self.value.text().as_str()
1310    }
1311
1312    /// Text slice as `Cow<str>`. Uses a byte range.
1313    #[inline]
1314    pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1315        self.value.str_slice_byte(range).expect("valid_range")
1316    }
1317
1318    /// Text slice as `Cow<str>`. Uses a byte range.
1319    #[inline]
1320    pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1321        self.value.str_slice_byte(range)
1322    }
1323
1324    /// Text slice as `Cow<str>`
1325    #[inline]
1326    pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
1327        self.value
1328            .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
1329            .expect("valid_range")
1330    }
1331
1332    /// Text slice as `Cow<str>`
1333    #[inline]
1334    pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
1335        self.value
1336            .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
1337    }
1338
1339    /// Length as grapheme count.
1340    #[inline]
1341    pub fn len(&self) -> upos_type {
1342        self.value.line_width(0).expect("valid_row")
1343    }
1344
1345    /// Length in bytes.
1346    #[inline]
1347    pub fn len_bytes(&self) -> usize {
1348        self.value.len_bytes()
1349    }
1350
1351    /// Length as grapheme count.
1352    #[inline]
1353    pub fn line_width(&self) -> upos_type {
1354        self.value.line_width(0).expect("valid_row")
1355    }
1356
1357    /// Get the grapheme at the given position.
1358    #[inline]
1359    pub fn grapheme_at(&self, pos: upos_type) -> Result<Option<Grapheme<'_>>, TextError> {
1360        self.value.grapheme_at(TextPosition::new(pos, 0))
1361    }
1362
1363    /// Get a cursor over all the text with the current position set at pos.
1364    #[inline]
1365    pub fn text_graphemes(&self, pos: upos_type) -> <TextString as TextStore>::GraphemeIter<'_> {
1366        self.try_text_graphemes(pos).expect("valid_pos")
1367    }
1368
1369    /// Get a cursor over all the text with the current position set at pos.
1370    #[inline]
1371    pub fn try_text_graphemes(
1372        &self,
1373        pos: upos_type,
1374    ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
1375        self.value.text_graphemes(TextPosition::new(pos, 0))
1376    }
1377
1378    /// Get a cursor over the text-range the current position set at pos.
1379    #[inline]
1380    pub fn graphemes(
1381        &self,
1382        range: Range<upos_type>,
1383        pos: upos_type,
1384    ) -> <TextString as TextStore>::GraphemeIter<'_> {
1385        self.try_graphemes(range, pos).expect("valid_args")
1386    }
1387
1388    /// Get a cursor over the text-range the current position set at pos.
1389    #[inline]
1390    pub fn try_graphemes(
1391        &self,
1392        range: Range<upos_type>,
1393        pos: upos_type,
1394    ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
1395        self.value.graphemes(
1396            TextRange::new((range.start, 0), (range.end, 0)),
1397            TextPosition::new(pos, 0),
1398        )
1399    }
1400
1401    /// Grapheme position to byte position.
1402    /// This is the (start,end) position of the single grapheme after pos.
1403    #[inline]
1404    pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
1405        self.try_byte_at(pos).expect("valid_pos")
1406    }
1407
1408    /// Grapheme position to byte position.
1409    /// This is the (start,end) position of the single grapheme after pos.
1410    #[inline]
1411    pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
1412        self.value.byte_at(TextPosition::new(pos, 0))
1413    }
1414
1415    /// Grapheme range to byte range.
1416    #[inline]
1417    pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
1418        self.try_bytes_at_range(range).expect("valid_range")
1419    }
1420
1421    /// Grapheme range to byte range.
1422    #[inline]
1423    pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
1424        self.value
1425            .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
1426    }
1427
1428    /// Byte position to grapheme position.
1429    /// Returns the position that contains the given byte index.
1430    #[inline]
1431    pub fn byte_pos(&self, byte: usize) -> upos_type {
1432        self.try_byte_pos(byte).expect("valid_pos")
1433    }
1434
1435    /// Byte position to grapheme position.
1436    /// Returns the position that contains the given byte index.
1437    #[inline]
1438    pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
1439        Ok(self.value.byte_pos(byte)?.x)
1440    }
1441
1442    /// Byte range to grapheme range.
1443    #[inline]
1444    pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
1445        self.try_byte_range(bytes).expect("valid_range")
1446    }
1447
1448    /// Byte range to grapheme range.
1449    #[inline]
1450    pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
1451        let r = self.value.byte_range(bytes)?;
1452        Ok(r.start.x..r.end.x)
1453    }
1454}
1455
1456impl MaskedInputState {
1457    /// Create a default value according to the mask.
1458    #[inline]
1459    fn default_value(&self) -> String {
1460        MaskToken::empty_section(&self.mask)
1461    }
1462
1463    /// Reset to empty.
1464    #[inline]
1465    pub fn clear(&mut self) -> bool {
1466        if self.is_empty() {
1467            false
1468        } else {
1469            self.offset = 0;
1470            self.value
1471                .set_text(TextString::new_string(self.default_value()));
1472            self.set_default_cursor();
1473            true
1474        }
1475    }
1476
1477    /// Set the value.
1478    ///
1479    /// No checks if the value conforms to the mask.
1480    /// If the value is too short it will be filled with space.
1481    /// if the value is too long it will be truncated.
1482    #[inline]
1483    pub fn set_text<S: Into<String>>(&mut self, s: S) {
1484        self.offset = 0;
1485        let mut text = s.into();
1486        while text.graphemes(true).count() > self.mask.len().saturating_sub(1) {
1487            text.pop();
1488        }
1489        while text.graphemes(true).count() < self.mask.len().saturating_sub(1) {
1490            text.push(' ');
1491        }
1492        let len = text.graphemes(true).count();
1493
1494        assert_eq!(len, self.mask.len().saturating_sub(1));
1495
1496        self.value.set_text(TextString::new_string(text));
1497        self.set_default_cursor();
1498    }
1499
1500    /// Insert a char at the current position.
1501    #[inline]
1502    pub fn insert_char(&mut self, c: char) -> bool {
1503        self.begin_undo_seq();
1504        if self.has_selection() {
1505            let sel = self.selection();
1506            mask_op::remove_range(self, sel.clone()).expect("valid_selection");
1507            self.set_cursor(sel.start, false);
1508        }
1509        let c0 = mask_op::advance_cursor(self, c);
1510        let c1 = mask_op::insert_char(self, c);
1511        self.end_undo_seq();
1512
1513        self.scroll_cursor_to_visible();
1514        c0 || c1
1515    }
1516
1517    /// Remove the selected range. The text will be replaced with the default value
1518    /// as defined by the mask.
1519    #[inline]
1520    pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1521        self.try_delete_range(range).expect("valid_range")
1522    }
1523
1524    /// Remove the selected range. The text will be replaced with the default value
1525    /// as defined by the mask.
1526    #[inline]
1527    pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1528        self.value.begin_undo_seq();
1529        let r = mask_op::remove_range(self, range.clone())?;
1530        if let Some(pos) = self.section_cursor(range.start) {
1531            self.set_cursor(pos, false);
1532        }
1533        self.value.end_undo_seq();
1534
1535        self.scroll_cursor_to_visible();
1536        Ok(r)
1537    }
1538}
1539
1540impl MaskedInputState {
1541    /// Delete the char after the cursor.
1542    #[inline]
1543    pub fn delete_next_char(&mut self) -> bool {
1544        if self.has_selection() {
1545            self.delete_range(self.selection())
1546        } else if self.cursor() == self.len() {
1547            false
1548        } else {
1549            mask_op::remove_next(self);
1550            self.scroll_cursor_to_visible();
1551            true
1552        }
1553    }
1554
1555    /// Delete the char before the cursor.
1556    #[inline]
1557    pub fn delete_prev_char(&mut self) -> bool {
1558        if self.has_selection() {
1559            self.delete_range(self.selection())
1560        } else if self.cursor() == 0 {
1561            false
1562        } else {
1563            mask_op::remove_prev(self);
1564            self.scroll_cursor_to_visible();
1565            true
1566        }
1567    }
1568
1569    /// Delete the previous section.
1570    #[inline]
1571    pub fn delete_prev_section(&mut self) -> bool {
1572        if self.has_selection() {
1573            self.delete_range(self.selection())
1574        } else {
1575            if let Some(range) = self.prev_section_range(self.cursor()) {
1576                self.delete_range(range)
1577            } else {
1578                false
1579            }
1580        }
1581    }
1582
1583    /// Delete the next section.
1584    #[inline]
1585    pub fn delete_next_section(&mut self) -> bool {
1586        if self.has_selection() {
1587            self.delete_range(self.selection())
1588        } else {
1589            if let Some(range) = self.next_section_range(self.cursor()) {
1590                self.delete_range(range)
1591            } else {
1592                false
1593            }
1594        }
1595    }
1596
1597    /// Move to the next char.
1598    #[inline]
1599    pub fn move_right(&mut self, extend_selection: bool) -> bool {
1600        let c = min(self.cursor() + 1, self.len());
1601        self.set_cursor(c, extend_selection)
1602    }
1603
1604    /// Move to the previous char.
1605    #[inline]
1606    pub fn move_left(&mut self, extend_selection: bool) -> bool {
1607        let c = self.cursor().saturating_sub(1);
1608        self.set_cursor(c, extend_selection)
1609    }
1610
1611    /// Start of line
1612    #[inline]
1613    pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1614        if let Some(c) = self.section_cursor(self.cursor()) {
1615            if c != self.cursor() {
1616                self.set_cursor(c, extend_selection)
1617            } else {
1618                self.set_cursor(0, extend_selection)
1619            }
1620        } else {
1621            self.set_cursor(0, extend_selection)
1622        }
1623    }
1624
1625    /// End of line
1626    #[inline]
1627    pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1628        self.set_cursor(self.len(), extend_selection)
1629    }
1630
1631    /// Move to start of previous section.
1632    #[inline]
1633    pub fn move_to_prev_section(&mut self, extend_selection: bool) -> bool {
1634        if let Some(curr) = self.section_range(self.cursor()) {
1635            if self.cursor() != curr.start {
1636                return self.set_cursor(curr.start, extend_selection);
1637            }
1638        }
1639        if let Some(range) = self.prev_section_range(self.cursor()) {
1640            self.set_cursor(range.start, extend_selection)
1641        } else {
1642            false
1643        }
1644    }
1645
1646    /// Move to end of previous section.
1647    #[inline]
1648    pub fn move_to_next_section(&mut self, extend_selection: bool) -> bool {
1649        if let Some(curr) = self.section_range(self.cursor()) {
1650            if self.cursor() != curr.end {
1651                return self.set_cursor(curr.end, extend_selection);
1652            }
1653        }
1654        if let Some(range) = self.next_section_range(self.cursor()) {
1655            self.set_cursor(range.end, extend_selection)
1656        } else {
1657            false
1658        }
1659    }
1660
1661    /// Select next section.
1662    #[inline]
1663    pub fn select_current_section(&mut self) -> bool {
1664        let selection = self.selection();
1665
1666        if let Some(next) = self.section_range(selection.start.saturating_sub(1)) {
1667            if !next.is_empty() {
1668                self.set_selection(next.start, next.end)
1669            } else {
1670                false
1671            }
1672        } else {
1673            false
1674        }
1675    }
1676
1677    /// Select next section.
1678    #[inline]
1679    pub fn select_next_section(&mut self) -> bool {
1680        let selection = self.selection();
1681
1682        if let Some(next) = self.next_section_range(selection.start) {
1683            if !next.is_empty() {
1684                self.set_selection(next.start, next.end)
1685            } else {
1686                false
1687            }
1688        } else {
1689            false
1690        }
1691    }
1692
1693    /// Select previous section.
1694    #[inline]
1695    pub fn select_prev_section(&mut self) -> bool {
1696        let selection = self.selection();
1697
1698        if let Some(next) = self.prev_section_range(selection.start.saturating_sub(1)) {
1699            if !next.is_empty() {
1700                self.set_selection(next.start, next.end)
1701            } else {
1702                false
1703            }
1704        } else {
1705            false
1706        }
1707    }
1708}
1709
1710impl HasScreenCursor for MaskedInputState {
1711    /// The current text cursor as an absolute screen position.
1712    #[inline]
1713    fn screen_cursor(&self) -> Option<(u16, u16)> {
1714        if self.is_focused() {
1715            if self.has_selection() {
1716                None
1717            } else {
1718                let cx = self.cursor();
1719                let ox = self.offset();
1720
1721                if cx < ox {
1722                    None
1723                } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1724                    None
1725                } else {
1726                    self.col_to_screen(cx)
1727                        .map(|sc| (self.inner.x + sc, self.inner.y))
1728                }
1729            }
1730        } else {
1731            None
1732        }
1733    }
1734}
1735
1736impl RelocatableState for MaskedInputState {
1737    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1738        // clip offset for some corrections.
1739        self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1740        self.area = relocate_area(self.area, shift, clip);
1741        self.inner = relocate_area(self.inner, shift, clip);
1742    }
1743}
1744
1745impl MaskedInputState {
1746    fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1747        let left_margin = self.offset();
1748        let right_margin = self.offset() + self.rendered.width as upos_type;
1749        let compact = self.compact && !self.is_focused();
1750
1751        let grapheme_iter = self
1752            .value
1753            .graphemes(TextRange::new((0, 0), (0, 1)), TextPosition::new(0, 0))
1754            .expect("valid-rows");
1755        let mask_iter = self.mask.iter();
1756
1757        let iter = MaskedGraphemes {
1758            iter_str: grapheme_iter,
1759            iter_mask: mask_iter,
1760            compact,
1761            sym_neg: self.neg_sym().to_string(),
1762            sym_dec: self.dec_sep().to_string(),
1763            sym_grp: self.grp_sep().to_string(),
1764            sym_pos: self.pos_sym().to_string(),
1765            byte_pos: 0,
1766        };
1767
1768        let mut it = GlyphIter2::new(TextPosition::new(0, 0), 0, iter, Default::default());
1769        it.set_tabs(0 /* no tabs */);
1770        it.set_show_ctrl(self.value.glyph_ctrl());
1771        it.set_lf_breaks(false);
1772        it.set_text_wrap(TextWrap2::Shift);
1773        it.set_left_margin(left_margin);
1774        it.set_right_margin(right_margin);
1775        it.set_word_margin(right_margin);
1776        it.prepare().expect("valid-rows");
1777
1778        Box::new(it)
1779    }
1780
1781    /// Converts from a widget relative screen coordinate to a grapheme index.
1782    /// x is the relative screen position.
1783    pub fn screen_to_col(&self, scx: i16) -> upos_type {
1784        let ox = self.offset();
1785
1786        let scx = scx + self.dark_offset.0 as i16;
1787
1788        if scx < 0 {
1789            ox.saturating_sub((scx as ipos_type).unsigned_abs())
1790        } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
1791            min(ox + scx as upos_type, self.len())
1792        } else {
1793            let scx = scx as u16;
1794
1795            let line = self.glyphs2();
1796
1797            let mut col = ox;
1798            for g in line {
1799                if g.contains_screen_x(scx) {
1800                    break;
1801                }
1802                col = g.pos().x + 1;
1803            }
1804            col
1805        }
1806    }
1807
1808    /// Converts a grapheme based position to a screen position
1809    /// relative to the widget area.
1810    pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1811        let ox = self.offset();
1812
1813        if pos < ox {
1814            return None;
1815        }
1816
1817        let line = self.glyphs2();
1818        let mut screen_x = 0;
1819        for g in line {
1820            if g.pos().x == pos {
1821                break;
1822            }
1823            screen_x = g.screen_pos().0 + g.screen_width();
1824        }
1825
1826        if screen_x >= self.dark_offset.0 {
1827            Some(screen_x - self.dark_offset.0)
1828        } else {
1829            None
1830        }
1831    }
1832
1833    /// Set the cursor position from a screen position relative to the origin
1834    /// of the widget. This value can be negative, which selects a currently
1835    /// not visible position and scrolls to it.
1836    #[inline]
1837    pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1838        let scx = cursor;
1839
1840        let cx = self.screen_to_col(scx);
1841
1842        self.set_cursor(cx, extend_selection)
1843    }
1844
1845    /// Set the cursor position from screen coordinates,
1846    /// rounds the position to the next section bounds.
1847    ///
1848    /// The cursor positions are relative to the inner rect.
1849    /// They may be negative too, this allows setting the cursor
1850    /// to a position that is currently scrolled away.
1851    pub fn set_screen_cursor_sections(
1852        &mut self,
1853        screen_cursor: i16,
1854        extend_selection: bool,
1855    ) -> bool {
1856        let anchor = self.anchor();
1857        let cursor = self.screen_to_col(screen_cursor);
1858
1859        let Some(range) = self.section_range(cursor) else {
1860            return false;
1861        };
1862
1863        let cursor = if cursor < anchor {
1864            range.start
1865        } else {
1866            range.end
1867        };
1868
1869        // extend anchor
1870        if !self.is_section_boundary(anchor) {
1871            if let Some(range) = self.section_range(anchor) {
1872                if cursor < anchor {
1873                    self.set_cursor(range.end, false);
1874                } else {
1875                    self.set_cursor(range.start, false);
1876                }
1877            };
1878        }
1879
1880        self.set_cursor(cursor, extend_selection)
1881    }
1882
1883    /// Scrolling
1884    pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1885        self.set_offset(self.offset.saturating_sub(delta));
1886        true
1887    }
1888
1889    /// Scrolling
1890    pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1891        self.set_offset(self.offset + delta);
1892        true
1893    }
1894
1895    /// Change the offset in a way that the cursor is visible.
1896    pub fn scroll_cursor_to_visible(&mut self) {
1897        self.scroll_to_cursor = true;
1898    }
1899}
1900
1901impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for MaskedInputState {
1902    fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1903        // small helper ...
1904        fn tc(r: bool) -> TextOutcome {
1905            if r {
1906                TextOutcome::TextChanged
1907            } else {
1908                TextOutcome::Unchanged
1909            }
1910        }
1911        fn overwrite(state: &mut MaskedInputState) {
1912            if state.overwrite {
1913                state.overwrite = false;
1914                state.clear();
1915            }
1916        }
1917        fn clear_overwrite(state: &mut MaskedInputState) {
1918            state.overwrite = false;
1919        }
1920
1921        // focus behaviour
1922        if self.lost_focus() {
1923            match self.on_focus_lost {
1924                TextFocusLost::None => {}
1925                TextFocusLost::Position0 => {
1926                    self.set_default_cursor();
1927                    self.scroll_cursor_to_visible();
1928                    // repaint is triggered by focus-change
1929                }
1930            }
1931        }
1932        if self.gained_focus() {
1933            match self.on_focus_gained {
1934                TextFocusGained::None => {}
1935                TextFocusGained::Overwrite => {
1936                    self.overwrite = true;
1937                }
1938                TextFocusGained::SelectAll => {
1939                    self.select_all();
1940                    // repaint is triggered by focus-change
1941                }
1942            }
1943        }
1944
1945        let mut r = if self.is_focused() {
1946            match event {
1947                ct_event!(key press c)
1948                | ct_event!(key press SHIFT-c)
1949                | ct_event!(key press CONTROL_ALT-c) => {
1950                    overwrite(self);
1951                    tc(self.insert_char(*c))
1952                }
1953                ct_event!(keycode press Backspace) => {
1954                    clear_overwrite(self);
1955                    tc(self.delete_prev_char())
1956                }
1957                ct_event!(keycode press Delete) => {
1958                    clear_overwrite(self);
1959                    tc(self.delete_next_char())
1960                }
1961                ct_event!(keycode press CONTROL-Backspace)
1962                | ct_event!(keycode press ALT-Backspace) => {
1963                    clear_overwrite(self);
1964                    tc(self.delete_prev_section())
1965                }
1966                ct_event!(keycode press CONTROL-Delete) => {
1967                    clear_overwrite(self);
1968                    tc(self.delete_next_section())
1969                }
1970                ct_event!(key press CONTROL-'x') => {
1971                    clear_overwrite(self);
1972                    tc(self.cut_to_clip())
1973                }
1974                ct_event!(key press CONTROL-'v') => {
1975                    clear_overwrite(self);
1976                    tc(self.paste_from_clip())
1977                }
1978                ct_event!(key press CONTROL-'d') => {
1979                    clear_overwrite(self);
1980                    tc(self.clear())
1981                }
1982                ct_event!(key press CONTROL-'z') => {
1983                    clear_overwrite(self);
1984                    tc(self.undo())
1985                }
1986                ct_event!(key press CONTROL_SHIFT-'Z') => {
1987                    clear_overwrite(self);
1988                    tc(self.redo())
1989                }
1990
1991                ct_event!(key release _)
1992                | ct_event!(key release SHIFT-_)
1993                | ct_event!(key release CONTROL_ALT-_)
1994                | ct_event!(keycode release Backspace)
1995                | ct_event!(keycode release Delete)
1996                | ct_event!(keycode release CONTROL-Backspace)
1997                | ct_event!(keycode release ALT-Backspace)
1998                | ct_event!(keycode release CONTROL-Delete)
1999                | ct_event!(key release CONTROL-'x')
2000                | ct_event!(key release CONTROL-'v')
2001                | ct_event!(key release CONTROL-'d')
2002                | ct_event!(key release CONTROL-'z')
2003                | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2004
2005                _ => TextOutcome::Continue,
2006            }
2007        } else {
2008            TextOutcome::Continue
2009        };
2010
2011        if r == TextOutcome::Continue {
2012            r = self.handle(event, ReadOnly);
2013        }
2014        r
2015    }
2016}
2017
2018impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for MaskedInputState {
2019    fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2020        fn clear_overwrite(state: &mut MaskedInputState) {
2021            state.overwrite = false;
2022        }
2023
2024        let mut r = if self.is_focused() {
2025            match event {
2026                ct_event!(keycode press Left) => {
2027                    clear_overwrite(self);
2028                    self.move_left(false).into()
2029                }
2030                ct_event!(keycode press Right) => {
2031                    clear_overwrite(self);
2032                    self.move_right(false).into()
2033                }
2034                ct_event!(keycode press CONTROL-Left) => {
2035                    clear_overwrite(self);
2036                    self.move_to_prev_section(false).into()
2037                }
2038                ct_event!(keycode press CONTROL-Right) => {
2039                    clear_overwrite(self);
2040                    self.move_to_next_section(false).into()
2041                }
2042                ct_event!(keycode press Home) => {
2043                    clear_overwrite(self);
2044                    self.move_to_line_start(false).into()
2045                }
2046                ct_event!(keycode press End) => {
2047                    clear_overwrite(self);
2048                    self.move_to_line_end(false).into()
2049                }
2050                ct_event!(keycode press SHIFT-Left) => {
2051                    clear_overwrite(self);
2052                    self.move_left(true).into()
2053                }
2054                ct_event!(keycode press SHIFT-Right) => {
2055                    clear_overwrite(self);
2056                    self.move_right(true).into()
2057                }
2058                ct_event!(keycode press CONTROL_SHIFT-Left) => {
2059                    clear_overwrite(self);
2060                    self.move_to_prev_section(true).into()
2061                }
2062                ct_event!(keycode press CONTROL_SHIFT-Right) => {
2063                    clear_overwrite(self);
2064                    self.move_to_next_section(true).into()
2065                }
2066                ct_event!(keycode press SHIFT-Home) => {
2067                    clear_overwrite(self);
2068                    self.move_to_line_start(true).into()
2069                }
2070                ct_event!(keycode press SHIFT-End) => {
2071                    clear_overwrite(self);
2072                    self.move_to_line_end(true).into()
2073                }
2074                ct_event!(keycode press Tab) => {
2075                    // ignore tab from focus
2076                    if !self.focus.gained() {
2077                        clear_overwrite(self);
2078                        self.select_next_section().into()
2079                    } else {
2080                        TextOutcome::Unchanged
2081                    }
2082                }
2083                ct_event!(keycode press SHIFT-BackTab) => {
2084                    // ignore tab from focus
2085                    if !self.focus.gained() {
2086                        clear_overwrite(self);
2087                        self.select_prev_section().into()
2088                    } else {
2089                        TextOutcome::Unchanged
2090                    }
2091                }
2092                ct_event!(key press CONTROL-'a') => {
2093                    clear_overwrite(self);
2094                    self.select_all().into()
2095                }
2096                ct_event!(key press CONTROL-'c') => {
2097                    clear_overwrite(self);
2098                    self.copy_to_clip().into()
2099                }
2100
2101                ct_event!(keycode release Left)
2102                | ct_event!(keycode release Right)
2103                | ct_event!(keycode release CONTROL-Left)
2104                | ct_event!(keycode release CONTROL-Right)
2105                | ct_event!(keycode release Home)
2106                | ct_event!(keycode release End)
2107                | ct_event!(keycode release SHIFT-Left)
2108                | ct_event!(keycode release SHIFT-Right)
2109                | ct_event!(keycode release CONTROL_SHIFT-Left)
2110                | ct_event!(keycode release CONTROL_SHIFT-Right)
2111                | ct_event!(keycode release SHIFT-Home)
2112                | ct_event!(keycode release SHIFT-End)
2113                | ct_event!(key release CONTROL-'a')
2114                | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2115
2116                _ => TextOutcome::Continue,
2117            }
2118        } else {
2119            TextOutcome::Continue
2120        };
2121
2122        if r == TextOutcome::Continue {
2123            r = self.handle(event, MouseOnly);
2124        }
2125        r
2126    }
2127}
2128
2129impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for MaskedInputState {
2130    fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
2131        fn clear_overwrite(state: &mut MaskedInputState) {
2132            state.overwrite = false;
2133        }
2134
2135        match event {
2136            ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2137                let c = (m.column as i16) - (self.inner.x as i16);
2138                clear_overwrite(self);
2139                self.set_screen_cursor(c, true).into()
2140            }
2141            ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2142                let cx = m.column as i16 - self.inner.x as i16;
2143                clear_overwrite(self);
2144                self.set_screen_cursor_sections(cx, true).into()
2145            }
2146            ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2147                let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
2148                clear_overwrite(self);
2149                if let Some(range) = self.section_range(tx) {
2150                    self.set_selection(range.start, range.end).into()
2151                } else {
2152                    TextOutcome::Unchanged
2153                }
2154            }
2155            ct_event!(mouse down Left for column,row) => {
2156                if self.gained_focus() {
2157                    // don't react to the first click that's for
2158                    // focus. this one shouldn't demolish the selection.
2159                    TextOutcome::Unchanged
2160                } else if self.inner.contains((*column, *row).into()) {
2161                    let c = (column - self.inner.x) as i16;
2162                    clear_overwrite(self);
2163                    self.set_screen_cursor(c, false).into()
2164                } else {
2165                    TextOutcome::Continue
2166                }
2167            }
2168            ct_event!(mouse down CONTROL-Left for column,row) => {
2169                if self.inner.contains((*column, *row).into()) {
2170                    let cx = (column - self.inner.x) as i16;
2171                    clear_overwrite(self);
2172                    self.set_screen_cursor(cx, true).into()
2173                } else {
2174                    TextOutcome::Continue
2175                }
2176            }
2177            ct_event!(mouse down ALT-Left for column,row) => {
2178                if self.inner.contains((*column, *row).into()) {
2179                    let cx = (column - self.inner.x) as i16;
2180                    clear_overwrite(self);
2181                    self.set_screen_cursor_sections(cx, true).into()
2182                } else {
2183                    TextOutcome::Continue
2184                }
2185            }
2186            _ => TextOutcome::Continue,
2187        }
2188    }
2189}
2190
2191/// Handle all events.
2192/// Text events are only processed if focus is true.
2193/// Mouse events are processed if they are in range.
2194pub fn handle_events(
2195    state: &mut MaskedInputState,
2196    focus: bool,
2197    event: &crossterm::event::Event,
2198) -> TextOutcome {
2199    state.focus.set(focus);
2200    state.handle(event, Regular)
2201}
2202
2203/// Handle only navigation events.
2204/// Text events are only processed if focus is true.
2205/// Mouse events are processed if they are in range.
2206pub fn handle_readonly_events(
2207    state: &mut TextInputState,
2208    focus: bool,
2209    event: &crossterm::event::Event,
2210) -> TextOutcome {
2211    state.focus.set(focus);
2212    state.handle(event, ReadOnly)
2213}
2214
2215/// Handle only mouse-events.
2216pub fn handle_mouse_events(
2217    state: &mut MaskedInputState,
2218    event: &crossterm::event::Event,
2219) -> TextOutcome {
2220    state.handle(event, MouseOnly)
2221}