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