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