Skip to main content

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