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