Skip to main content

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    /// Clear the text, any styles and the undo buffer.
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    /// Clears the undo-buffer and sets the cursor to 0.
1156    #[inline]
1157    pub fn set_value<S: Into<String>>(&mut self, s: S) {
1158        self.offset = 0;
1159        self.value.set_text(TextString::new_string(s.into()));
1160    }
1161
1162    /// Set text.
1163    /// Clears the undo-buffer and sets the cursor to 0.
1164    #[inline]
1165    pub fn set_text<S: Into<String>>(&mut self, s: S) {
1166        self.offset = 0;
1167        self.value.set_text(TextString::new_string(s.into()));
1168    }
1169
1170    /// Insert a char at the current position.
1171    #[inline]
1172    pub fn insert_char(&mut self, c: char) -> bool {
1173        if self.has_selection() {
1174            self.value
1175                .remove_str_range(self.value.selection())
1176                .expect("valid_selection");
1177        }
1178        if c == '\n' {
1179            return false;
1180        } else {
1181            self.value
1182                .insert_char(self.value.cursor(), c)
1183                .expect("valid_cursor");
1184        }
1185        self.scroll_cursor_to_visible();
1186        true
1187    }
1188
1189    /// Insert a str at the current position.
1190    #[inline]
1191    pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1192        let t = t.as_ref();
1193        if self.has_selection() {
1194            self.value
1195                .remove_str_range(self.value.selection())
1196                .expect("valid_selection");
1197        }
1198        self.value
1199            .insert_str(self.value.cursor(), t)
1200            .expect("valid_cursor");
1201        self.scroll_cursor_to_visible();
1202        true
1203    }
1204
1205    /// Deletes the given range.
1206    #[inline]
1207    pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1208        self.try_delete_range(range).expect("valid_range")
1209    }
1210
1211    /// Deletes the given range.
1212    #[inline]
1213    pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1214        if !range.is_empty() {
1215            self.value
1216                .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1217            self.scroll_cursor_to_visible();
1218            Ok(true)
1219        } else {
1220            Ok(false)
1221        }
1222    }
1223}
1224
1225impl TextInputState {
1226    /// Delete the char after the cursor.
1227    #[inline]
1228    pub fn delete_next_char(&mut self) -> bool {
1229        if self.has_selection() {
1230            self.delete_range(self.selection())
1231        } else {
1232            let pos = self.value.cursor();
1233            let r = remove_next_char(&mut self.value, pos).expect("valid_cursor");
1234            self.scroll_cursor_to_visible();
1235            r
1236        }
1237    }
1238
1239    /// Delete the char before the cursor.
1240    #[inline]
1241    pub fn delete_prev_char(&mut self) -> bool {
1242        if self.value.has_selection() {
1243            self.delete_range(self.selection())
1244        } else {
1245            let pos = self.value.cursor();
1246            let r = remove_prev_char(&mut self.value, pos).expect("valid_cursor");
1247            self.scroll_cursor_to_visible();
1248            r
1249        }
1250    }
1251
1252    /// Find the start of the next word. Word is everything that is not whitespace.
1253    pub fn next_word_start(&self, pos: upos_type) -> upos_type {
1254        self.try_next_word_start(pos).expect("valid_pos")
1255    }
1256
1257    /// Find the start of the next word. Word is everything that is not whitespace.
1258    pub fn try_next_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1259        next_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1260    }
1261
1262    /// Find the end of the next word.  Skips whitespace first, then goes on
1263    /// until it finds the next whitespace.
1264    pub fn next_word_end(&self, pos: upos_type) -> upos_type {
1265        self.try_next_word_end(pos).expect("valid_pos")
1266    }
1267
1268    /// Find the end of the next word.  Skips whitespace first, then goes on
1269    /// until it finds the next whitespace.
1270    pub fn try_next_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1271        next_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1272    }
1273
1274    /// Find prev word. Skips whitespace first.
1275    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1276    /// both return start<=end!
1277    pub fn prev_word_start(&self, pos: upos_type) -> upos_type {
1278        self.try_prev_word_start(pos).expect("valid_pos")
1279    }
1280
1281    /// Find prev word. Skips whitespace first.
1282    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1283    /// both return start<=end!
1284    pub fn try_prev_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1285        prev_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1286    }
1287
1288    /// Find the end of the previous word. Word is everything that is not whitespace.
1289    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1290    /// both return start<=end!
1291    pub fn prev_word_end(&self, pos: upos_type) -> upos_type {
1292        self.try_prev_word_end(pos).expect("valid_pos")
1293    }
1294
1295    /// Find the end of the previous word. Word is everything that is not whitespace.
1296    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1297    /// both return start<=end!
1298    pub fn try_prev_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1299        prev_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1300    }
1301
1302    /// Is the position at a word boundary?
1303    pub fn is_word_boundary(&self, pos: upos_type) -> bool {
1304        self.try_is_word_boundary(pos).expect("valid_pos")
1305    }
1306
1307    /// Is the position at a word boundary?
1308    pub fn try_is_word_boundary(&self, pos: upos_type) -> Result<bool, TextError> {
1309        is_word_boundary(&self.value, TextPosition::new(pos, 0))
1310    }
1311
1312    /// Find the start of the word at pos.
1313    pub fn word_start(&self, pos: upos_type) -> upos_type {
1314        self.try_word_start(pos).expect("valid_pos")
1315    }
1316
1317    /// Find the start of the word at pos.
1318    pub fn try_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1319        word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1320    }
1321
1322    /// Find the end of the word at pos.
1323    pub fn word_end(&self, pos: upos_type) -> upos_type {
1324        self.try_word_end(pos).expect("valid_pos")
1325    }
1326
1327    /// Find the end of the word at pos.
1328    pub fn try_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1329        word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1330    }
1331
1332    /// Deletes the next word.
1333    #[inline]
1334    pub fn delete_next_word(&mut self) -> bool {
1335        if self.has_selection() {
1336            self.delete_range(self.selection())
1337        } else {
1338            let cursor = self.cursor();
1339
1340            let start = self.next_word_start(cursor);
1341            if start != cursor {
1342                self.delete_range(cursor..start)
1343            } else {
1344                let end = self.next_word_end(cursor);
1345                self.delete_range(cursor..end)
1346            }
1347        }
1348    }
1349
1350    /// Deletes the given range.
1351    #[inline]
1352    pub fn delete_prev_word(&mut self) -> bool {
1353        if self.has_selection() {
1354            self.delete_range(self.selection())
1355        } else {
1356            let cursor = self.cursor();
1357
1358            let end = self.prev_word_end(cursor);
1359            if end != cursor {
1360                self.delete_range(end..cursor)
1361            } else {
1362                let start = self.prev_word_start(cursor);
1363                self.delete_range(start..cursor)
1364            }
1365        }
1366    }
1367
1368    /// Move to the next char.
1369    #[inline]
1370    pub fn move_right(&mut self, extend_selection: bool) -> bool {
1371        let c = min(self.cursor() + 1, self.len());
1372        self.set_cursor(c, extend_selection)
1373    }
1374
1375    /// Move to the previous char.
1376    #[inline]
1377    pub fn move_left(&mut self, extend_selection: bool) -> bool {
1378        let c = self.cursor().saturating_sub(1);
1379        self.set_cursor(c, extend_selection)
1380    }
1381
1382    /// Start of line
1383    #[inline]
1384    pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1385        self.set_cursor(0, extend_selection)
1386    }
1387
1388    /// End of line
1389    #[inline]
1390    pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1391        self.set_cursor(self.len(), extend_selection)
1392    }
1393
1394    #[inline]
1395    pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1396        let cursor = self.cursor();
1397        let end = self.next_word_end(cursor);
1398        self.set_cursor(end, extend_selection)
1399    }
1400
1401    #[inline]
1402    pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1403        let cursor = self.cursor();
1404        let start = self.prev_word_start(cursor);
1405        self.set_cursor(start, extend_selection)
1406    }
1407}
1408
1409impl HasScreenCursor for TextInputState {
1410    /// The current text cursor as an absolute screen position.
1411    #[inline]
1412    fn screen_cursor(&self) -> Option<(u16, u16)> {
1413        if self.is_focused() {
1414            if self.has_selection() {
1415                None
1416            } else {
1417                let cx = self.cursor();
1418                let ox = self.offset();
1419
1420                if cx < ox {
1421                    None
1422                } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1423                    None
1424                } else {
1425                    self.col_to_screen(cx)
1426                        .map(|sc| (self.inner.x + sc, self.inner.y))
1427                }
1428            }
1429        } else {
1430            None
1431        }
1432    }
1433}
1434
1435impl RelocatableState for TextInputState {
1436    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1437        self.area.relocate(shift, clip);
1438        self.inner.relocate(shift, clip);
1439        // clip offset for some corrections.
1440        self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1441    }
1442}
1443
1444impl TextInputState {
1445    fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1446        let (text_wrap, left_margin, right_margin, word_margin) = (
1447            TextWrap2::Shift,
1448            self.offset() as upos_type,
1449            self.offset() as upos_type + self.rendered.width as upos_type,
1450            self.offset() as upos_type + self.rendered.width as upos_type,
1451        );
1452        self.value
1453            .glyphs2(
1454                self.rendered,
1455                0,
1456                0..1,
1457                0, /* no tabs */
1458                text_wrap,
1459                false,
1460                left_margin,
1461                right_margin,
1462                word_margin,
1463            )
1464            .expect("valid-row")
1465    }
1466
1467    /// Converts from a widget relative screen coordinate to a grapheme index.
1468    /// x is the relative screen position.
1469    pub fn screen_to_col(&self, scx: i16) -> upos_type {
1470        let ox = self.offset();
1471
1472        let scx = scx + self.dark_offset.0 as i16;
1473
1474        if scx < 0 {
1475            ox.saturating_sub((scx as ipos_type).unsigned_abs())
1476        } else if scx as u16 >= self.rendered.width {
1477            min(ox + scx as upos_type, self.len())
1478        } else {
1479            let scx = scx as u16;
1480
1481            let line = self.glyphs2();
1482
1483            let mut col = ox;
1484            for g in line {
1485                if g.contains_screen_x(scx) {
1486                    break;
1487                }
1488                col = g.pos().x + 1;
1489            }
1490            col
1491        }
1492    }
1493
1494    /// Converts a grapheme based position to a screen position
1495    /// relative to the widget area.
1496    pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1497        let ox = self.offset();
1498
1499        if pos < ox {
1500            return None;
1501        }
1502
1503        let line = self.glyphs2();
1504        let mut screen_x = 0;
1505        for g in line {
1506            if g.pos().x == pos {
1507                break;
1508            }
1509            screen_x = g.screen_pos().0 + g.screen_width();
1510        }
1511
1512        if screen_x >= self.dark_offset.0 {
1513            Some(screen_x - self.dark_offset.0)
1514        } else {
1515            None
1516        }
1517    }
1518
1519    /// Set the cursor position from a screen position relative to the origin
1520    /// of the widget. This value can be negative, which selects a currently
1521    /// not visible position and scrolls to it.
1522    #[inline]
1523    pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1524        let scx = cursor;
1525
1526        let cx = self.screen_to_col(scx);
1527
1528        self.set_cursor(cx, extend_selection)
1529    }
1530
1531    /// Set the cursor position from screen coordinates,
1532    /// rounds the position to the next word start/end.
1533    ///
1534    /// The cursor positions are relative to the inner rect.
1535    /// They may be negative too, this allows setting the cursor
1536    /// to a position that is currently scrolled away.
1537    pub fn set_screen_cursor_words(&mut self, screen_cursor: i16, extend_selection: bool) -> bool {
1538        let anchor = self.anchor();
1539
1540        let cx = self.screen_to_col(screen_cursor);
1541        let cursor = cx;
1542
1543        let cursor = if cursor < anchor {
1544            self.word_start(cursor)
1545        } else {
1546            self.word_end(cursor)
1547        };
1548
1549        // extend anchor
1550        if !self.is_word_boundary(anchor) {
1551            if cursor < anchor {
1552                self.set_cursor(self.word_end(anchor), false);
1553            } else {
1554                self.set_cursor(self.word_start(anchor), false);
1555            }
1556        }
1557
1558        self.set_cursor(cursor, extend_selection)
1559    }
1560
1561    /// Scrolling
1562    pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1563        self.set_offset(self.offset.saturating_sub(delta));
1564        true
1565    }
1566
1567    /// Scrolling
1568    pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1569        self.set_offset(self.offset + delta);
1570        true
1571    }
1572
1573    /// Change the offset in a way that the cursor is visible.
1574    pub fn scroll_cursor_to_visible(&mut self) {
1575        self.scroll_to_cursor.set(true);
1576    }
1577}
1578
1579impl HandleEvent<Event, Regular, TextOutcome> for TextInputState {
1580    fn handle(&mut self, event: &Event, _keymap: Regular) -> TextOutcome {
1581        // small helper ...
1582        fn tc(r: bool) -> TextOutcome {
1583            if r {
1584                TextOutcome::TextChanged
1585            } else {
1586                TextOutcome::Unchanged
1587            }
1588        }
1589        fn overwrite(state: &mut TextInputState) {
1590            if state.overwrite.get() {
1591                state.overwrite.set(false);
1592                state.delete_range(0..state.len());
1593            }
1594        }
1595        fn clear_overwrite(state: &mut TextInputState) {
1596            state.overwrite.set(false);
1597        }
1598
1599        let mut r = if self.is_focused() {
1600            match event {
1601                ct_event!(key press c)
1602                | ct_event!(key press SHIFT-c)
1603                | ct_event!(key press CONTROL_ALT-c) => {
1604                    overwrite(self);
1605                    tc(self.insert_char(*c))
1606                }
1607                ct_event!(keycode press Backspace) | ct_event!(keycode press SHIFT-Backspace) => {
1608                    clear_overwrite(self);
1609                    tc(self.delete_prev_char())
1610                }
1611                ct_event!(keycode press Delete) | ct_event!(keycode press SHIFT-Delete) => {
1612                    clear_overwrite(self);
1613                    tc(self.delete_next_char())
1614                }
1615                ct_event!(keycode press CONTROL-Backspace)
1616                | ct_event!(keycode press ALT-Backspace) => {
1617                    clear_overwrite(self);
1618                    tc(self.delete_prev_word())
1619                }
1620                ct_event!(keycode press CONTROL-Delete) => {
1621                    clear_overwrite(self);
1622                    tc(self.delete_next_word())
1623                }
1624                ct_event!(key press CONTROL-'x') => {
1625                    clear_overwrite(self);
1626                    tc(self.cut_to_clip())
1627                }
1628                ct_event!(key press CONTROL-'v') => {
1629                    overwrite(self);
1630                    tc(self.paste_from_clip())
1631                }
1632                ct_event!(paste v) => {
1633                    overwrite(self);
1634                    tc(self.insert_str(v))
1635                }
1636                ct_event!(key press CONTROL-'d') => {
1637                    clear_overwrite(self);
1638                    tc(self.clear())
1639                }
1640                ct_event!(key press CONTROL-'z') => {
1641                    clear_overwrite(self);
1642                    tc(self.undo())
1643                }
1644                ct_event!(key press CONTROL_SHIFT-'Z') => {
1645                    clear_overwrite(self);
1646                    tc(self.redo())
1647                }
1648
1649                ct_event!(key release _)
1650                | ct_event!(key release SHIFT-_)
1651                | ct_event!(key release CONTROL_ALT-_)
1652                | ct_event!(keycode release Tab)
1653                | ct_event!(keycode release Backspace)
1654                | ct_event!(keycode release Delete)
1655                | ct_event!(keycode release CONTROL-Backspace)
1656                | ct_event!(keycode release ALT-Backspace)
1657                | ct_event!(keycode release CONTROL-Delete)
1658                | ct_event!(key release CONTROL-'x')
1659                | ct_event!(key release CONTROL-'v')
1660                | ct_event!(key release CONTROL-'d')
1661                | ct_event!(key release CONTROL-'y')
1662                | ct_event!(key release CONTROL-'z')
1663                | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1664
1665                _ => TextOutcome::Continue,
1666            }
1667        } else {
1668            TextOutcome::Continue
1669        };
1670        if r == TextOutcome::Continue {
1671            r = self.handle(event, ReadOnly);
1672        }
1673        r
1674    }
1675}
1676
1677impl HandleEvent<Event, ReadOnly, TextOutcome> for TextInputState {
1678    fn handle(&mut self, event: &Event, _keymap: ReadOnly) -> TextOutcome {
1679        fn clear_overwrite(state: &mut TextInputState) {
1680            state.overwrite.set(false);
1681        }
1682
1683        let mut r = if self.is_focused() {
1684            match event {
1685                ct_event!(keycode press Left) => {
1686                    clear_overwrite(self);
1687                    self.move_left(false).into()
1688                }
1689                ct_event!(keycode press Right) => {
1690                    clear_overwrite(self);
1691                    self.move_right(false).into()
1692                }
1693                ct_event!(keycode press CONTROL-Left) => {
1694                    clear_overwrite(self);
1695                    self.move_to_prev_word(false).into()
1696                }
1697                ct_event!(keycode press CONTROL-Right) => {
1698                    clear_overwrite(self);
1699                    self.move_to_next_word(false).into()
1700                }
1701                ct_event!(keycode press Home) => {
1702                    clear_overwrite(self);
1703                    self.move_to_line_start(false).into()
1704                }
1705                ct_event!(keycode press End) => {
1706                    clear_overwrite(self);
1707                    self.move_to_line_end(false).into()
1708                }
1709                ct_event!(keycode press SHIFT-Left) => {
1710                    clear_overwrite(self);
1711                    self.move_left(true).into()
1712                }
1713                ct_event!(keycode press SHIFT-Right) => {
1714                    clear_overwrite(self);
1715                    self.move_right(true).into()
1716                }
1717                ct_event!(keycode press CONTROL_SHIFT-Left) => {
1718                    clear_overwrite(self);
1719                    self.move_to_prev_word(true).into()
1720                }
1721                ct_event!(keycode press CONTROL_SHIFT-Right) => {
1722                    clear_overwrite(self);
1723                    self.move_to_next_word(true).into()
1724                }
1725                ct_event!(keycode press SHIFT-Home) => {
1726                    clear_overwrite(self);
1727                    self.move_to_line_start(true).into()
1728                }
1729                ct_event!(keycode press SHIFT-End) => {
1730                    clear_overwrite(self);
1731                    self.move_to_line_end(true).into()
1732                }
1733                ct_event!(keycode press ALT-Left) => {
1734                    clear_overwrite(self);
1735                    self.scroll_left(1).into()
1736                }
1737                ct_event!(keycode press ALT-Right) => {
1738                    clear_overwrite(self);
1739                    self.scroll_right(1).into()
1740                }
1741                ct_event!(key press CONTROL-'a') => {
1742                    clear_overwrite(self);
1743                    self.select_all().into()
1744                }
1745                ct_event!(key press CONTROL-'c') => {
1746                    clear_overwrite(self);
1747                    self.copy_to_clip().into()
1748                }
1749
1750                ct_event!(keycode release Left)
1751                | ct_event!(keycode release Right)
1752                | ct_event!(keycode release CONTROL-Left)
1753                | ct_event!(keycode release CONTROL-Right)
1754                | ct_event!(keycode release Home)
1755                | ct_event!(keycode release End)
1756                | ct_event!(keycode release SHIFT-Left)
1757                | ct_event!(keycode release SHIFT-Right)
1758                | ct_event!(keycode release CONTROL_SHIFT-Left)
1759                | ct_event!(keycode release CONTROL_SHIFT-Right)
1760                | ct_event!(keycode release SHIFT-Home)
1761                | ct_event!(keycode release SHIFT-End)
1762                | ct_event!(key release CONTROL-'a')
1763                | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1764
1765                _ => TextOutcome::Continue,
1766            }
1767        } else {
1768            TextOutcome::Continue
1769        };
1770
1771        if r == TextOutcome::Continue {
1772            r = self.handle(event, MouseOnly);
1773        }
1774        r
1775    }
1776}
1777
1778impl HandleEvent<Event, MouseOnly, TextOutcome> for TextInputState {
1779    fn handle(&mut self, event: &Event, _keymap: MouseOnly) -> TextOutcome {
1780        if !self.has_mouse_focus() {
1781            return TextOutcome::Continue;
1782        }
1783
1784        fn clear_overwrite(state: &mut TextInputState) {
1785            state.overwrite.set(false);
1786        }
1787
1788        match event {
1789            ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1790                let c = (m.column as i16) - (self.inner.x as i16);
1791                clear_overwrite(self);
1792                self.set_screen_cursor(c, true).into()
1793            }
1794            ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1795                let cx = m.column as i16 - self.inner.x as i16;
1796                clear_overwrite(self);
1797                self.set_screen_cursor_words(cx, true).into()
1798            }
1799            ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1800                let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1801                let start = self.word_start(tx);
1802                let end = self.word_end(tx);
1803                clear_overwrite(self);
1804                self.set_selection(start, end).into()
1805            }
1806            ct_event!(mouse down Left for column,row) => {
1807                if self.gained_focus() {
1808                    // don't react to the first click that's for
1809                    // focus. this one shouldn't demolish the selection.
1810                    TextOutcome::Unchanged
1811                } else if self.inner.contains((*column, *row).into()) {
1812                    let c = (column - self.inner.x) as i16;
1813                    clear_overwrite(self);
1814                    self.set_screen_cursor(c, false).into()
1815                } else {
1816                    TextOutcome::Continue
1817                }
1818            }
1819            ct_event!(mouse down CONTROL-Left for column,row) => {
1820                if self.inner.contains((*column, *row).into()) {
1821                    let cx = (column - self.inner.x) as i16;
1822                    clear_overwrite(self);
1823                    self.set_screen_cursor(cx, true).into()
1824                } else {
1825                    TextOutcome::Continue
1826                }
1827            }
1828            ct_event!(mouse down ALT-Left for column,row) => {
1829                if self.inner.contains((*column, *row).into()) {
1830                    let cx = (column - self.inner.x) as i16;
1831                    clear_overwrite(self);
1832                    self.set_screen_cursor_words(cx, true).into()
1833                } else {
1834                    TextOutcome::Continue
1835                }
1836            }
1837            _ => TextOutcome::Continue,
1838        }
1839    }
1840}
1841
1842/// Handle all events.
1843/// Text events are only processed if focus is true.
1844/// Mouse events are processed if they are in range.
1845pub fn handle_events(state: &mut TextInputState, focus: bool, event: &Event) -> TextOutcome {
1846    state.focus.set(focus);
1847    state.handle(event, Regular)
1848}
1849
1850/// Handle only navigation events.
1851/// Text events are only processed if focus is true.
1852/// Mouse events are processed if they are in range.
1853pub fn handle_readonly_events(
1854    state: &mut TextInputState,
1855    focus: bool,
1856    event: &Event,
1857) -> TextOutcome {
1858    state.focus.set(focus);
1859    state.handle(event, ReadOnly)
1860}
1861
1862/// Handle only mouse-events.
1863pub fn handle_mouse_events(state: &mut TextInputState, event: &Event) -> TextOutcome {
1864    state.handle(event, MouseOnly)
1865}