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