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