rat_text/
text_input.rs

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