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    ///
1735    /// Returns an error if the text contains line-breaks.
1736    #[inline]
1737    pub fn set_value<S: ToString>(&mut self, s: S) {
1738        self.set_text(s.to_string());
1739    }
1740
1741    /// Set the value.
1742    ///
1743    /// No checks if the value conforms to the mask.
1744    /// If the value is too short it will be filled with space.
1745    /// if the value is too long it will be truncated.
1746    #[inline]
1747    pub fn set_text<S: Into<String>>(&mut self, s: S) {
1748        self.offset = 0;
1749        let mut text = s.into();
1750        while text.graphemes(true).count() > self.mask.len().saturating_sub(1) {
1751            text.pop();
1752        }
1753        while text.graphemes(true).count() < self.mask.len().saturating_sub(1) {
1754            text.push(' ');
1755        }
1756        let len = text.graphemes(true).count();
1757
1758        assert_eq!(len, self.mask.len().saturating_sub(1));
1759
1760        self.value.set_text(TextString::new_string(text));
1761        self.set_default_cursor();
1762    }
1763
1764    /// Insert a char at the current position.
1765    #[inline]
1766    pub fn insert_char(&mut self, c: char) -> bool {
1767        self.begin_undo_seq();
1768        if self.has_selection() {
1769            let sel = self.selection();
1770            mask_op::remove_range(self, sel.clone()).expect("valid_selection");
1771            self.set_cursor(sel.start, false);
1772        }
1773        let c0 = mask_op::advance_cursor(self, c);
1774        let c1 = mask_op::insert_char(self, c);
1775        self.end_undo_seq();
1776
1777        self.scroll_cursor_to_visible();
1778        c0 || c1
1779    }
1780
1781    /// Remove the selected range. The text will be replaced with the default value
1782    /// as defined by the mask.
1783    #[inline]
1784    pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1785        self.try_delete_range(range).expect("valid_range")
1786    }
1787
1788    /// Remove the selected range. The text will be replaced with the default value
1789    /// as defined by the mask.
1790    #[inline]
1791    pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1792        self.value.begin_undo_seq();
1793        let r = mask_op::remove_range(self, range.clone())?;
1794        if let Some(pos) = self.section_cursor(range.start) {
1795            self.set_cursor(pos, false);
1796        }
1797        self.value.end_undo_seq();
1798
1799        self.scroll_cursor_to_visible();
1800        Ok(r)
1801    }
1802}
1803
1804impl MaskedInputState {
1805    /// Delete the char after the cursor.
1806    #[inline]
1807    pub fn delete_next_char(&mut self) -> bool {
1808        if self.has_selection() {
1809            self.delete_range(self.selection())
1810        } else if self.cursor() == self.len() {
1811            false
1812        } else {
1813            mask_op::remove_next(self);
1814            self.scroll_cursor_to_visible();
1815            true
1816        }
1817    }
1818
1819    /// Delete the char before the cursor.
1820    #[inline]
1821    pub fn delete_prev_char(&mut self) -> bool {
1822        if self.has_selection() {
1823            self.delete_range(self.selection())
1824        } else if self.cursor() == 0 {
1825            false
1826        } else {
1827            mask_op::remove_prev(self);
1828            self.scroll_cursor_to_visible();
1829            true
1830        }
1831    }
1832
1833    /// Delete the previous section.
1834    #[inline]
1835    pub fn delete_prev_section(&mut self) -> bool {
1836        if self.has_selection() {
1837            self.delete_range(self.selection())
1838        } else {
1839            if let Some(range) = self.prev_section_range(self.cursor()) {
1840                self.delete_range(range)
1841            } else {
1842                false
1843            }
1844        }
1845    }
1846
1847    /// Delete the next section.
1848    #[inline]
1849    pub fn delete_next_section(&mut self) -> bool {
1850        if self.has_selection() {
1851            self.delete_range(self.selection())
1852        } else {
1853            if let Some(range) = self.next_section_range(self.cursor()) {
1854                self.delete_range(range)
1855            } else {
1856                false
1857            }
1858        }
1859    }
1860
1861    /// Move to the next char.
1862    #[inline]
1863    pub fn move_right(&mut self, extend_selection: bool) -> bool {
1864        let c = min(self.cursor() + 1, self.len());
1865        self.set_cursor(c, extend_selection)
1866    }
1867
1868    /// Move to the previous char.
1869    #[inline]
1870    pub fn move_left(&mut self, extend_selection: bool) -> bool {
1871        let c = self.cursor().saturating_sub(1);
1872        self.set_cursor(c, extend_selection)
1873    }
1874
1875    /// Start of line
1876    #[inline]
1877    pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1878        if let Some(c) = self.section_cursor(self.cursor()) {
1879            if c != self.cursor() {
1880                self.set_cursor(c, extend_selection)
1881            } else {
1882                self.set_cursor(0, extend_selection)
1883            }
1884        } else {
1885            self.set_cursor(0, extend_selection)
1886        }
1887    }
1888
1889    /// End of line
1890    #[inline]
1891    pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1892        self.set_cursor(self.len(), extend_selection)
1893    }
1894
1895    /// Move to start of previous section.
1896    #[inline]
1897    pub fn move_to_prev_section(&mut self, extend_selection: bool) -> bool {
1898        if let Some(curr) = self.section_range(self.cursor()) {
1899            if self.cursor() != curr.start {
1900                return self.set_cursor(curr.start, extend_selection);
1901            }
1902        }
1903        if let Some(range) = self.prev_section_range(self.cursor()) {
1904            self.set_cursor(range.start, extend_selection)
1905        } else {
1906            false
1907        }
1908    }
1909
1910    /// Move to end of previous section.
1911    #[inline]
1912    pub fn move_to_next_section(&mut self, extend_selection: bool) -> bool {
1913        if let Some(curr) = self.section_range(self.cursor()) {
1914            if self.cursor() != curr.end {
1915                return self.set_cursor(curr.end, extend_selection);
1916            }
1917        }
1918        if let Some(range) = self.next_section_range(self.cursor()) {
1919            self.set_cursor(range.end, extend_selection)
1920        } else {
1921            false
1922        }
1923    }
1924
1925    /// Select next section.
1926    #[inline]
1927    pub fn select_current_section(&mut self) -> bool {
1928        let selection = self.selection();
1929
1930        if let Some(next) = self.section_range(selection.start.saturating_sub(1)) {
1931            if !next.is_empty() {
1932                self.set_selection(next.start, next.end)
1933            } else {
1934                false
1935            }
1936        } else {
1937            false
1938        }
1939    }
1940
1941    /// Select next section.
1942    #[inline]
1943    pub fn select_next_section(&mut self) -> bool {
1944        let selection = self.selection();
1945
1946        if let Some(next) = self.next_section_range(selection.start) {
1947            if !next.is_empty() {
1948                self.set_selection(next.start, next.end)
1949            } else {
1950                false
1951            }
1952        } else {
1953            false
1954        }
1955    }
1956
1957    /// Select previous section.
1958    #[inline]
1959    pub fn select_prev_section(&mut self) -> bool {
1960        let selection = self.selection();
1961
1962        if let Some(next) = self.prev_section_range(selection.start.saturating_sub(1)) {
1963            if !next.is_empty() {
1964                self.set_selection(next.start, next.end)
1965            } else {
1966                false
1967            }
1968        } else {
1969            false
1970        }
1971    }
1972}
1973
1974impl HasScreenCursor for MaskedInputState {
1975    /// The current text cursor as an absolute screen position.
1976    #[inline]
1977    fn screen_cursor(&self) -> Option<(u16, u16)> {
1978        if self.is_focused() {
1979            if self.has_selection() {
1980                None
1981            } else {
1982                let cx = self.cursor();
1983                let ox = self.offset();
1984
1985                if cx < ox {
1986                    None
1987                } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1988                    None
1989                } else {
1990                    self.col_to_screen(cx)
1991                        .map(|sc| (self.inner.x + sc, self.inner.y))
1992                }
1993            }
1994        } else {
1995            None
1996        }
1997    }
1998}
1999
2000impl RelocatableState for MaskedInputState {
2001    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
2002        self.area.relocate(shift, clip);
2003        self.inner.relocate(shift, clip);
2004        // clip offset for some corrections.
2005        self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
2006    }
2007}
2008
2009impl MaskedInputState {
2010    fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
2011        let left_margin = self.offset();
2012        let right_margin = self.offset() + self.rendered.width as upos_type;
2013        let compact = self.compact && !self.is_focused();
2014
2015        let grapheme_iter = self
2016            .value
2017            .graphemes(TextRange::new((0, 0), (0, 1)), TextPosition::new(0, 0))
2018            .expect("valid-rows");
2019        let mask_iter = self.mask.iter();
2020
2021        let iter = MaskedGraphemes {
2022            iter_str: grapheme_iter,
2023            iter_mask: mask_iter,
2024            compact,
2025            sym_neg: self.neg_sym().to_string(),
2026            sym_dec: self.dec_sep().to_string(),
2027            sym_grp: self.grp_sep().to_string(),
2028            sym_pos: self.pos_sym().to_string(),
2029            byte_pos: 0,
2030        };
2031
2032        let mut it = GlyphIter2::new(TextPosition::new(0, 0), 0, iter, Default::default());
2033        it.set_tabs(0 /* no tabs */);
2034        it.set_show_ctrl(self.value.glyph_ctrl());
2035        it.set_lf_breaks(false);
2036        it.set_text_wrap(TextWrap2::Shift);
2037        it.set_left_margin(left_margin);
2038        it.set_right_margin(right_margin);
2039        it.set_word_margin(right_margin);
2040        it.prepare().expect("valid-rows");
2041
2042        Box::new(it)
2043    }
2044
2045    /// Converts from a widget relative screen coordinate to a grapheme index.
2046    /// x is the relative screen position.
2047    pub fn screen_to_col(&self, scx: i16) -> upos_type {
2048        let ox = self.offset();
2049
2050        let scx = scx + self.dark_offset.0 as i16;
2051
2052        if scx < 0 {
2053            ox.saturating_sub((scx as ipos_type).unsigned_abs())
2054        } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
2055            min(ox + scx as upos_type, self.len())
2056        } else {
2057            let scx = scx as u16;
2058
2059            let line = self.glyphs2();
2060
2061            let mut col = ox;
2062            for g in line {
2063                if g.contains_screen_x(scx) {
2064                    break;
2065                }
2066                col = g.pos().x + 1;
2067            }
2068            col
2069        }
2070    }
2071
2072    /// Converts a grapheme based position to a screen position
2073    /// relative to the widget area.
2074    pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
2075        let ox = self.offset();
2076
2077        if pos < ox {
2078            return None;
2079        }
2080
2081        let line = self.glyphs2();
2082        let mut screen_x = 0;
2083        for g in line {
2084            if g.pos().x == pos {
2085                break;
2086            }
2087            screen_x = g.screen_pos().0 + g.screen_width();
2088        }
2089
2090        if screen_x >= self.dark_offset.0 {
2091            Some(screen_x - self.dark_offset.0)
2092        } else {
2093            None
2094        }
2095    }
2096
2097    /// Set the cursor position from a screen position relative to the origin
2098    /// of the widget. This value can be negative, which selects a currently
2099    /// not visible position and scrolls to it.
2100    #[inline]
2101    pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
2102        let scx = cursor;
2103
2104        let cx = self.screen_to_col(scx);
2105
2106        self.set_cursor(cx, extend_selection)
2107    }
2108
2109    /// Set the cursor position from screen coordinates,
2110    /// rounds the position to the next section bounds.
2111    ///
2112    /// The cursor positions are relative to the inner rect.
2113    /// They may be negative too, this allows setting the cursor
2114    /// to a position that is currently scrolled away.
2115    pub fn set_screen_cursor_sections(
2116        &mut self,
2117        screen_cursor: i16,
2118        extend_selection: bool,
2119    ) -> bool {
2120        let anchor = self.anchor();
2121        let cursor = self.screen_to_col(screen_cursor);
2122
2123        let Some(range) = self.section_range(cursor) else {
2124            return false;
2125        };
2126
2127        let cursor = if cursor < anchor {
2128            range.start
2129        } else {
2130            range.end
2131        };
2132
2133        // extend anchor
2134        if !self.is_section_boundary(anchor) {
2135            if let Some(range) = self.section_range(anchor) {
2136                if cursor < anchor {
2137                    self.set_cursor(range.end, false);
2138                } else {
2139                    self.set_cursor(range.start, false);
2140                }
2141            };
2142        }
2143
2144        self.set_cursor(cursor, extend_selection)
2145    }
2146
2147    /// Scrolling
2148    pub fn scroll_left(&mut self, delta: upos_type) -> bool {
2149        self.set_offset(self.offset.saturating_sub(delta));
2150        true
2151    }
2152
2153    /// Scrolling
2154    pub fn scroll_right(&mut self, delta: upos_type) -> bool {
2155        self.set_offset(self.offset + delta);
2156        true
2157    }
2158
2159    /// Change the offset in a way that the cursor is visible.
2160    pub fn scroll_cursor_to_visible(&mut self) {
2161        self.scroll_to_cursor.set(true);
2162    }
2163}
2164
2165impl HandleEvent<Event, Regular, TextOutcome> for MaskedInputState {
2166    fn handle(&mut self, event: &Event, _keymap: Regular) -> TextOutcome {
2167        // small helper ...
2168        fn tc(r: bool) -> TextOutcome {
2169            if r {
2170                TextOutcome::TextChanged
2171            } else {
2172                TextOutcome::Unchanged
2173            }
2174        }
2175        fn overwrite(state: &mut MaskedInputState) {
2176            if state.overwrite.get() {
2177                state.overwrite.set(false);
2178                state.clear();
2179            }
2180        }
2181        fn clear_overwrite(state: &mut MaskedInputState) {
2182            state.overwrite.set(false);
2183        }
2184
2185        let mut r = if self.is_focused() {
2186            match event {
2187                ct_event!(key press c)
2188                | ct_event!(key press SHIFT-c)
2189                | ct_event!(key press CONTROL_ALT-c) => {
2190                    overwrite(self);
2191                    tc(self.insert_char(*c))
2192                }
2193                ct_event!(keycode press Backspace) | ct_event!(keycode press SHIFT-Backspace) => {
2194                    clear_overwrite(self);
2195                    tc(self.delete_prev_char())
2196                }
2197                ct_event!(keycode press Delete) | ct_event!(keycode press SHIFT-Delete) => {
2198                    clear_overwrite(self);
2199                    tc(self.delete_next_char())
2200                }
2201                ct_event!(keycode press CONTROL-Backspace)
2202                | ct_event!(keycode press ALT-Backspace) => {
2203                    clear_overwrite(self);
2204                    tc(self.delete_prev_section())
2205                }
2206                ct_event!(keycode press CONTROL-Delete) => {
2207                    clear_overwrite(self);
2208                    tc(self.delete_next_section())
2209                }
2210                ct_event!(key press CONTROL-'x') => {
2211                    clear_overwrite(self);
2212                    tc(self.cut_to_clip())
2213                }
2214                ct_event!(key press CONTROL-'v') => {
2215                    clear_overwrite(self);
2216                    tc(self.paste_from_clip())
2217                }
2218                ct_event!(key press CONTROL-'d') => {
2219                    clear_overwrite(self);
2220                    tc(self.clear())
2221                }
2222                ct_event!(key press CONTROL-'z') => {
2223                    clear_overwrite(self);
2224                    tc(self.undo())
2225                }
2226                ct_event!(key press CONTROL_SHIFT-'Z') => {
2227                    clear_overwrite(self);
2228                    tc(self.redo())
2229                }
2230                ct_event!(keycode press Tab) => {
2231                    if self.on_tab == TextTab::MoveToNextSection {
2232                        // ignore tab from focus
2233                        if !self.focus.gained() {
2234                            clear_overwrite(self);
2235                            self.select_next_section().into()
2236                        } else {
2237                            TextOutcome::Unchanged
2238                        }
2239                    } else {
2240                        TextOutcome::Continue
2241                    }
2242                }
2243                ct_event!(keycode press SHIFT-BackTab) => {
2244                    if self.on_tab == TextTab::MoveToNextSection {
2245                        // ignore tab from focus
2246                        if !self.focus.gained() {
2247                            clear_overwrite(self);
2248                            self.select_prev_section().into()
2249                        } else {
2250                            TextOutcome::Unchanged
2251                        }
2252                    } else {
2253                        TextOutcome::Continue
2254                    }
2255                }
2256                ct_event!(keycode press ALT-Left) => {
2257                    clear_overwrite(self);
2258                    self.select_prev_section().into()
2259                }
2260                ct_event!(keycode press ALT-Right) => {
2261                    clear_overwrite(self);
2262                    self.select_next_section().into()
2263                }
2264
2265                ct_event!(key release _)
2266                | ct_event!(key release SHIFT-_)
2267                | ct_event!(key release CONTROL_ALT-_)
2268                | ct_event!(keycode release Backspace)
2269                | ct_event!(keycode release Delete)
2270                | ct_event!(keycode release CONTROL-Backspace)
2271                | ct_event!(keycode release ALT-Backspace)
2272                | ct_event!(keycode release CONTROL-Delete)
2273                | ct_event!(key release CONTROL-'x')
2274                | ct_event!(key release CONTROL-'v')
2275                | ct_event!(key release CONTROL-'d')
2276                | ct_event!(key release CONTROL-'z')
2277                | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2278
2279                _ => TextOutcome::Continue,
2280            }
2281        } else {
2282            TextOutcome::Continue
2283        };
2284
2285        if r == TextOutcome::Continue {
2286            r = self.handle(event, ReadOnly);
2287        }
2288        r
2289    }
2290}
2291
2292impl HandleEvent<Event, ReadOnly, TextOutcome> for MaskedInputState {
2293    fn handle(&mut self, event: &Event, _keymap: ReadOnly) -> TextOutcome {
2294        fn clear_overwrite(state: &mut MaskedInputState) {
2295            state.overwrite.set(false);
2296        }
2297
2298        let mut r = if self.is_focused() {
2299            match event {
2300                ct_event!(keycode press Left) => {
2301                    clear_overwrite(self);
2302                    self.move_left(false).into()
2303                }
2304                ct_event!(keycode press Right) => {
2305                    clear_overwrite(self);
2306                    self.move_right(false).into()
2307                }
2308                ct_event!(keycode press CONTROL-Left) => {
2309                    clear_overwrite(self);
2310                    self.move_to_prev_section(false).into()
2311                }
2312                ct_event!(keycode press CONTROL-Right) => {
2313                    clear_overwrite(self);
2314                    self.move_to_next_section(false).into()
2315                }
2316                ct_event!(keycode press Home) => {
2317                    clear_overwrite(self);
2318                    self.move_to_line_start(false).into()
2319                }
2320                ct_event!(keycode press End) => {
2321                    clear_overwrite(self);
2322                    self.move_to_line_end(false).into()
2323                }
2324                ct_event!(keycode press SHIFT-Left) => {
2325                    clear_overwrite(self);
2326                    self.move_left(true).into()
2327                }
2328                ct_event!(keycode press SHIFT-Right) => {
2329                    clear_overwrite(self);
2330                    self.move_right(true).into()
2331                }
2332                ct_event!(keycode press CONTROL_SHIFT-Left) => {
2333                    clear_overwrite(self);
2334                    self.move_to_prev_section(true).into()
2335                }
2336                ct_event!(keycode press CONTROL_SHIFT-Right) => {
2337                    clear_overwrite(self);
2338                    self.move_to_next_section(true).into()
2339                }
2340                ct_event!(keycode press SHIFT-Home) => {
2341                    clear_overwrite(self);
2342                    self.move_to_line_start(true).into()
2343                }
2344                ct_event!(keycode press SHIFT-End) => {
2345                    clear_overwrite(self);
2346                    self.move_to_line_end(true).into()
2347                }
2348                ct_event!(key press CONTROL-'a') => {
2349                    clear_overwrite(self);
2350                    self.select_all().into()
2351                }
2352                ct_event!(key press CONTROL-'c') => {
2353                    clear_overwrite(self);
2354                    self.copy_to_clip().into()
2355                }
2356
2357                ct_event!(keycode release Left)
2358                | ct_event!(keycode release Right)
2359                | ct_event!(keycode release CONTROL-Left)
2360                | ct_event!(keycode release CONTROL-Right)
2361                | ct_event!(keycode release Home)
2362                | ct_event!(keycode release End)
2363                | ct_event!(keycode release SHIFT-Left)
2364                | ct_event!(keycode release SHIFT-Right)
2365                | ct_event!(keycode release CONTROL_SHIFT-Left)
2366                | ct_event!(keycode release CONTROL_SHIFT-Right)
2367                | ct_event!(keycode release SHIFT-Home)
2368                | ct_event!(keycode release SHIFT-End)
2369                | ct_event!(key release CONTROL-'a')
2370                | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2371
2372                _ => TextOutcome::Continue,
2373            }
2374        } else {
2375            TextOutcome::Continue
2376        };
2377
2378        if r == TextOutcome::Continue {
2379            r = self.handle(event, MouseOnly);
2380        }
2381        r
2382    }
2383}
2384
2385impl HandleEvent<Event, MouseOnly, TextOutcome> for MaskedInputState {
2386    fn handle(&mut self, event: &Event, _keymap: MouseOnly) -> TextOutcome {
2387        fn clear_overwrite(state: &mut MaskedInputState) {
2388            state.overwrite.set(false);
2389        }
2390
2391        match event {
2392            ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2393                let c = (m.column as i16) - (self.inner.x as i16);
2394                clear_overwrite(self);
2395                self.set_screen_cursor(c, true).into()
2396            }
2397            ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2398                let cx = m.column as i16 - self.inner.x as i16;
2399                clear_overwrite(self);
2400                self.set_screen_cursor_sections(cx, true).into()
2401            }
2402            ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2403                let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
2404                clear_overwrite(self);
2405                if let Some(range) = self.section_range(tx) {
2406                    self.set_selection(range.start, range.end).into()
2407                } else {
2408                    TextOutcome::Unchanged
2409                }
2410            }
2411            ct_event!(mouse down Left for column,row) => {
2412                if self.gained_focus() {
2413                    // don't react to the first click that's for
2414                    // focus. this one shouldn't demolish the selection.
2415                    TextOutcome::Unchanged
2416                } else if self.inner.contains((*column, *row).into()) {
2417                    let c = (column - self.inner.x) as i16;
2418                    clear_overwrite(self);
2419                    self.set_screen_cursor(c, false).into()
2420                } else {
2421                    TextOutcome::Continue
2422                }
2423            }
2424            ct_event!(mouse down CONTROL-Left for column,row) => {
2425                if self.inner.contains((*column, *row).into()) {
2426                    let cx = (column - self.inner.x) as i16;
2427                    clear_overwrite(self);
2428                    self.set_screen_cursor(cx, true).into()
2429                } else {
2430                    TextOutcome::Continue
2431                }
2432            }
2433            ct_event!(mouse down ALT-Left for column,row) => {
2434                if self.inner.contains((*column, *row).into()) {
2435                    let cx = (column - self.inner.x) as i16;
2436                    clear_overwrite(self);
2437                    self.set_screen_cursor_sections(cx, true).into()
2438                } else {
2439                    TextOutcome::Continue
2440                }
2441            }
2442            _ => TextOutcome::Continue,
2443        }
2444    }
2445}
2446
2447/// Handle all events.
2448/// Text events are only processed if focus is true.
2449/// Mouse events are processed if they are in range.
2450pub fn handle_events(state: &mut MaskedInputState, focus: bool, event: &Event) -> TextOutcome {
2451    state.focus.set(focus);
2452    state.handle(event, Regular)
2453}
2454
2455/// Handle only navigation events.
2456/// Text events are only processed if focus is true.
2457/// Mouse events are processed if they are in range.
2458pub fn handle_readonly_events(
2459    state: &mut TextInputState,
2460    focus: bool,
2461    event: &Event,
2462) -> TextOutcome {
2463    state.focus.set(focus);
2464    state.handle(event, ReadOnly)
2465}
2466
2467/// Handle only mouse-events.
2468pub fn handle_mouse_events(state: &mut MaskedInputState, event: &Event) -> TextOutcome {
2469    state.handle(event, MouseOnly)
2470}