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