rat_text/
text_input.rs

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