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