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