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