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