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