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