rat_text/
text_input_mask.rs

1//! Text input widget with an input mask.
2//!
3//! * Can do the usual insert/delete/move operations.
4//! * Text selection with keyboard + mouse
5//! * Scrolls with the cursor.
6//! * Modes for focus and valid.
7//! * Localization with [format_num_pattern::NumberSymbols]
8//!
9//! * Accepts an input mask:
10//!   * `0`: can enter digit, display as 0
11//!   * `9`: can enter digit, display as space
12//!   * `#`: digit, plus or minus sign, display as space
13//!   * `-`: sign
14//!   * `+`: sign, positive is '+', negative is '-', not localized.
15//!   * `.` and `,`: decimal and grouping separators
16//!
17//!   * `H`: must enter a hex digit, display as 0
18//!   * `h`: can enter a hex digit, display as space
19//!   * `O`: must enter an octal digit, display as 0
20//!   * `o`: can enter an octal digit, display as space
21//!   * `D`: must enter a decimal digit, display as 0
22//!   * `d`: can enter a decimal digit, display as space
23//!
24//!   * `l`: can enter letter, display as space
25//!   * `a`: can enter letter or digit, display as space
26//!   * `c`: can enter character or space, display as space
27//!   * `_`: anything, display as space
28//!
29//!   * `<space>` separator character move the cursor when entered.
30//!   * `\`: escapes the following character and uses it as a separator.
31//!   * everything else must be escaped
32//!
33//! * Accepts a display overlay used instead of the default chars of the input mask.
34//!
35//! ```rust ignore
36//! use ratatui::widgets::StatefulWidget;
37//! use rat_input::masked_input::{MaskedInput, MaskedInputState};
38//!
39//! let date_focused = false;
40//! let creditcard_focused = true;
41//! let area = Rect::default();
42//! let buf = Buffer::default();
43//!
44//! let mut date_state = MaskedInputState::new();
45//! date_state.set_mask("99\\/99\\/9999")?;
46//!
47//! let w_date = MaskedInput::default();
48//! w_date.render(area, &mut buf, &mut date_state);
49//! if date_focused {
50//!     frame.set_cursor(date_state.cursor.x, date_state.cursor.y);
51//! }
52//!
53//! let mut creditcard_state = MaskedInputState::new();
54//! creditcard_state.set_mask("dddd dddd dddd dddd")?;
55//!
56//! let w_creditcard = MaskedInput::default();
57//! w_creditcard.render(area, &mut buf, &mut creditcard_state);
58//! if creditcard_focused {
59//!     frame.set_cursor(creditcard_state.cursor.x, creditcard_state.cursor.y);
60//! }
61//!
62//! ```
63//!
64//! The visual cursor must be set separately after rendering.
65//! It is accessible as [TextInputState::screen_cursor()] after rendering.
66//!
67//! Event handling by calling the freestanding fn [handle_events].
68//! There's [handle_mouse_events] if you want to override the default key bindings but keep
69//! the mouse behaviour.
70//!
71
72use crate::_private::NonExhaustive;
73use crate::clipboard::Clipboard;
74use crate::event::{ReadOnly, TextOutcome};
75use crate::text_input::TextInputState;
76use crate::text_mask_core::MaskedCore;
77use crate::undo_buffer::{UndoBuffer, UndoEntry};
78use crate::{
79    ipos_type, upos_type, Cursor, Glyph, Grapheme, HasScreenCursor, TextError, TextFocusGained,
80    TextFocusLost, TextStyle,
81};
82use crossterm::event::KeyModifiers;
83use format_num_pattern::NumberSymbols;
84use rat_event::util::MouseFlags;
85use rat_event::{ct_event, HandleEvent, MouseOnly, Regular};
86use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
87use rat_reloc::{relocate_area, relocate_dark_offset, RelocatableState};
88use ratatui::buffer::Buffer;
89use ratatui::layout::Rect;
90use ratatui::prelude::BlockExt;
91use ratatui::style::{Style, Stylize};
92use ratatui::widgets::{Block, StatefulWidget, Widget};
93use std::borrow::Cow;
94use std::cmp::min;
95use std::fmt;
96use std::ops::Range;
97
98/// Text input widget with input mask.
99///
100/// # Stateful
101/// This widget implements [`StatefulWidget`], you can use it with
102/// [`MaskedInputState`] to handle common actions.
103#[derive(Debug, Default, Clone)]
104pub struct MaskedInput<'a> {
105    compact: bool,
106    block: Option<Block<'a>>,
107    style: Style,
108    focus_style: Option<Style>,
109    select_style: Option<Style>,
110    invalid_style: Option<Style>,
111    text_style: Vec<Style>,
112    on_focus_gained: TextFocusGained,
113    on_focus_lost: TextFocusLost,
114}
115
116/// State & event-handling.
117#[derive(Debug)]
118pub struct MaskedInputState {
119    /// The whole area with block.
120    /// __read only__ renewed with each render.
121    pub area: Rect,
122    /// Area inside a possible block.
123    /// __read only__ renewed with each render.
124    pub inner: Rect,
125
126    /// Display offset
127    /// __read+write__
128    pub offset: upos_type,
129    /// Dark offset due to clipping.
130    /// __read only__ secondary offset due to clipping.
131    pub dark_offset: (u16, u16),
132    /// __read+write__ use scroll_cursor_to_visible().
133    pub scroll_to_cursor: bool,
134
135    /// Editing core
136    pub value: MaskedCore,
137    /// Display as invalid.
138    /// __read+write__
139    pub invalid: bool,
140    /// The next user edit clears the text for doing any edit.
141    /// It will reset this flag. Other interactions may reset this flag too.
142    pub overwrite: bool,
143    /// Focus behaviour.
144    /// __read only__
145    pub on_focus_gained: TextFocusGained,
146    /// Focus behaviour.
147    /// __read only__
148    pub on_focus_lost: TextFocusLost,
149
150    /// Current focus state.
151    /// __read+write__
152    pub focus: FocusFlag,
153
154    /// Mouse selection in progress.
155    /// __read+write__
156    pub mouse: MouseFlags,
157
158    /// Construct with `..Default::default()`
159    pub non_exhaustive: NonExhaustive,
160}
161
162impl<'a> MaskedInput<'a> {
163    /// New widget.
164    pub fn new() -> Self {
165        Self::default()
166    }
167
168    /// Show a compact form of the content without unnecessary spaces,
169    /// if this widget is not focused.
170    #[inline]
171    pub fn compact(mut self, show_compact: bool) -> Self {
172        self.compact = show_compact;
173        self
174    }
175
176    /// Set the combined style.
177    #[inline]
178    pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
179        if let Some(styles) = styles {
180            self.styles(styles)
181        } else {
182            self
183        }
184    }
185
186    /// Set the combined style.
187    #[inline]
188    pub fn styles(mut self, styles: TextStyle) -> Self {
189        self.style = styles.style;
190        if styles.focus.is_some() {
191            self.focus_style = styles.focus;
192        }
193        if styles.select.is_some() {
194            self.select_style = styles.select;
195        }
196        if styles.invalid.is_some() {
197            self.invalid_style = styles.invalid;
198        }
199        if let Some(of) = styles.on_focus_gained {
200            self.on_focus_gained = of;
201        }
202        if let Some(of) = styles.on_focus_lost {
203            self.on_focus_lost = of;
204        }
205        if let Some(border_style) = styles.border_style {
206            self.block = self.block.map(|v| v.border_style(border_style));
207        }
208        self.block = self.block.map(|v| v.style(self.style));
209        if styles.block.is_some() {
210            self.block = styles.block;
211        }
212        self.block = self.block.map(|v| v.style(self.style));
213        self
214    }
215
216    /// Base text style.
217    #[inline]
218    pub fn style(mut self, style: impl Into<Style>) -> Self {
219        self.style = style.into();
220        self.block = self.block.map(|v| v.style(self.style));
221        self
222    }
223
224    /// Style when focused.
225    #[inline]
226    pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
227        self.focus_style = Some(style.into());
228        self
229    }
230
231    /// Style for selection
232    #[inline]
233    pub fn select_style(mut self, style: impl Into<Style>) -> Self {
234        self.select_style = Some(style.into());
235        self
236    }
237
238    /// Style for the invalid indicator.
239    /// This is patched onto either base_style or focus_style
240    #[inline]
241    pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
242        self.invalid_style = Some(style.into());
243        self
244    }
245
246    /// List of text-styles.
247    ///
248    /// Use [MaskedInputState::add_style()] to refer a text range to
249    /// one of these styles.
250    pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
251        self.text_style = styles.into_iter().collect();
252        self
253    }
254
255    /// Block.
256    #[inline]
257    pub fn block(mut self, block: Block<'a>) -> Self {
258        self.block = Some(block);
259        self.block = self.block.map(|v| v.style(self.style));
260        self
261    }
262
263    /// Focus behaviour
264    #[inline]
265    pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
266        self.on_focus_gained = of;
267        self
268    }
269
270    /// Focus behaviour
271    #[inline]
272    pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
273        self.on_focus_lost = of;
274        self
275    }
276}
277
278impl<'a> StatefulWidget for &MaskedInput<'a> {
279    type State = MaskedInputState;
280
281    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
282        render_ref(self, area, buf, state);
283    }
284}
285
286impl StatefulWidget for MaskedInput<'_> {
287    type State = MaskedInputState;
288
289    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
290        render_ref(&self, area, buf, state);
291    }
292}
293
294fn render_ref(
295    widget: &MaskedInput<'_>,
296    area: Rect,
297
298    buf: &mut Buffer,
299    state: &mut MaskedInputState,
300) {
301    state.area = area;
302    state.inner = widget.block.inner_if_some(area);
303    state.on_focus_gained = widget.on_focus_gained;
304    state.on_focus_lost = widget.on_focus_lost;
305
306    if state.scroll_to_cursor {
307        let c = state.cursor();
308        let o = state.offset();
309        let mut no = if c < o {
310            c
311        } else if c >= o + (state.inner.width + state.dark_offset.0) as upos_type {
312            c.saturating_sub((state.inner.width + state.dark_offset.0) as upos_type)
313        } else {
314            o
315        };
316        // correct by one at right margin. block cursors appear as part of the
317        // right border otherwise.
318        if c == no + (state.inner.width + state.dark_offset.0) as upos_type {
319            no = no.saturating_add(1);
320        }
321        state.set_offset(no);
322    }
323
324    let inner = state.inner;
325
326    let style = widget.style;
327    let focus_style = if let Some(focus_style) = widget.focus_style {
328        focus_style
329    } else {
330        style
331    };
332    let select_style = if let Some(select_style) = widget.select_style {
333        select_style
334    } else {
335        Style::default().black().on_yellow()
336    };
337    let invalid_style = if let Some(invalid_style) = widget.invalid_style {
338        invalid_style
339    } else {
340        Style::default().red()
341    };
342
343    let (style, select_style) = if state.focus.get() {
344        if state.invalid {
345            (
346                style.patch(focus_style).patch(invalid_style),
347                style
348                    .patch(focus_style)
349                    .patch(select_style)
350                    .patch(invalid_style),
351            )
352        } else {
353            (
354                style.patch(focus_style),
355                style.patch(focus_style).patch(select_style),
356            )
357        }
358    } else {
359        if state.invalid {
360            (style.patch(invalid_style), style.patch(invalid_style))
361        } else {
362            (style, style)
363        }
364    };
365
366    // set base style
367    if let Some(block) = &widget.block {
368        block.render(area, buf);
369    } else {
370        buf.set_style(area, style);
371    }
372
373    if inner.width == 0 || inner.height == 0 {
374        // noop
375        return;
376    }
377
378    let ox = state.offset() as u16;
379    // this is just a guess at the display-width
380    let show_range = {
381        let start = ox as upos_type;
382        let end = min(start + inner.width as upos_type, state.len());
383        state.bytes_at_range(start..end)
384    };
385    let selection = state.selection();
386    let mut styles = Vec::new();
387
388    let mut glyph_iter_regular;
389    let mut glyph_iter_cond;
390    let glyph_iter: &mut dyn Iterator<Item = Glyph<'_>>;
391    if state.is_focused() || !widget.compact {
392        glyph_iter_regular = state
393            .value
394            .glyphs(0..1, ox, inner.width)
395            .expect("valid_offset");
396        glyph_iter = &mut glyph_iter_regular;
397    } else {
398        glyph_iter_cond = state
399            .value
400            .condensed_glyphs(0..1, ox, inner.width)
401            .expect("valid_offset");
402        glyph_iter = &mut glyph_iter_cond;
403    }
404
405    for g in glyph_iter {
406        if g.screen_width() > 0 {
407            let mut style = style;
408            styles.clear();
409            state
410                .value
411                .styles_at_page(show_range.clone(), g.text_bytes().start, &mut styles);
412            for style_nr in &styles {
413                if let Some(s) = widget.text_style.get(*style_nr) {
414                    style = style.patch(*s);
415                }
416            }
417            // selection
418            if selection.contains(&g.pos().x) {
419                style = style.patch(select_style);
420            };
421
422            // relative screen-pos of the glyph
423            let screen_pos = g.screen_pos();
424
425            // render glyph
426            if let Some(cell) = buf.cell_mut((inner.x + screen_pos.0, inner.y + screen_pos.1)) {
427                cell.set_symbol(g.glyph());
428                cell.set_style(style);
429            }
430            // clear the reset of the cells to avoid interferences.
431            for d in 1..g.screen_width() {
432                if let Some(cell) =
433                    buf.cell_mut((inner.x + screen_pos.0 + d, inner.y + screen_pos.1))
434                {
435                    cell.reset();
436                    cell.set_style(style);
437                }
438            }
439        }
440    }
441}
442
443impl Clone for MaskedInputState {
444    fn clone(&self) -> Self {
445        Self {
446            area: self.area,
447            inner: self.inner,
448            offset: self.offset,
449            dark_offset: self.dark_offset,
450            scroll_to_cursor: self.scroll_to_cursor,
451            value: self.value.clone(),
452            invalid: self.invalid,
453            overwrite: Default::default(),
454            on_focus_gained: Default::default(),
455            on_focus_lost: Default::default(),
456            focus: FocusFlag::named(self.focus.name()),
457            mouse: Default::default(),
458            non_exhaustive: NonExhaustive,
459        }
460    }
461}
462
463impl Default for MaskedInputState {
464    fn default() -> Self {
465        Self {
466            area: Default::default(),
467            inner: Default::default(),
468            offset: Default::default(),
469            dark_offset: Default::default(),
470            scroll_to_cursor: Default::default(),
471            value: Default::default(),
472            invalid: Default::default(),
473            overwrite: Default::default(),
474            on_focus_gained: Default::default(),
475            on_focus_lost: Default::default(),
476            focus: Default::default(),
477            mouse: Default::default(),
478            non_exhaustive: NonExhaustive,
479        }
480    }
481}
482
483impl HasFocus for MaskedInputState {
484    fn build(&self, builder: &mut FocusBuilder) {
485        builder.leaf_widget(self);
486    }
487
488    fn focus(&self) -> FocusFlag {
489        self.focus.clone()
490    }
491
492    fn area(&self) -> Rect {
493        self.area
494    }
495
496    fn navigable(&self) -> Navigation {
497        let sel = self.selection();
498
499        let has_next = self
500            .value
501            .next_section_range(sel.end)
502            .map(|v| !v.is_empty())
503            .is_some();
504        let has_prev = self
505            .value
506            .prev_section_range(sel.start.saturating_sub(1))
507            .map(|v| !v.is_empty())
508            .is_some();
509
510        if has_next {
511            if has_prev {
512                Navigation::Reach
513            } else {
514                Navigation::ReachLeaveFront
515            }
516        } else {
517            if has_prev {
518                Navigation::ReachLeaveBack
519            } else {
520                Navigation::Regular
521            }
522        }
523    }
524}
525
526impl MaskedInputState {
527    pub fn new() -> Self {
528        Self::default()
529    }
530
531    pub fn named(name: &str) -> Self {
532        Self {
533            focus: FocusFlag::named(name),
534            ..MaskedInputState::default()
535        }
536    }
537
538    /// With localized symbols for number formatting.
539    #[inline]
540    pub fn with_symbols(mut self, sym: NumberSymbols) -> Self {
541        self.set_num_symbols(sym);
542        self
543    }
544
545    /// With input mask.
546    pub fn with_mask<S: AsRef<str>>(mut self, mask: S) -> Result<Self, fmt::Error> {
547        self.value.set_mask(mask.as_ref())?;
548        Ok(self)
549    }
550
551    /// Set symbols for number display.
552    ///
553    /// These are only used for rendering and to map user input.
554    /// The value itself uses ".", "," and "-".
555    #[inline]
556    pub fn set_num_symbols(&mut self, sym: NumberSymbols) {
557        self.value.set_num_symbols(sym);
558    }
559
560    /// Set the input mask. This overwrites the display mask and the value
561    /// with a default representation of the mask.
562    ///
563    /// The result value contains all punctuation and
564    /// the value given as 'display' below.
565    ///
566    /// * `0`: can enter digit, display as 0
567    /// * `9`: can enter digit, display as space
568    /// * `#`: digit, plus or minus sign, display as space
569    /// * `+`: sign. display '+' for positive
570    /// * `-`: sign. display ' ' for positive
571    /// * `.` and `,`: decimal and grouping separators
572    ///
573    /// * `H`: must enter a hex digit, display as 0
574    /// * `h`: can enter a hex digit, display as space
575    /// * `O`: must enter an octal digit, display as 0
576    /// * `o`: can enter an octal digit, display as space
577    /// * `D`: must enter a decimal digit, display as 0
578    /// * `d`: can enter a decimal digit, display as space
579    ///
580    /// * `l`: can enter letter, display as space
581    /// * `a`: can enter letter or digit, display as space
582    /// * `c`: can enter character or space, display as space
583    /// * `_`: anything, display as space
584    ///
585    /// * `SPACE`: separator character move the cursor when entered.
586    /// * `\`: escapes the following character and uses it as a separator.
587    /// * all other ascii characters a reserved.
588    ///
589    /// Inspired by <https://support.microsoft.com/en-gb/office/control-data-entry-formats-with-input-masks-e125997a-7791-49e5-8672-4a47832de8da>
590    #[inline]
591    pub fn set_mask<S: AsRef<str>>(&mut self, s: S) -> Result<(), fmt::Error> {
592        self.value.set_mask(s)
593    }
594
595    /// Display mask.
596    #[inline]
597    pub fn mask(&self) -> String {
598        self.value.mask()
599    }
600
601    /// Renders the widget in invalid style.
602    #[inline]
603    pub fn set_invalid(&mut self, invalid: bool) {
604        self.invalid = invalid;
605    }
606
607    /// Renders the widget in invalid style.
608    #[inline]
609    pub fn get_invalid(&self) -> bool {
610        self.invalid
611    }
612
613    /// The next edit operation will overwrite the current content
614    /// instead of adding text. Any move operations will cancel
615    /// this overwrite.
616    #[inline]
617    pub fn set_overwrite(&mut self, overwrite: bool) {
618        self.overwrite = overwrite;
619    }
620
621    /// Will the next edit operation overwrite the content?
622    #[inline]
623    pub fn overwrite(&self) -> bool {
624        self.overwrite
625    }
626}
627
628impl MaskedInputState {
629    /// Clipboard used.
630    /// Default is to use the global_clipboard().
631    #[inline]
632    pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
633        match clip {
634            None => self.value.set_clipboard(None),
635            Some(v) => self.value.set_clipboard(Some(Box::new(v))),
636        }
637    }
638
639    /// Clipboard used.
640    /// Default is to use the global_clipboard().
641    #[inline]
642    pub fn clipboard(&self) -> Option<&dyn Clipboard> {
643        self.value.clipboard()
644    }
645
646    /// Copy to internal buffer
647    #[inline]
648    pub fn copy_to_clip(&mut self) -> bool {
649        let Some(clip) = self.value.clipboard() else {
650            return false;
651        };
652
653        _ = clip.set_string(self.selected_text().as_ref());
654
655        true
656    }
657
658    /// Cut to internal buffer
659    #[inline]
660    pub fn cut_to_clip(&mut self) -> bool {
661        let Some(clip) = self.value.clipboard() else {
662            return false;
663        };
664
665        match clip.set_string(self.selected_text().as_ref()) {
666            Ok(_) => self.delete_range(self.selection()),
667            Err(_) => true,
668        }
669    }
670
671    /// Paste from internal buffer.
672    #[inline]
673    pub fn paste_from_clip(&mut self) -> bool {
674        let Some(clip) = self.value.clipboard() else {
675            return false;
676        };
677
678        if let Ok(text) = clip.get_string() {
679            for c in text.chars() {
680                self.insert_char(c);
681            }
682            true
683        } else {
684            false
685        }
686    }
687}
688
689impl MaskedInputState {
690    /// Set undo buffer.
691    #[inline]
692    pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
693        match undo {
694            None => self.value.set_undo_buffer(None),
695            Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
696        }
697    }
698
699    /// Undo
700    #[inline]
701    pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
702        self.value.undo_buffer()
703    }
704
705    /// Undo
706    #[inline]
707    pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
708        self.value.undo_buffer_mut()
709    }
710
711    /// Get all recent replay recordings.
712    #[inline]
713    pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
714        self.value.recent_replay_log()
715    }
716
717    /// Apply the replay recording.
718    #[inline]
719    pub fn replay_log(&mut self, replay: &[UndoEntry]) {
720        self.value.replay_log(replay)
721    }
722
723    /// Undo operation
724    #[inline]
725    pub fn undo(&mut self) -> bool {
726        self.value.undo()
727    }
728
729    /// Redo operation
730    #[inline]
731    pub fn redo(&mut self) -> bool {
732        self.value.redo()
733    }
734}
735
736impl MaskedInputState {
737    /// Set and replace all styles.
738    #[inline]
739    pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
740        self.value.set_styles(styles);
741    }
742
743    /// Add a style for a byte-range. The style-nr refers to
744    /// one of the styles set with the widget.
745    #[inline]
746    pub fn add_style(&mut self, range: Range<usize>, style: usize) {
747        self.value.add_style(range, style);
748    }
749
750    /// Add a style for a `Range<upos_type>` to denote the cells.
751    /// The style-nr refers to one of the styles set with the widget.
752    #[inline]
753    pub fn add_range_style(
754        &mut self,
755        range: Range<upos_type>,
756        style: usize,
757    ) -> Result<(), TextError> {
758        let r = self.value.bytes_at_range(range)?;
759        self.value.add_style(r, style);
760        Ok(())
761    }
762
763    /// Remove the exact byte-range and style.
764    #[inline]
765    pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
766        self.value.remove_style(range, style);
767    }
768
769    /// Remove the exact `Range<upos_type>` and style.
770    #[inline]
771    pub fn remove_range_style(
772        &mut self,
773        range: Range<upos_type>,
774        style: usize,
775    ) -> Result<(), TextError> {
776        let r = self.value.bytes_at_range(range)?;
777        self.value.remove_style(r, style);
778        Ok(())
779    }
780
781    /// Find all styles that touch the given range.
782    pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
783        self.value.styles_in(range, buf)
784    }
785
786    /// All styles active at the given position.
787    #[inline]
788    pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
789        self.value.styles_at(byte_pos, buf)
790    }
791
792    /// Check if the given style applies at the position and
793    /// return the complete range for the style.
794    #[inline]
795    pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
796        self.value.style_match(byte_pos, style)
797    }
798
799    /// List of all styles.
800    #[inline]
801    pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
802        self.value.styles()
803    }
804}
805
806impl MaskedInputState {
807    /// Offset shown.
808    #[inline]
809    pub fn offset(&self) -> upos_type {
810        self.offset
811    }
812
813    /// Offset shown. This is corrected if the cursor wouldn't be visible.
814    #[inline]
815    pub fn set_offset(&mut self, offset: upos_type) {
816        self.scroll_to_cursor = false;
817        self.offset = offset;
818    }
819
820    /// Cursor position.
821    #[inline]
822    pub fn cursor(&self) -> upos_type {
823        self.value.cursor()
824    }
825
826    /// Set the cursor position.
827    /// Scrolls the cursor to a visible position.
828    #[inline]
829    pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
830        self.scroll_cursor_to_visible();
831        self.value.set_cursor(cursor, extend_selection)
832    }
833
834    /// Place cursor at the decimal separator, if any.
835    /// 0 otherwise.
836    /// Scrolls the cursor to a visible position.  
837    #[inline]
838    pub fn set_default_cursor(&mut self) {
839        self.scroll_cursor_to_visible();
840        self.value.set_default_cursor();
841    }
842
843    /// Selection anchor.
844    #[inline]
845    pub fn anchor(&self) -> upos_type {
846        self.value.anchor()
847    }
848
849    /// Selection.
850    #[inline]
851    pub fn has_selection(&self) -> bool {
852        self.value.has_selection()
853    }
854
855    /// Selection.
856    #[inline]
857    pub fn selection(&self) -> Range<upos_type> {
858        self.value.selection()
859    }
860
861    /// Selection.
862    /// Scrolls the cursor to a visible position.
863    #[inline]
864    pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
865        self.scroll_cursor_to_visible();
866        self.value.set_selection(anchor, cursor)
867    }
868
869    /// Selection.
870    /// Scrolls the cursor to a visible position.
871    #[inline]
872    pub fn select_all(&mut self) -> bool {
873        self.scroll_cursor_to_visible();
874        if let Some(section) = self.value.section_range(self.cursor()) {
875            if self.selection() == section {
876                self.value.select_all()
877            } else {
878                self.value.set_selection(section.start, section.end)
879            }
880        } else {
881            self.value.select_all()
882        }
883    }
884
885    /// Selection.
886    #[inline]
887    pub fn selected_text(&self) -> &str {
888        self.value.selected_text()
889    }
890}
891
892impl MaskedInputState {
893    /// Empty
894    #[inline]
895    pub fn is_empty(&self) -> bool {
896        self.value.is_empty()
897    }
898
899    /// Value with all punctuation and default values according to the mask type.
900    #[inline]
901    pub fn text(&self) -> &str {
902        self.value.text()
903    }
904
905    /// Text slice as `Cow<str>`. Uses a byte range.
906    #[inline]
907    pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
908        self.value.str_slice_byte(range).expect("valid_range")
909    }
910
911    /// Text slice as `Cow<str>`. Uses a byte range.
912    #[inline]
913    pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
914        self.value.str_slice_byte(range)
915    }
916
917    /// Text slice as `Cow<str>`
918    #[inline]
919    pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
920        self.value.str_slice(range).expect("valid_range")
921    }
922
923    /// Text slice as `Cow<str>`
924    #[inline]
925    pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
926        self.value.str_slice(range)
927    }
928
929    /// Length as grapheme count.
930    #[inline]
931    pub fn len(&self) -> upos_type {
932        self.value.line_width()
933    }
934
935    /// Length as grapheme count.
936    #[inline]
937    pub fn line_width(&self) -> upos_type {
938        self.value.line_width()
939    }
940
941    /// Iterator for the glyphs of the lines in range.
942    /// Glyphs here a grapheme + display length.
943    #[inline]
944    pub fn glyphs(&self, screen_offset: u16, screen_width: u16) -> impl Iterator<Item = Glyph<'_>> {
945        self.value
946            .glyphs(0..1, screen_offset, screen_width)
947            .expect("valid_row")
948    }
949
950    /// Iterator for the glyphs of the lines in range.
951    /// Glyphs here a grapheme + display length.
952    #[inline]
953    pub fn condensed_glyphs(
954        &self,
955        screen_offset: u16,
956        screen_width: u16,
957    ) -> impl Iterator<Item = Glyph<'_>> {
958        self.value
959            .condensed_glyphs(0..1, screen_offset, screen_width)
960            .expect("valid_row")
961    }
962
963    /// Get a cursor over all the text with the current position set at pos.
964    #[inline]
965    pub fn text_graphemes(&self, pos: upos_type) -> impl Cursor<Item = Grapheme<'_>> {
966        self.value.text_graphemes(pos).expect("valid_pos")
967    }
968
969    /// Get a cursor over all the text with the current position set at pos.
970    #[inline]
971    pub fn try_text_graphemes(
972        &self,
973        pos: upos_type,
974    ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
975        self.value.text_graphemes(pos)
976    }
977
978    /// Get a cursor over the text-range the current position set at pos.
979    #[inline]
980    pub fn graphemes(
981        &self,
982        range: Range<upos_type>,
983        pos: upos_type,
984    ) -> impl Cursor<Item = Grapheme<'_>> {
985        self.value.graphemes(range, pos).expect("valid_args")
986    }
987
988    /// Get a cursor over the text-range the current position set at pos.
989    #[inline]
990    pub fn try_graphemes(
991        &self,
992        range: Range<upos_type>,
993        pos: upos_type,
994    ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
995        self.value.graphemes(range, pos)
996    }
997
998    /// Grapheme position to byte position.
999    /// This is the (start,end) position of the single grapheme after pos.
1000    #[inline]
1001    pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
1002        self.value.byte_at(pos).expect("valid_pos")
1003    }
1004
1005    /// Grapheme position to byte position.
1006    /// This is the (start,end) position of the single grapheme after pos.
1007    #[inline]
1008    pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
1009        self.value.byte_at(pos)
1010    }
1011
1012    /// Grapheme range to byte range.
1013    #[inline]
1014    pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
1015        self.value.bytes_at_range(range).expect("valid_range")
1016    }
1017
1018    /// Grapheme range to byte range.
1019    #[inline]
1020    pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
1021        self.value.bytes_at_range(range)
1022    }
1023
1024    /// Byte position to grapheme position.
1025    /// Returns the position that contains the given byte index.
1026    #[inline]
1027    pub fn byte_pos(&self, byte: usize) -> upos_type {
1028        self.value.byte_pos(byte).expect("valid_pos")
1029    }
1030
1031    /// Byte position to grapheme position.
1032    /// Returns the position that contains the given byte index.
1033    #[inline]
1034    pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
1035        self.value.byte_pos(byte)
1036    }
1037
1038    /// Byte range to grapheme range.
1039    #[inline]
1040    pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
1041        self.value.byte_range(bytes).expect("valid_range")
1042    }
1043
1044    /// Byte range to grapheme range.
1045    #[inline]
1046    pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
1047        self.value.byte_range(bytes)
1048    }
1049}
1050
1051impl MaskedInputState {
1052    /// Reset to empty.
1053    #[inline]
1054    pub fn clear(&mut self) -> bool {
1055        if self.is_empty() {
1056            false
1057        } else {
1058            self.offset = 0;
1059            self.value.clear();
1060            true
1061        }
1062    }
1063
1064    /// Set the value.
1065    ///
1066    /// No checks if the value conforms to the mask.
1067    /// If the value is too short it will be filled with space.
1068    /// if the value is too long it will be truncated.
1069    #[inline]
1070    pub fn set_text<S: Into<String>>(&mut self, s: S) {
1071        self.offset = 0;
1072        self.value.set_text(s);
1073        self.value.set_default_cursor();
1074    }
1075
1076    /// Insert a char at the current position.
1077    #[inline]
1078    pub fn insert_char(&mut self, c: char) -> bool {
1079        self.value.begin_undo_seq();
1080        if self.value.has_selection() {
1081            let sel = self.value.selection();
1082            self.value
1083                .remove_range(sel.clone())
1084                .expect("valid_selection");
1085            self.value.set_cursor(sel.start, false);
1086        }
1087        let c0 = self.value.advance_cursor(c);
1088        let c1 = self.value.insert_char(c);
1089        self.value.end_undo_seq();
1090
1091        self.scroll_cursor_to_visible();
1092        c0 || c1
1093    }
1094
1095    /// Remove the selected range. The text will be replaced with the default value
1096    /// as defined by the mask.
1097    #[inline]
1098    pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1099        self.try_delete_range(range).expect("valid_range")
1100    }
1101
1102    /// Remove the selected range. The text will be replaced with the default value
1103    /// as defined by the mask.
1104    #[inline]
1105    pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1106        self.value.begin_undo_seq();
1107        let r = self.value.remove_range(range.clone())?;
1108        if let Some(pos) = self.value.section_cursor(range.start) {
1109            self.value.set_cursor(pos, false);
1110        }
1111        self.value.end_undo_seq();
1112
1113        self.scroll_cursor_to_visible();
1114        Ok(r)
1115    }
1116}
1117
1118impl MaskedInputState {
1119    /// Delete the char after the cursor.
1120    #[inline]
1121    pub fn delete_next_char(&mut self) -> bool {
1122        if self.has_selection() {
1123            self.delete_range(self.selection())
1124        } else if self.cursor() == self.len() {
1125            false
1126        } else {
1127            self.value.remove_next();
1128            self.scroll_cursor_to_visible();
1129            true
1130        }
1131    }
1132
1133    /// Delete the char before the cursor.
1134    #[inline]
1135    pub fn delete_prev_char(&mut self) -> bool {
1136        if self.has_selection() {
1137            self.delete_range(self.selection())
1138        } else if self.cursor() == 0 {
1139            false
1140        } else {
1141            self.value.remove_prev();
1142            self.scroll_cursor_to_visible();
1143            true
1144        }
1145    }
1146
1147    /// Delete the previous section.
1148    #[inline]
1149    pub fn delete_prev_section(&mut self) -> bool {
1150        if self.has_selection() {
1151            self.delete_range(self.selection())
1152        } else {
1153            if let Some(range) = self.value.prev_section_range(self.cursor()) {
1154                self.delete_range(range)
1155            } else {
1156                false
1157            }
1158        }
1159    }
1160
1161    /// Delete the next section.
1162    #[inline]
1163    pub fn delete_next_section(&mut self) -> bool {
1164        if self.has_selection() {
1165            self.delete_range(self.selection())
1166        } else {
1167            if let Some(range) = self.value.next_section_range(self.cursor()) {
1168                self.delete_range(range)
1169            } else {
1170                false
1171            }
1172        }
1173    }
1174
1175    /// Move to the next char.
1176    #[inline]
1177    pub fn move_right(&mut self, extend_selection: bool) -> bool {
1178        let c = min(self.cursor() + 1, self.len());
1179        self.set_cursor(c, extend_selection)
1180    }
1181
1182    /// Move to the previous char.
1183    #[inline]
1184    pub fn move_left(&mut self, extend_selection: bool) -> bool {
1185        let c = self.cursor().saturating_sub(1);
1186        self.set_cursor(c, extend_selection)
1187    }
1188
1189    /// Start of line
1190    #[inline]
1191    pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1192        if let Some(c) = self.value.section_cursor(self.cursor()) {
1193            if c != self.cursor() {
1194                self.set_cursor(c, extend_selection)
1195            } else {
1196                self.set_cursor(0, extend_selection)
1197            }
1198        } else {
1199            self.set_cursor(0, extend_selection)
1200        }
1201    }
1202
1203    /// End of line
1204    #[inline]
1205    pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1206        self.set_cursor(self.len(), extend_selection)
1207    }
1208
1209    /// Move to start of previous section.
1210    #[inline]
1211    pub fn move_to_prev_section(&mut self, extend_selection: bool) -> bool {
1212        if let Some(curr) = self.value.section_range(self.cursor()) {
1213            if self.value.cursor() != curr.start {
1214                return self.value.set_cursor(curr.start, extend_selection);
1215            }
1216        }
1217        if let Some(range) = self.value.prev_section_range(self.cursor()) {
1218            self.value.set_cursor(range.start, extend_selection)
1219        } else {
1220            false
1221        }
1222    }
1223
1224    /// Move to end of previous section.
1225    #[inline]
1226    pub fn move_to_next_section(&mut self, extend_selection: bool) -> bool {
1227        if let Some(curr) = self.value.section_range(self.cursor()) {
1228            if self.value.cursor() != curr.end {
1229                return self.value.set_cursor(curr.end, extend_selection);
1230            }
1231        }
1232        if let Some(range) = self.value.next_section_range(self.cursor()) {
1233            self.value.set_cursor(range.end, extend_selection)
1234        } else {
1235            false
1236        }
1237    }
1238
1239    /// Select next section.
1240    #[inline]
1241    pub fn select_current_section(&mut self) -> bool {
1242        let selection = self.selection();
1243
1244        if let Some(next) = self.value.section_range(selection.start.saturating_sub(1)) {
1245            if !next.is_empty() {
1246                self.set_selection(next.start, next.end)
1247            } else {
1248                false
1249            }
1250        } else {
1251            false
1252        }
1253    }
1254
1255    /// Select next section.
1256    #[inline]
1257    pub fn select_next_section(&mut self) -> bool {
1258        let selection = self.selection();
1259
1260        if let Some(next) = self.value.next_section_range(selection.start) {
1261            if !next.is_empty() {
1262                self.set_selection(next.start, next.end)
1263            } else {
1264                false
1265            }
1266        } else {
1267            false
1268        }
1269    }
1270
1271    /// Select previous section.
1272    #[inline]
1273    pub fn select_prev_section(&mut self) -> bool {
1274        let selection = self.selection();
1275
1276        if let Some(next) = self
1277            .value
1278            .prev_section_range(selection.start.saturating_sub(1))
1279        {
1280            if !next.is_empty() {
1281                self.set_selection(next.start, next.end)
1282            } else {
1283                false
1284            }
1285        } else {
1286            false
1287        }
1288    }
1289}
1290
1291impl HasScreenCursor for MaskedInputState {
1292    /// The current text cursor as an absolute screen position.
1293    #[inline]
1294    fn screen_cursor(&self) -> Option<(u16, u16)> {
1295        if self.is_focused() {
1296            if self.has_selection() {
1297                None
1298            } else {
1299                let cx = self.cursor();
1300                let ox = self.offset();
1301
1302                if cx < ox {
1303                    None
1304                } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1305                    None
1306                } else {
1307                    self.col_to_screen(cx)
1308                        .map(|sc| (self.inner.x + sc, self.inner.y))
1309                }
1310            }
1311        } else {
1312            None
1313        }
1314    }
1315}
1316
1317impl RelocatableState for MaskedInputState {
1318    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1319        // clip offset for some corrections.
1320        self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1321        self.area = relocate_area(self.area, shift, clip);
1322        self.inner = relocate_area(self.inner, shift, clip);
1323    }
1324}
1325
1326impl MaskedInputState {
1327    /// Converts from a widget relative screen coordinate to a grapheme index.
1328    /// x is the relative screen position.
1329    pub fn screen_to_col(&self, scx: i16) -> upos_type {
1330        let ox = self.offset();
1331
1332        let scx = scx + self.dark_offset.0 as i16;
1333
1334        if scx < 0 {
1335            ox.saturating_sub((scx as ipos_type).unsigned_abs())
1336        } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
1337            min(ox + scx as upos_type, self.len())
1338        } else {
1339            let scx = scx as u16;
1340
1341            let line = self.glyphs(ox as u16, self.inner.width + self.dark_offset.0);
1342
1343            let mut col = ox;
1344            for g in line {
1345                if scx < g.screen_pos().0 + g.screen_width() {
1346                    break;
1347                }
1348                col = g.pos().x + 1;
1349            }
1350            col
1351        }
1352    }
1353
1354    /// Converts a grapheme based position to a screen position
1355    /// relative to the widget area.
1356    pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1357        let ox = self.offset();
1358
1359        if pos < ox {
1360            return None;
1361        }
1362
1363        let line = self.glyphs(ox as u16, self.inner.width + self.dark_offset.0);
1364        let mut screen_x = 0;
1365        for g in line {
1366            if g.pos().x == pos {
1367                break;
1368            }
1369            screen_x = g.screen_pos().0 + g.screen_width();
1370        }
1371
1372        if screen_x >= self.dark_offset.0 {
1373            Some(screen_x - self.dark_offset.0)
1374        } else {
1375            None
1376        }
1377    }
1378
1379    /// Set the cursor position from a screen position relative to the origin
1380    /// of the widget. This value can be negative, which selects a currently
1381    /// not visible position and scrolls to it.
1382    #[inline]
1383    pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1384        let scx = cursor;
1385
1386        let cx = self.screen_to_col(scx);
1387
1388        self.set_cursor(cx, extend_selection)
1389    }
1390
1391    /// Set the cursor position from screen coordinates,
1392    /// rounds the position to the next section bounds.
1393    ///
1394    /// The cursor positions are relative to the inner rect.
1395    /// They may be negative too, this allows setting the cursor
1396    /// to a position that is currently scrolled away.
1397    pub fn set_screen_cursor_sections(
1398        &mut self,
1399        screen_cursor: i16,
1400        extend_selection: bool,
1401    ) -> bool {
1402        let anchor = self.anchor();
1403        let cursor = self.screen_to_col(screen_cursor);
1404
1405        let Some(range) = self.value.section_range(cursor) else {
1406            return false;
1407        };
1408
1409        let cursor = if cursor < anchor {
1410            range.start
1411        } else {
1412            range.end
1413        };
1414
1415        // extend anchor
1416        if !self.value.is_section_boundary(anchor) {
1417            if let Some(range) = self.value.section_range(anchor) {
1418                if cursor < anchor {
1419                    self.set_cursor(range.end, false);
1420                } else {
1421                    self.set_cursor(range.start, false);
1422                }
1423            };
1424        }
1425
1426        self.set_cursor(cursor, extend_selection)
1427    }
1428
1429    /// Scrolling
1430    pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1431        self.set_offset(self.offset.saturating_sub(delta));
1432        true
1433    }
1434
1435    /// Scrolling
1436    pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1437        self.set_offset(self.offset + delta);
1438        true
1439    }
1440
1441    /// Change the offset in a way that the cursor is visible.
1442    pub fn scroll_cursor_to_visible(&mut self) {
1443        self.scroll_to_cursor = true;
1444    }
1445}
1446
1447impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for MaskedInputState {
1448    fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1449        // small helper ...
1450        fn tc(r: bool) -> TextOutcome {
1451            if r {
1452                TextOutcome::TextChanged
1453            } else {
1454                TextOutcome::Unchanged
1455            }
1456        }
1457        fn overwrite(state: &mut MaskedInputState) {
1458            if state.overwrite {
1459                state.overwrite = false;
1460                state.clear();
1461            }
1462        }
1463        fn clear_overwrite(state: &mut MaskedInputState) {
1464            state.overwrite = false;
1465        }
1466
1467        // focus behaviour
1468        if self.lost_focus() {
1469            match self.on_focus_lost {
1470                TextFocusLost::None => {}
1471                TextFocusLost::Position0 => {
1472                    self.set_default_cursor();
1473                    self.scroll_cursor_to_visible();
1474                    // repaint is triggered by focus-change
1475                }
1476            }
1477        }
1478        if self.gained_focus() {
1479            match self.on_focus_gained {
1480                TextFocusGained::None => {}
1481                TextFocusGained::Overwrite => {
1482                    self.overwrite = true;
1483                }
1484                TextFocusGained::SelectAll => {
1485                    self.select_all();
1486                    // repaint is triggered by focus-change
1487                }
1488            }
1489        }
1490
1491        let mut r = if self.is_focused() {
1492            match event {
1493                ct_event!(key press c)
1494                | ct_event!(key press SHIFT-c)
1495                | ct_event!(key press CONTROL_ALT-c) => {
1496                    overwrite(self);
1497                    tc(self.insert_char(*c))
1498                }
1499                ct_event!(keycode press Backspace) => {
1500                    clear_overwrite(self);
1501                    tc(self.delete_prev_char())
1502                }
1503                ct_event!(keycode press Delete) => {
1504                    clear_overwrite(self);
1505                    tc(self.delete_next_char())
1506                }
1507                ct_event!(keycode press CONTROL-Backspace)
1508                | ct_event!(keycode press ALT-Backspace) => {
1509                    clear_overwrite(self);
1510                    tc(self.delete_prev_section())
1511                }
1512                ct_event!(keycode press CONTROL-Delete) => {
1513                    clear_overwrite(self);
1514                    tc(self.delete_next_section())
1515                }
1516                ct_event!(key press CONTROL-'x') => {
1517                    clear_overwrite(self);
1518                    tc(self.cut_to_clip())
1519                }
1520                ct_event!(key press CONTROL-'v') => {
1521                    clear_overwrite(self);
1522                    tc(self.paste_from_clip())
1523                }
1524                ct_event!(key press CONTROL-'d') => {
1525                    clear_overwrite(self);
1526                    tc(self.clear())
1527                }
1528                ct_event!(key press CONTROL-'z') => {
1529                    clear_overwrite(self);
1530                    tc(self.undo())
1531                }
1532                ct_event!(key press CONTROL_SHIFT-'Z') => {
1533                    clear_overwrite(self);
1534                    tc(self.redo())
1535                }
1536
1537                ct_event!(key release _)
1538                | ct_event!(key release SHIFT-_)
1539                | ct_event!(key release CONTROL_ALT-_)
1540                | ct_event!(keycode release Backspace)
1541                | ct_event!(keycode release Delete)
1542                | ct_event!(keycode release CONTROL-Backspace)
1543                | ct_event!(keycode release ALT-Backspace)
1544                | ct_event!(keycode release CONTROL-Delete)
1545                | ct_event!(key release CONTROL-'x')
1546                | ct_event!(key release CONTROL-'v')
1547                | ct_event!(key release CONTROL-'d')
1548                | ct_event!(key release CONTROL-'z')
1549                | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1550
1551                _ => TextOutcome::Continue,
1552            }
1553        } else {
1554            TextOutcome::Continue
1555        };
1556
1557        if r == TextOutcome::Continue {
1558            r = self.handle(event, ReadOnly);
1559        }
1560        r
1561    }
1562}
1563
1564impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for MaskedInputState {
1565    fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1566        fn clear_overwrite(state: &mut MaskedInputState) {
1567            state.overwrite = false;
1568        }
1569
1570        let mut r = if self.is_focused() {
1571            match event {
1572                ct_event!(keycode press Left) => {
1573                    clear_overwrite(self);
1574                    self.move_left(false).into()
1575                }
1576                ct_event!(keycode press Right) => {
1577                    clear_overwrite(self);
1578                    self.move_right(false).into()
1579                }
1580                ct_event!(keycode press CONTROL-Left) => {
1581                    clear_overwrite(self);
1582                    self.move_to_prev_section(false).into()
1583                }
1584                ct_event!(keycode press CONTROL-Right) => {
1585                    clear_overwrite(self);
1586                    self.move_to_next_section(false).into()
1587                }
1588                ct_event!(keycode press Home) => {
1589                    clear_overwrite(self);
1590                    self.move_to_line_start(false).into()
1591                }
1592                ct_event!(keycode press End) => {
1593                    clear_overwrite(self);
1594                    self.move_to_line_end(false).into()
1595                }
1596                ct_event!(keycode press SHIFT-Left) => {
1597                    clear_overwrite(self);
1598                    self.move_left(true).into()
1599                }
1600                ct_event!(keycode press SHIFT-Right) => {
1601                    clear_overwrite(self);
1602                    self.move_right(true).into()
1603                }
1604                ct_event!(keycode press CONTROL_SHIFT-Left) => {
1605                    clear_overwrite(self);
1606                    self.move_to_prev_section(true).into()
1607                }
1608                ct_event!(keycode press CONTROL_SHIFT-Right) => {
1609                    clear_overwrite(self);
1610                    self.move_to_next_section(true).into()
1611                }
1612                ct_event!(keycode press SHIFT-Home) => {
1613                    clear_overwrite(self);
1614                    self.move_to_line_start(true).into()
1615                }
1616                ct_event!(keycode press SHIFT-End) => {
1617                    clear_overwrite(self);
1618                    self.move_to_line_end(true).into()
1619                }
1620                ct_event!(keycode press Tab) => {
1621                    // ignore tab from focus
1622                    if !self.focus.gained() {
1623                        clear_overwrite(self);
1624                        self.select_next_section().into()
1625                    } else {
1626                        TextOutcome::Unchanged
1627                    }
1628                }
1629                ct_event!(keycode press SHIFT-BackTab) => {
1630                    // ignore tab from focus
1631                    if !self.focus.gained() {
1632                        clear_overwrite(self);
1633                        self.select_prev_section().into()
1634                    } else {
1635                        TextOutcome::Unchanged
1636                    }
1637                }
1638                ct_event!(key press CONTROL-'a') => {
1639                    clear_overwrite(self);
1640                    self.select_all().into()
1641                }
1642                ct_event!(key press CONTROL-'c') => {
1643                    clear_overwrite(self);
1644                    self.copy_to_clip().into()
1645                }
1646
1647                ct_event!(keycode release Left)
1648                | ct_event!(keycode release Right)
1649                | ct_event!(keycode release CONTROL-Left)
1650                | ct_event!(keycode release CONTROL-Right)
1651                | ct_event!(keycode release Home)
1652                | ct_event!(keycode release End)
1653                | ct_event!(keycode release SHIFT-Left)
1654                | ct_event!(keycode release SHIFT-Right)
1655                | ct_event!(keycode release CONTROL_SHIFT-Left)
1656                | ct_event!(keycode release CONTROL_SHIFT-Right)
1657                | ct_event!(keycode release SHIFT-Home)
1658                | ct_event!(keycode release SHIFT-End)
1659                | ct_event!(key release CONTROL-'a')
1660                | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1661
1662                _ => TextOutcome::Continue,
1663            }
1664        } else {
1665            TextOutcome::Continue
1666        };
1667
1668        if r == TextOutcome::Continue {
1669            r = self.handle(event, MouseOnly);
1670        }
1671        r
1672    }
1673}
1674
1675impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for MaskedInputState {
1676    fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1677        fn clear_overwrite(state: &mut MaskedInputState) {
1678            state.overwrite = false;
1679        }
1680
1681        match event {
1682            ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1683                let c = (m.column as i16) - (self.inner.x as i16);
1684                clear_overwrite(self);
1685                self.set_screen_cursor(c, true).into()
1686            }
1687            ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1688                let cx = m.column as i16 - self.inner.x as i16;
1689                clear_overwrite(self);
1690                self.set_screen_cursor_sections(cx, true).into()
1691            }
1692            ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1693                let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1694                clear_overwrite(self);
1695                if let Some(range) = self.value.section_range(tx) {
1696                    self.set_selection(range.start, range.end).into()
1697                } else {
1698                    TextOutcome::Unchanged
1699                }
1700            }
1701            ct_event!(mouse down Left for column,row) => {
1702                if self.gained_focus() {
1703                    // don't react to the first click that's for
1704                    // focus. this one shouldn't demolish the selection.
1705                    TextOutcome::Unchanged
1706                } else if self.inner.contains((*column, *row).into()) {
1707                    let c = (column - self.inner.x) as i16;
1708                    clear_overwrite(self);
1709                    self.set_screen_cursor(c, false).into()
1710                } else {
1711                    TextOutcome::Continue
1712                }
1713            }
1714            ct_event!(mouse down CONTROL-Left for column,row) => {
1715                if self.inner.contains((*column, *row).into()) {
1716                    let cx = (column - self.inner.x) as i16;
1717                    clear_overwrite(self);
1718                    self.set_screen_cursor(cx, true).into()
1719                } else {
1720                    TextOutcome::Continue
1721                }
1722            }
1723            ct_event!(mouse down ALT-Left for column,row) => {
1724                if self.inner.contains((*column, *row).into()) {
1725                    let cx = (column - self.inner.x) as i16;
1726                    clear_overwrite(self);
1727                    self.set_screen_cursor_sections(cx, true).into()
1728                } else {
1729                    TextOutcome::Continue
1730                }
1731            }
1732            _ => TextOutcome::Continue,
1733        }
1734    }
1735}
1736
1737/// Handle all events.
1738/// Text events are only processed if focus is true.
1739/// Mouse events are processed if they are in range.
1740pub fn handle_events(
1741    state: &mut MaskedInputState,
1742    focus: bool,
1743    event: &crossterm::event::Event,
1744) -> TextOutcome {
1745    state.focus.set(focus);
1746    state.handle(event, Regular)
1747}
1748
1749/// Handle only navigation events.
1750/// Text events are only processed if focus is true.
1751/// Mouse events are processed if they are in range.
1752pub fn handle_readonly_events(
1753    state: &mut TextInputState,
1754    focus: bool,
1755    event: &crossterm::event::Event,
1756) -> TextOutcome {
1757    state.focus.set(focus);
1758    state.handle(event, ReadOnly)
1759}
1760
1761/// Handle only mouse-events.
1762pub fn handle_mouse_events(
1763    state: &mut MaskedInputState,
1764    event: &crossterm::event::Event,
1765) -> TextOutcome {
1766    state.handle(event, MouseOnly)
1767}