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