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