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