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