rat_text/
text_area.rs

1//!
2//! A text-area widget with text-styling abilities.
3//! And undo + clipboard support.
4//!
5
6use crate::_private::NonExhaustive;
7use crate::clipboard::{global_clipboard, Clipboard};
8use crate::event::{ReadOnly, TextOutcome};
9use crate::grapheme::{Glyph, Grapheme};
10use crate::text_core::TextCore;
11use crate::text_store::text_rope::TextRope;
12use crate::text_store::TextStore;
13use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
14use crate::{
15    ipos_type, upos_type, Cursor, HasScreenCursor, TextError, TextPosition, TextRange, TextStyle,
16};
17use crossterm::event::KeyModifiers;
18use rat_event::util::MouseFlags;
19use rat_event::{ct_event, flow, HandleEvent, MouseOnly, Regular};
20use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
21use rat_reloc::{relocate_area, relocate_dark_offset, RelocatableState};
22use rat_scrolled::event::ScrollOutcome;
23use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState};
24use ratatui::buffer::Buffer;
25use ratatui::layout::Rect;
26use ratatui::style::{Style, Stylize};
27use ratatui::widgets::{Block, StatefulWidget};
28use ropey::Rope;
29use std::borrow::Cow;
30use std::cmp::{max, min};
31use std::collections::HashMap;
32use std::ops::Range;
33
34/// Text area widget.
35///
36/// Backend used is [ropey](https://docs.rs/ropey/latest/ropey/), so large
37/// texts are no problem. Editing time increases with the number of
38/// styles applied. Everything below a million styles should be fine.
39///
40/// For emoji support this uses
41/// [unicode_display_width](https://docs.rs/unicode-display-width/latest/unicode_display_width/index.html)
42/// which helps with those double-width emojis. Input of emojis
43/// strongly depends on the terminal. It may or may not work.
44/// And even with display there are sometimes strange glitches
45/// that I haven't found yet.
46///
47/// Keyboard and mouse are implemented for crossterm, but it should be
48/// easy to extend to other event-types. Every interaction is available
49/// as function on the state.
50///
51/// Scrolling doesn't depend on the cursor, but the editing and move
52/// functions take care that the cursor stays visible.
53///
54/// Wordwrap is not available. For display only use
55/// [Paragraph](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Paragraph.html), as
56/// for editing: why?
57///
58/// You can directly access the underlying Rope for readonly purposes, and
59/// conversion from/to byte/char positions are available. That should probably be
60/// enough to write a parser that generates some styling.
61///
62/// The cursor must set externally on the ratatui Frame as usual.
63/// [screen_cursor](TextAreaState::screen_cursor) gives you the correct value.
64/// There is the inverse too [set_screen_cursor](TextAreaState::set_screen_cursor)
65/// For more interactions you can use [screen_to_col](TextAreaState::screen_to_col),
66/// and [try_col_to_screen](TextAreaState::try_col_to_screen). They calculate everything,
67/// even in the presence of more complex graphemes and those double-width emojis.
68///
69/// # Stateful
70/// This widget implements [`StatefulWidget`], you can use it with
71/// [`TextAreaState`] to handle common actions.
72#[derive(Debug, Default, Clone)]
73pub struct TextArea<'a> {
74    block: Option<Block<'a>>,
75    hscroll: Option<Scroll<'a>>,
76    h_max_offset: Option<usize>,
77    h_overscroll: Option<usize>,
78    vscroll: Option<Scroll<'a>>,
79
80    style: Style,
81    focus_style: Option<Style>,
82    select_style: Option<Style>,
83    text_style: HashMap<usize, Style>,
84}
85
86/// State & event handling.
87#[derive(Debug)]
88pub struct TextAreaState {
89    /// The whole area with block.
90    /// __read only__ renewed with each render.
91    pub area: Rect,
92    /// Area inside a possible block.
93    /// __read only__ renewed with each render.
94    pub inner: Rect,
95
96    /// Horizontal scroll
97    /// __read+write__
98    pub hscroll: ScrollState,
99    /// Vertical offset
100    /// __read+write__
101    pub vscroll: ScrollState,
102    /// Dark offset due to clipping.
103    /// __read only__ secondary offset due to clipping.
104    pub dark_offset: (u16, u16),
105    /// The scroll offset will be adjusted to display
106    /// the cursor. This will be the minimal adjustment,
107    /// the cursor will stay at the same screen position if
108    /// it's already visible or appear at the start/end if it's not.
109    /// __read+write__ use scroll_cursor_to_visible().
110    pub scroll_to_cursor: bool,
111
112    /// Text edit core
113    pub value: TextCore<TextRope>,
114
115    /// movement column
116    pub move_col: Option<upos_type>,
117    /// auto indent active
118    pub auto_indent: bool,
119    /// quote selection active
120    pub auto_quote: bool,
121
122    /// Current focus state.
123    pub focus: FocusFlag,
124
125    /// Mouse selection in progress.
126    /// __read+write__
127    pub mouse: MouseFlags,
128
129    pub non_exhaustive: NonExhaustive,
130}
131
132impl Clone for TextAreaState {
133    fn clone(&self) -> Self {
134        Self {
135            focus: FocusFlag::named(self.focus.name()),
136            area: self.area,
137            inner: self.inner,
138            value: self.value.clone(),
139            hscroll: self.hscroll.clone(),
140            vscroll: self.vscroll.clone(),
141            move_col: None,
142            auto_indent: self.auto_indent,
143            auto_quote: self.auto_quote,
144            mouse: Default::default(),
145            dark_offset: self.dark_offset,
146            scroll_to_cursor: self.scroll_to_cursor,
147            non_exhaustive: NonExhaustive,
148        }
149    }
150}
151
152impl<'a> TextArea<'a> {
153    /// New widget.
154    pub fn new() -> Self {
155        Self::default()
156    }
157
158    /// Set the combined style.
159    #[inline]
160    pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
161        if let Some(styles) = styles {
162            self.styles(styles)
163        } else {
164            self
165        }
166    }
167
168    /// Set the combined style.
169    #[inline]
170    pub fn styles(mut self, styles: TextStyle) -> Self {
171        self.style = styles.style;
172        if styles.focus.is_some() {
173            self.focus_style = styles.focus;
174        }
175        if styles.select.is_some() {
176            self.select_style = styles.select;
177        }
178        if let Some(border_style) = styles.border_style {
179            self.block = self.block.map(|v| v.border_style(border_style));
180        }
181        self.block = self.block.map(|v| v.style(self.style));
182        if styles.block.is_some() {
183            self.block = styles.block;
184        }
185        if let Some(styles) = styles.scroll {
186            self.hscroll = self.hscroll.map(|v| v.styles(styles.clone()));
187            self.vscroll = self.vscroll.map(|v| v.styles(styles));
188        }
189        self
190    }
191
192    /// Base style.
193    pub fn style(mut self, style: Style) -> Self {
194        self.style = style;
195        self
196    }
197
198    /// Style when focused.
199    pub fn focus_style(mut self, style: Style) -> Self {
200        self.focus_style = Some(style);
201        self.block = self.block.map(|v| v.style(self.style));
202        self
203    }
204
205    /// Selection style.
206    pub fn select_style(mut self, style: Style) -> Self {
207        self.select_style = Some(style);
208        self
209    }
210
211    /// List of text-styles.
212    ///
213    /// Use [TextAreaState::add_style()] to refer a text range to
214    /// one of these styles.
215    pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
216        for (i, s) in styles.into_iter().enumerate() {
217            self.text_style.insert(i, s);
218        }
219        self
220    }
221
222    /// Map of style_id -> text_style.
223    ///
224    /// Use [TextAreaState::add_style()] to refer a text range to
225    /// one of these styles.
226    pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
227        for (i, s) in styles.into_iter() {
228            self.text_style.insert(i, s.into());
229        }
230        self
231    }
232
233    /// Block.
234    #[inline]
235    pub fn block(mut self, block: Block<'a>) -> Self {
236        self.block = Some(block);
237        self
238    }
239
240    /// Set both scrollbars.
241    pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
242        self.hscroll = Some(scroll.clone().override_horizontal());
243        self.vscroll = Some(scroll.override_vertical());
244        self
245    }
246
247    /// Set the horizontal scrollbar.
248    pub fn hscroll(mut self, scroll: Scroll<'a>) -> Self {
249        self.hscroll = Some(scroll.override_horizontal());
250        self
251    }
252
253    /// Set a maximum horizontal offset that will be used even
254    /// if there is no horizontal scrollbar set.
255    ///
256    /// This widget doesn't try to find a maximum text-length for
257    /// all lines.
258    ///
259    /// Default is 255
260    pub fn set_horizontal_max_offset(mut self, offset: usize) -> Self {
261        self.h_max_offset = Some(offset);
262        self
263    }
264
265    /// Set a horizontal overscroll that will be used even if
266    /// there is no horizontal scrollbar set.
267    ///
268    /// Default is 16384
269    pub fn set_horizontal_overscroll(mut self, overscroll: usize) -> Self {
270        self.h_overscroll = Some(overscroll);
271        self
272    }
273
274    /// Set the vertical scrollbar.
275    pub fn vscroll(mut self, scroll: Scroll<'a>) -> Self {
276        self.vscroll = Some(scroll.override_vertical());
277        self
278    }
279}
280
281impl<'a> StatefulWidget for &TextArea<'a> {
282    type State = TextAreaState;
283
284    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
285        render_text_area(self, area, buf, state);
286    }
287}
288
289impl StatefulWidget for TextArea<'_> {
290    type State = TextAreaState;
291
292    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
293        render_text_area(&self, area, buf, state);
294    }
295}
296
297fn render_text_area(
298    widget: &TextArea<'_>,
299    area: Rect,
300    buf: &mut Buffer,
301    state: &mut TextAreaState,
302) {
303    state.area = area;
304
305    let style = widget.style;
306    let select_style = if let Some(select_style) = widget.select_style {
307        // if the select_style has no fg/bg don't patch
308        if select_style.fg.is_none() && select_style.bg.is_none() {
309            select_style
310        } else {
311            style.patch(select_style)
312        }
313    } else {
314        Style::default().black().on_yellow()
315    };
316
317    let sa = ScrollArea::new()
318        .block(widget.block.as_ref())
319        .h_scroll(widget.hscroll.as_ref())
320        .v_scroll(widget.vscroll.as_ref())
321        .style(style);
322
323    state.inner = sa.inner(area, Some(&state.hscroll), Some(&state.vscroll));
324
325    if let Some(h_max_offset) = widget.h_max_offset {
326        state.hscroll.set_max_offset(h_max_offset);
327    }
328    if let Some(h_overscroll) = widget.h_overscroll {
329        state.hscroll.set_overscroll_by(Some(h_overscroll));
330    }
331    state.hscroll.set_page_len(state.inner.width as usize);
332    state.vscroll.set_max_offset(
333        state
334            .len_lines()
335            .saturating_sub(state.inner.height as upos_type) as usize,
336    );
337    state.vscroll.set_page_len(state.inner.height as usize);
338
339    if state.scroll_to_cursor {
340        let cursor = state.cursor();
341        let (ox, oy) = state.offset();
342        let (ox, oy) = (ox as upos_type, oy as upos_type);
343        let mut noy = if cursor.y < oy {
344            cursor.y
345        } else if cursor.y >= oy + (state.inner.height + state.dark_offset.1) as upos_type {
346            cursor
347                .y
348                .saturating_sub((state.inner.height + state.dark_offset.1) as upos_type)
349        } else {
350            oy
351        };
352        // correct by one at bottom margin. block cursors appear as part of the
353        // bottom border otherwise.
354        if cursor.y == noy + (state.inner.height + state.dark_offset.1) as upos_type {
355            noy = noy.saturating_add(1);
356        }
357
358        let mut nox = if cursor.x < ox {
359            cursor.x
360        } else if cursor.x >= ox + (state.inner.width + state.dark_offset.0) as upos_type {
361            cursor
362                .x
363                .saturating_sub((state.inner.width + state.dark_offset.0) as upos_type)
364        } else {
365            ox
366        };
367        // correct by one at right margin. block cursors appear as part of the
368        // right border otherwise.
369        if cursor.x == nox + (state.inner.width + state.dark_offset.0) as upos_type {
370            nox = nox.saturating_add(1);
371        }
372
373        state.set_offset((nox as usize, noy as usize));
374    }
375
376    let inner = state.inner;
377
378    // set base style
379    sa.render(
380        area,
381        buf,
382        &mut ScrollAreaState::new()
383            .h_scroll(&mut state.hscroll)
384            .v_scroll(&mut state.vscroll),
385    );
386
387    if inner.width == 0 || inner.height == 0 {
388        // noop
389        return;
390    }
391
392    if state.vscroll.offset() > state.value.len_lines() as usize {
393        return;
394    }
395
396    let (ox, oy) = state.offset();
397    let page_rows = (oy as upos_type)
398        ..min(
399            oy as upos_type + inner.height as upos_type,
400            state.value.len_lines(),
401        );
402    let page_bytes = state
403        .try_bytes_at_range(TextRange::new((0, page_rows.start), (0, page_rows.end)))
404        .expect("valid_rows");
405    let selection = state.selection();
406    let mut styles = Vec::new();
407
408    let glyph_iter = state
409        .value
410        .glyphs(page_rows.clone(), ox as u16, inner.width)
411        .expect("valid_offset");
412
413    for g in glyph_iter {
414        if g.screen_width() > 0 {
415            let mut style = style;
416            // text-styles
417            styles.clear();
418            state
419                .value
420                .styles_at_page(page_bytes.clone(), g.text_bytes().start, &mut styles);
421            for style_nr in &styles {
422                if let Some(s) = widget.text_style.get(style_nr) {
423                    style = style.patch(*s);
424                }
425            }
426            // selection
427            if selection.contains_pos(g.pos()) {
428                style = style.patch(select_style);
429            };
430
431            // relative screen-pos of the glyph
432            let screen_pos = g.screen_pos();
433
434            // render glyph
435            if let Some(cell) = buf.cell_mut((inner.x + screen_pos.0, inner.y + screen_pos.1)) {
436                cell.set_symbol(g.glyph());
437                cell.set_style(style);
438            }
439            // clear the reset of the cells to avoid interferences.
440            for d in 1..g.screen_width() {
441                if let Some(cell) =
442                    buf.cell_mut((inner.x + screen_pos.0 + d, inner.y + screen_pos.1))
443                {
444                    cell.reset();
445                    cell.set_style(style);
446                }
447            }
448        }
449    }
450}
451
452impl Default for TextAreaState {
453    fn default() -> Self {
454        let mut s = Self {
455            focus: Default::default(),
456            area: Default::default(),
457            inner: Default::default(),
458            mouse: Default::default(),
459            value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
460            hscroll: Default::default(),
461            non_exhaustive: NonExhaustive,
462            vscroll: Default::default(),
463            move_col: Default::default(),
464            auto_indent: true,
465            auto_quote: true,
466            dark_offset: Default::default(),
467            scroll_to_cursor: Default::default(),
468        };
469        s.hscroll.set_max_offset(255);
470        s.hscroll.set_overscroll_by(Some(16384));
471        s
472    }
473}
474
475impl HasFocus for TextAreaState {
476    fn build(&self, builder: &mut FocusBuilder) {
477        builder.leaf_widget(self);
478    }
479
480    fn focus(&self) -> FocusFlag {
481        self.focus.clone()
482    }
483
484    fn area(&self) -> Rect {
485        self.area
486    }
487
488    fn navigable(&self) -> Navigation {
489        Navigation::Reach
490    }
491}
492
493impl TextAreaState {
494    /// New State.
495    #[inline]
496    pub fn new() -> Self {
497        Self::default()
498    }
499
500    /// New state with a focus name.
501    #[inline]
502    pub fn named(name: &str) -> Self {
503        Self {
504            focus: FocusFlag::named(name),
505            ..Default::default()
506        }
507    }
508
509    /// Sets the line ending used for insert.
510    /// There is no auto-detection or conversion done for set_value().
511    ///
512    /// Caution: If this doesn't match the line ending used in the value, you
513    /// will get a value with mixed line endings.
514    #[inline]
515    pub fn set_newline(&mut self, br: impl Into<String>) {
516        self.value.set_newline(br.into());
517    }
518
519    /// Line ending used for insert.
520    #[inline]
521    pub fn newline(&self) -> &str {
522        self.value.newline()
523    }
524
525    /// Sets auto-indent on new-line.
526    #[inline]
527    pub fn set_auto_indent(&mut self, indent: bool) {
528        self.auto_indent = indent;
529    }
530
531    /// Activates 'add quotes to selection'.
532    #[inline]
533    pub fn set_auto_quote(&mut self, quote: bool) {
534        self.auto_quote = quote;
535    }
536
537    /// Set tab-width.
538    #[inline]
539    pub fn set_tab_width(&mut self, tabs: u16) {
540        self.value.set_tab_width(tabs);
541    }
542
543    /// Tab-width
544    #[inline]
545    pub fn tab_width(&self) -> u16 {
546        self.value.tab_width()
547    }
548
549    /// Expand tabs to spaces. Only for new inputs.
550    #[inline]
551    pub fn set_expand_tabs(&mut self, expand: bool) {
552        self.value.set_expand_tabs(expand);
553    }
554
555    /// Expand tabs to spaces. Only for new inputs.
556    #[inline]
557    pub fn expand_tabs(&self) -> bool {
558        self.value.expand_tabs()
559    }
560
561    /// Show control characters.
562    #[inline]
563    pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
564        self.value.set_glyph_ctrl(show_ctrl);
565    }
566
567    /// Show control characters.
568    pub fn show_ctrl(&self) -> bool {
569        self.value.glyph_ctrl()
570    }
571
572    /// Extra column information for cursor movement.
573    ///
574    /// The cursor position is capped to the current line length, so if you
575    /// move up one row, you might end at a position left of the current column.
576    /// If you move up once more you want to return to the original position.
577    /// That's what is stored here.
578    #[inline]
579    pub fn set_move_col(&mut self, col: Option<upos_type>) {
580        self.move_col = col;
581    }
582
583    /// Extra column information for cursor movement.
584    #[inline]
585    pub fn move_col(&mut self) -> Option<upos_type> {
586        self.move_col
587    }
588}
589
590impl TextAreaState {
591    /// Clipboard used.
592    /// Default is to use the global_clipboard().
593    #[inline]
594    pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
595        match clip {
596            None => self.value.set_clipboard(None),
597            Some(v) => self.value.set_clipboard(Some(Box::new(v))),
598        }
599    }
600
601    /// Clipboard used.
602    /// Default is to use the global_clipboard().
603    #[inline]
604    pub fn clipboard(&self) -> Option<&dyn Clipboard> {
605        self.value.clipboard()
606    }
607
608    /// Copy to internal buffer
609    #[inline]
610    pub fn copy_to_clip(&mut self) -> bool {
611        let Some(clip) = self.value.clipboard() else {
612            return false;
613        };
614
615        _ = clip.set_string(self.selected_text().as_ref());
616        false
617    }
618
619    /// Cut to internal buffer
620    #[inline]
621    pub fn cut_to_clip(&mut self) -> bool {
622        let Some(clip) = self.value.clipboard() else {
623            return false;
624        };
625
626        match clip.set_string(self.selected_text().as_ref()) {
627            Ok(_) => self.delete_range(self.selection()),
628            Err(_) => false,
629        }
630    }
631
632    /// Paste from internal buffer.
633    #[inline]
634    pub fn paste_from_clip(&mut self) -> bool {
635        let Some(clip) = self.value.clipboard() else {
636            return false;
637        };
638
639        if let Ok(text) = clip.get_string() {
640            self.insert_str(text)
641        } else {
642            false
643        }
644    }
645}
646
647impl TextAreaState {
648    /// Set undo buffer.
649    #[inline]
650    pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
651        match undo {
652            None => self.value.set_undo_buffer(None),
653            Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
654        }
655    }
656
657    /// Undo
658    #[inline]
659    pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
660        self.value.undo_buffer()
661    }
662
663    /// Undo
664    #[inline]
665    pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
666        self.value.undo_buffer_mut()
667    }
668
669    /// Begin a sequence of changes that should be undone in one go.
670    #[inline]
671    pub fn begin_undo_seq(&mut self) {
672        self.value.begin_undo_seq()
673    }
674
675    /// End a sequence of changes that should be undone in one go.
676    #[inline]
677    pub fn end_undo_seq(&mut self) {
678        self.value.end_undo_seq()
679    }
680
681    /// Get all recent replay recordings.
682    #[inline]
683    pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
684        self.value.recent_replay_log()
685    }
686
687    /// Apply the replay recording.
688    #[inline]
689    pub fn replay_log(&mut self, replay: &[UndoEntry]) {
690        self.value.replay_log(replay)
691    }
692
693    /// Undo operation
694    #[inline]
695    pub fn undo(&mut self) -> bool {
696        self.value.undo()
697    }
698
699    /// Redo operation
700    #[inline]
701    pub fn redo(&mut self) -> bool {
702        self.value.redo()
703    }
704}
705
706impl TextAreaState {
707    /// Set and replace all styles.
708    ///
709    /// The ranges are byte-ranges into the text.
710    /// Each byte-range maps to an index into the styles set
711    /// with the widget.
712    ///
713    /// Any style-idx that don't have a match there are just
714    /// ignored. You can use this to store other range based information.
715    /// The ranges are corrected during edits, no need to recalculate
716    /// everything after each keystroke.
717    #[inline]
718    pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
719        self.value.set_styles(styles);
720    }
721
722    /// Add a style for a [TextRange].
723    ///
724    /// The style-idx refers to one of the styles set with the widget.
725    /// Missing styles are just ignored.
726    #[inline]
727    pub fn add_style(&mut self, range: Range<usize>, style: usize) {
728        self.value.add_style(range, style);
729    }
730
731    /// Add a style for a [TextRange]. The style-nr refers to one
732    /// of the styles set with the widget.
733    /// Missing styles are just ignored.
734    #[inline]
735    pub fn add_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
736        let r = self.value.bytes_at_range(range)?;
737        self.value.add_style(r, style);
738        Ok(())
739    }
740
741    /// Remove the exact TextRange and style.
742    #[inline]
743    pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
744        self.value.remove_style(range, style);
745    }
746
747    /// Remove the exact TextRange and style.
748    #[inline]
749    pub fn remove_range_style(&mut self, range: TextRange, style: usize) -> Result<(), TextError> {
750        let r = self.value.bytes_at_range(range)?;
751        self.value.remove_style(r, style);
752        Ok(())
753    }
754
755    /// Find all styles that touch the given range.
756    pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
757        self.value.styles_in(range, buf)
758    }
759
760    /// All styles active at the given position.
761    #[inline]
762    pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
763        self.value.styles_at(byte_pos, buf)
764    }
765
766    /// Check if the given style applies at the position and
767    /// return the complete range for the style.
768    #[inline]
769    pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
770        self.value.style_match(byte_pos, style)
771    }
772
773    /// List of all styles.
774    #[inline]
775    pub fn styles(&self) -> impl Iterator<Item = (Range<usize>, usize)> + '_ {
776        self.value.styles().expect("styles")
777    }
778}
779
780impl TextAreaState {
781    /// Current offset for scrolling.
782    #[inline]
783    pub fn offset(&self) -> (usize, usize) {
784        (self.hscroll.offset(), self.vscroll.offset())
785    }
786
787    /// Set the offset for scrolling.
788    #[inline]
789    pub fn set_offset(&mut self, offset: (usize, usize)) -> bool {
790        self.scroll_to_cursor = false;
791        let c = self.hscroll.set_offset(offset.0);
792        let r = self.vscroll.set_offset(offset.1);
793        r || c
794    }
795
796    /// Cursor position.
797    #[inline]
798    pub fn cursor(&self) -> TextPosition {
799        self.value.cursor()
800    }
801
802    /// Set the cursor position and scroll the cursor to a visible offset.
803    #[inline]
804    pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
805        self.scroll_cursor_to_visible();
806        self.value.set_cursor(cursor.into(), extend_selection)
807    }
808
809    /// Selection anchor.
810    #[inline]
811    pub fn anchor(&self) -> TextPosition {
812        self.value.anchor()
813    }
814
815    /// Has a selection?
816    #[inline]
817    pub fn has_selection(&self) -> bool {
818        self.value.has_selection()
819    }
820
821    /// Current selection.
822    #[inline]
823    pub fn selection(&self) -> TextRange {
824        self.value.selection()
825    }
826
827    /// Set the selection.
828    /// Scrolls the cursor to a visible position.
829    #[inline]
830    pub fn set_selection(
831        &mut self,
832        anchor: impl Into<TextPosition>,
833        cursor: impl Into<TextPosition>,
834    ) -> bool {
835        self.scroll_cursor_to_visible();
836        self.value.set_selection(anchor.into(), cursor.into())
837    }
838
839    /// Select all.
840    /// Scrolls the cursor to a visible position.
841    #[inline]
842    pub fn select_all(&mut self) -> bool {
843        self.scroll_cursor_to_visible();
844        self.value.select_all()
845    }
846
847    /// Selection.
848    #[inline]
849    pub fn selected_text(&self) -> Cow<'_, str> {
850        self.value
851            .str_slice(self.value.selection())
852            .expect("valid_selection")
853    }
854}
855
856impl TextAreaState {
857    /// Empty.
858    #[inline]
859    pub fn is_empty(&self) -> bool {
860        self.value.is_empty()
861    }
862
863    /// Borrow the rope
864    #[inline]
865    pub fn rope(&self) -> &Rope {
866        self.value.text().rope()
867    }
868
869    /// Text value
870    #[inline]
871    pub fn text(&self) -> String {
872        self.value.text().string()
873    }
874
875    /// Text slice as `Cow<str>`. Uses a byte range.
876    #[inline]
877    pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
878        self.value.str_slice_byte(range).expect("valid_range")
879    }
880
881    /// Text slice as `Cow<str>`. Uses a byte range.
882    #[inline]
883    pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
884        self.value.str_slice_byte(range)
885    }
886
887    /// Text slice as `Cow<str>`
888    #[inline]
889    pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
890        self.value.str_slice(range.into()).expect("valid_range")
891    }
892
893    /// Text slice as `Cow<str>`
894    #[inline]
895    pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
896        self.value.str_slice(range.into())
897    }
898
899    /// Line count.
900    #[inline]
901    pub fn len_lines(&self) -> upos_type {
902        self.value.len_lines()
903    }
904
905    /// Line width as grapheme count.
906    #[inline]
907    pub fn line_width(&self, row: upos_type) -> upos_type {
908        self.value.line_width(row).expect("valid_row")
909    }
910
911    /// Line width as grapheme count.
912    #[inline]
913    pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
914        self.value.line_width(row)
915    }
916
917    /// Line as RopeSlice.
918    /// This contains the \n at the end.
919    #[inline]
920    pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
921        self.value.line_at(row).expect("valid_row")
922    }
923
924    /// Line as RopeSlice.
925    /// This contains the \n at the end.
926    #[inline]
927    pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
928        self.value.line_at(row)
929    }
930
931    /// Iterate over text-lines, starting at offset.
932    #[inline]
933    pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
934        self.value.lines_at(row).expect("valid_row")
935    }
936
937    /// Iterate over text-lines, starting at offset.
938    #[inline]
939    pub fn try_lines_at(
940        &self,
941        row: upos_type,
942    ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
943        self.value.lines_at(row)
944    }
945
946    // Iterator for the glyphs of the lines in range.
947    /// Glyphs here a grapheme + display length.
948    #[inline]
949    pub fn glyphs(
950        &self,
951        rows: Range<upos_type>,
952        screen_offset: u16,
953        screen_width: u16,
954    ) -> impl Iterator<Item = Glyph<'_>> {
955        self.value
956            .glyphs(rows, screen_offset, screen_width)
957            .expect("valid_rows")
958    }
959
960    // Iterator for the glyphs of the lines in range.
961    /// Glyphs here a grapheme + display length.
962    #[inline]
963    pub fn try_glyphs(
964        &self,
965        rows: Range<upos_type>,
966        screen_offset: u16,
967        screen_width: u16,
968    ) -> Result<impl Iterator<Item = Glyph<'_>>, TextError> {
969        self.value.glyphs(rows, screen_offset, screen_width)
970    }
971
972    /// Grapheme iterator for a given line.
973    /// This contains the \n at the end.
974    #[inline]
975    pub fn line_graphemes(&self, row: upos_type) -> impl Iterator<Item = Grapheme<'_>> {
976        self.value.line_graphemes(row).expect("valid_row")
977    }
978
979    /// Grapheme iterator for a given line.
980    /// This contains the \n at the end.
981    #[inline]
982    pub fn try_line_graphemes(
983        &self,
984        row: upos_type,
985    ) -> Result<impl Iterator<Item = Grapheme<'_>>, TextError> {
986        self.value.line_graphemes(row)
987    }
988
989    /// Get a cursor over all the text with the current position set at pos.
990    #[inline]
991    pub fn text_graphemes(&self, pos: TextPosition) -> impl Cursor<Item = Grapheme<'_>> {
992        self.value.text_graphemes(pos).expect("valid_pos")
993    }
994
995    /// Get a cursor over all the text with the current position set at pos.
996    #[inline]
997    pub fn try_text_graphemes(
998        &self,
999        pos: TextPosition,
1000    ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1001        self.value.text_graphemes(pos)
1002    }
1003
1004    /// Get a cursor over the text-range the current position set at pos.
1005    #[inline]
1006    pub fn graphemes(
1007        &self,
1008        range: TextRange,
1009        pos: TextPosition,
1010    ) -> impl Cursor<Item = Grapheme<'_>> {
1011        self.value.graphemes(range, pos).expect("valid_args")
1012    }
1013
1014    /// Get a cursor over the text-range the current position set at pos.
1015    #[inline]
1016    pub fn try_graphemes(
1017        &self,
1018        range: TextRange,
1019        pos: TextPosition,
1020    ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1021        self.value.graphemes(range, pos)
1022    }
1023
1024    /// Grapheme position to byte position.
1025    /// This is the (start,end) position of the single grapheme after pos.
1026    #[inline]
1027    pub fn byte_at(&self, pos: TextPosition) -> Range<usize> {
1028        self.value.byte_at(pos).expect("valid_pos")
1029    }
1030
1031    /// Grapheme position to byte position.
1032    /// This is the (start,end) position of the single grapheme after pos.
1033    #[inline]
1034    pub fn try_byte_at(&self, pos: TextPosition) -> Result<Range<usize>, TextError> {
1035        self.value.byte_at(pos)
1036    }
1037
1038    /// Grapheme range to byte range.
1039    #[inline]
1040    pub fn try_bytes_at_range(&self, range: TextRange) -> Result<Range<usize>, TextError> {
1041        self.value.bytes_at_range(range)
1042    }
1043
1044    /// Grapheme range to byte range.
1045    #[inline]
1046    pub fn bytes_at_range(&self, range: TextRange) -> Range<usize> {
1047        self.value.bytes_at_range(range).expect("valid_range")
1048    }
1049
1050    /// Byte position to grapheme position.
1051    /// Returns the position that contains the given byte index.
1052    #[inline]
1053    pub fn byte_pos(&self, byte: usize) -> TextPosition {
1054        self.value.byte_pos(byte).expect("valid_pos")
1055    }
1056
1057    /// Byte position to grapheme position.
1058    /// Returns the position that contains the given byte index.
1059    #[inline]
1060    pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1061        self.value.byte_pos(byte)
1062    }
1063
1064    /// Byte range to grapheme range.
1065    #[inline]
1066    pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1067        self.value.byte_range(bytes).expect("valid_range")
1068    }
1069
1070    /// Byte range to grapheme range.
1071    #[inline]
1072    pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1073        self.value.byte_range(bytes)
1074    }
1075}
1076
1077impl TextAreaState {
1078    /// Clear everything.
1079    #[inline]
1080    pub fn clear(&mut self) -> bool {
1081        if !self.is_empty() {
1082            self.value.clear();
1083            true
1084        } else {
1085            false
1086        }
1087    }
1088
1089    /// Set the text value.
1090    /// Resets all internal state.
1091    #[inline]
1092    pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1093        self.scroll_to_cursor = false;
1094        self.vscroll.set_offset(0);
1095        self.hscroll.set_offset(0);
1096
1097        self.value.set_text(TextRope::new_text(s.as_ref()));
1098    }
1099
1100    /// Set the text value as a Rope.
1101    /// Resets all internal state.
1102    #[inline]
1103    pub fn set_rope(&mut self, r: Rope) {
1104        self.scroll_to_cursor = false;
1105        self.vscroll.set_offset(0);
1106        self.hscroll.set_offset(0);
1107
1108        self.value.set_text(TextRope::new_rope(r));
1109    }
1110
1111    /// Insert a character at the cursor position.
1112    /// Removes the selection and inserts the char.
1113    ///
1114    /// This insert makes no special actions when encountering
1115    /// a new-line or tab. Use insert_newline and insert_tab for
1116    /// this.
1117    pub fn insert_char(&mut self, c: char) -> bool {
1118        let mut insert = true;
1119        if self.has_selection() {
1120            if self.auto_quote
1121                && (c == '\''
1122                    || c == '"'
1123                    || c == '`'
1124                    || c == '<'
1125                    || c == '['
1126                    || c == '('
1127                    || c == '{')
1128            {
1129                self.value
1130                    .insert_quotes(self.selection(), c)
1131                    .expect("valid_selection");
1132                insert = false;
1133            } else {
1134                self.value
1135                    .remove_str_range(self.selection())
1136                    .expect("valid_selection");
1137            }
1138        }
1139
1140        if insert {
1141            if c == '\n' {
1142                self.value
1143                    .insert_newline(self.cursor())
1144                    .expect("valid_cursor");
1145            } else if c == '\t' {
1146                self.value.insert_tab(self.cursor()).expect("valid_cursor");
1147            } else {
1148                self.value
1149                    .insert_char(self.cursor(), c)
1150                    .expect("valid_cursor");
1151            }
1152        }
1153
1154        self.scroll_cursor_to_visible();
1155
1156        true
1157    }
1158
1159    /// Inserts tab at the current position. This respects the
1160    /// tab-width set.
1161    ///
1162    /// If there is a text-selection the text-rows will be indented instead.
1163    /// This can be deactivated with auto_indent=false.
1164    pub fn insert_tab(&mut self) -> bool {
1165        if self.has_selection() {
1166            if self.auto_indent {
1167                let sel = self.selection();
1168                let indent = " ".repeat(self.tab_width() as usize);
1169
1170                self.value.begin_undo_seq();
1171                for r in sel.start.y..=sel.end.y {
1172                    self.value
1173                        .insert_str(TextPosition::new(0, r), &indent)
1174                        .expect("valid_row");
1175                }
1176                self.value.end_undo_seq();
1177
1178                true
1179            } else {
1180                false
1181            }
1182        } else {
1183            self.value.insert_tab(self.cursor()).expect("valid_cursor");
1184            self.scroll_cursor_to_visible();
1185
1186            true
1187        }
1188    }
1189
1190    /// Unindents the selected text by tab-width. If there is no
1191    /// selection this does nothing.
1192    ///
1193    /// This can be deactivated with auto_indent=false.
1194    pub fn insert_backtab(&mut self) -> bool {
1195        let sel = self.selection();
1196
1197        self.value.begin_undo_seq();
1198        for r in sel.start.y..=sel.end.y {
1199            let mut idx = 0;
1200            let g_it = self
1201                .value
1202                .graphemes(TextRange::new((0, r), (0, r + 1)), TextPosition::new(0, r))
1203                .expect("valid_range")
1204                .take(self.tab_width() as usize);
1205            for g in g_it {
1206                if g != " " && g != "\t" {
1207                    break;
1208                }
1209                idx += 1;
1210            }
1211
1212            self.value
1213                .remove_str_range(TextRange::new((0, r), (idx, r)))
1214                .expect("valid_range");
1215        }
1216        self.value.end_undo_seq();
1217
1218        true
1219    }
1220
1221    /// Insert text at the cursor position.
1222    /// Removes the selection and inserts the text.
1223    pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1224        let t = t.as_ref();
1225        if self.has_selection() {
1226            self.value
1227                .remove_str_range(self.selection())
1228                .expect("valid_selection");
1229        }
1230        self.value
1231            .insert_str(self.cursor(), t)
1232            .expect("valid_cursor");
1233        self.scroll_cursor_to_visible();
1234        true
1235    }
1236
1237    /// Insert a line break at the cursor position.
1238    ///
1239    /// If auto_indent is set the new line starts with the same
1240    /// indent as the current.
1241    pub fn insert_newline(&mut self) -> bool {
1242        if self.has_selection() {
1243            self.value
1244                .remove_str_range(self.selection())
1245                .expect("valid_selection");
1246        }
1247        self.value
1248            .insert_newline(self.cursor())
1249            .expect("valid_cursor");
1250
1251        // insert leading spaces
1252        if self.auto_indent {
1253            let cursor = self.cursor();
1254            if cursor.y > 0 {
1255                let mut blanks = String::new();
1256                for g in self.line_graphemes(cursor.y - 1) {
1257                    if g == " " || g == "\t" {
1258                        blanks.push_str(g.grapheme());
1259                    } else {
1260                        break;
1261                    }
1262                }
1263                if !blanks.is_empty() {
1264                    self.value
1265                        .insert_str(cursor, &blanks)
1266                        .expect("valid_cursor");
1267                }
1268            }
1269        }
1270
1271        self.scroll_cursor_to_visible();
1272        true
1273    }
1274
1275    /// Deletes the given range.
1276    #[inline]
1277    pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1278        self.try_delete_range(range).expect("valid_range")
1279    }
1280
1281    /// Deletes the given range.
1282    #[inline]
1283    pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1284        let range = range.into();
1285        if !range.is_empty() {
1286            self.value.remove_str_range(range)?;
1287            self.scroll_cursor_to_visible();
1288            Ok(true)
1289        } else {
1290            Ok(false)
1291        }
1292    }
1293}
1294
1295impl TextAreaState {
1296    /// Duplicates the selection or the current line.
1297    /// Returns true if there was any real change.
1298    pub fn duplicate_text(&mut self) -> bool {
1299        if self.has_selection() {
1300            let sel_range = self.selection();
1301            if !sel_range.is_empty() {
1302                let v = self.str_slice(sel_range).to_string();
1303                self.value
1304                    .insert_str(sel_range.end, &v)
1305                    .expect("valid_selection");
1306                true
1307            } else {
1308                false
1309            }
1310        } else {
1311            let pos = self.cursor();
1312            let row_range = TextRange::new((0, pos.y), (0, pos.y + 1));
1313            let v = self.str_slice(row_range).to_string();
1314            self.value
1315                .insert_str(row_range.start, &v)
1316                .expect("valid_cursor");
1317            true
1318        }
1319    }
1320
1321    /// Deletes the current line.
1322    /// Returns true if there was any real change.
1323    pub fn delete_line(&mut self) -> bool {
1324        let pos = self.cursor();
1325        if pos.y + 1 < self.len_lines() {
1326            self.delete_range(TextRange::new((0, pos.y), (0, pos.y + 1)))
1327        } else {
1328            let width = self.line_width(pos.y);
1329            self.delete_range(TextRange::new((0, pos.y), (width, pos.y)))
1330        }
1331    }
1332
1333    /// Deletes the next char or the current selection.
1334    /// Returns true if there was any real change.
1335    pub fn delete_next_char(&mut self) -> bool {
1336        if self.has_selection() {
1337            self.delete_range(self.selection())
1338        } else {
1339            let r = self
1340                .value
1341                .remove_next_char(self.cursor())
1342                .expect("valid_cursor");
1343            self.scroll_cursor_to_visible();
1344            r
1345        }
1346    }
1347
1348    /// Deletes the previous char or the selection.
1349    /// Returns true if there was any real change.
1350    pub fn delete_prev_char(&mut self) -> bool {
1351        if self.has_selection() {
1352            self.delete_range(self.selection())
1353        } else {
1354            let r = self
1355                .value
1356                .remove_prev_char(self.cursor())
1357                .expect("valid_cursor");
1358            self.scroll_cursor_to_visible();
1359            r
1360        }
1361    }
1362
1363    /// Find the start of the next word. If the position is at the start
1364    /// or inside a word, the same position is returned.
1365    pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1366        self.value.next_word_start(pos.into()).expect("valid_pos")
1367    }
1368
1369    /// Find the start of the next word. If the position is at the start
1370    /// or inside a word, the same position is returned.
1371    pub fn try_next_word_start(
1372        &self,
1373        pos: impl Into<TextPosition>,
1374    ) -> Result<TextPosition, TextError> {
1375        self.value.next_word_start(pos.into())
1376    }
1377
1378    /// Find the end of the next word. Skips whitespace first, then goes on
1379    /// until it finds the next whitespace.
1380    pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1381        self.value.next_word_end(pos.into()).expect("valid_pos")
1382    }
1383
1384    /// Find the end of the next word. Skips whitespace first, then goes on
1385    /// until it finds the next whitespace.
1386    pub fn try_next_word_end(
1387        &self,
1388        pos: impl Into<TextPosition>,
1389    ) -> Result<TextPosition, TextError> {
1390        self.value.next_word_end(pos.into())
1391    }
1392
1393    /// Find the start of the prev word. Skips whitespace first, then goes on
1394    /// until it finds the next whitespace.
1395    ///
1396    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1397    /// both return start<=end!
1398    pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1399        self.value.prev_word_start(pos.into()).expect("valid_pos")
1400    }
1401
1402    /// Find the start of the prev word. Skips whitespace first, then goes on
1403    /// until it finds the next whitespace.
1404    ///
1405    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1406    /// both return start<=end!
1407    pub fn try_prev_word_start(
1408        &self,
1409        pos: impl Into<TextPosition>,
1410    ) -> Result<TextPosition, TextError> {
1411        self.value.prev_word_start(pos.into())
1412    }
1413
1414    /// Find the end of the previous word. Word is everything that is not whitespace.
1415    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1416    /// both return start<=end!
1417    pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1418        self.value.prev_word_end(pos.into()).expect("valid_pos")
1419    }
1420
1421    /// Find the end of the previous word. Word is everything that is not whitespace.
1422    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1423    /// both return start<=end!
1424    pub fn try_prev_word_end(
1425        &self,
1426        pos: impl Into<TextPosition>,
1427    ) -> Result<TextPosition, TextError> {
1428        self.value.prev_word_end(pos.into())
1429    }
1430
1431    /// Is the position at a word boundary?
1432    pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1433        self.value.is_word_boundary(pos.into()).expect("valid_pos")
1434    }
1435
1436    /// Is the position at a word boundary?
1437    pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1438        self.value.is_word_boundary(pos.into())
1439    }
1440
1441    /// Find the start of the word at pos.
1442    /// Returns pos if the position is not inside a word.
1443    pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1444        self.value.word_start(pos.into()).expect("valid_pos")
1445    }
1446
1447    /// Find the start of the word at pos.
1448    /// Returns pos if the position is not inside a word.
1449    pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1450        self.value.word_start(pos.into())
1451    }
1452
1453    /// Find the end of the word at pos.
1454    /// Returns pos if the position is not inside a word.
1455    pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1456        self.value.word_end(pos.into()).expect("valid_pos")
1457    }
1458
1459    /// Find the end of the word at pos.
1460    /// Returns pos if the position is not inside a word.
1461    pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1462        self.value.word_end(pos.into())
1463    }
1464
1465    /// Delete the next word. This alternates deleting the whitespace between words and
1466    /// the words themselves.
1467    pub fn delete_next_word(&mut self) -> bool {
1468        if self.has_selection() {
1469            self.delete_range(self.selection())
1470        } else {
1471            let cursor = self.cursor();
1472
1473            let start = self.next_word_start(cursor);
1474            if start != cursor {
1475                self.delete_range(cursor..start)
1476            } else {
1477                let end = self.next_word_end(cursor);
1478                self.delete_range(cursor..end)
1479            }
1480        }
1481    }
1482
1483    /// Deletes the previous word. This alternates deleting the whitespace
1484    /// between words and the words themselves.
1485    pub fn delete_prev_word(&mut self) -> bool {
1486        if self.has_selection() {
1487            self.delete_range(self.selection())
1488        } else {
1489            let cursor = self.cursor();
1490
1491            // delete to beginning of line?
1492            let till_line_start = if cursor.x != 0 {
1493                self.graphemes(TextRange::new((0, cursor.y), cursor), cursor)
1494                    .rev_cursor()
1495                    .all(|v| v.is_whitespace())
1496            } else {
1497                false
1498            };
1499
1500            if till_line_start {
1501                self.delete_range(TextRange::new((0, cursor.y), cursor))
1502            } else {
1503                let end = self.prev_word_end(cursor);
1504                if end != cursor {
1505                    self.delete_range(end..cursor)
1506                } else {
1507                    let start = self.prev_word_start(cursor);
1508                    self.delete_range(start..cursor)
1509                }
1510            }
1511        }
1512    }
1513
1514    /// Move the cursor left. Scrolls the cursor to visible.
1515    /// Returns true if there was any real change.
1516    pub fn move_left(&mut self, n: upos_type, extend_selection: bool) -> bool {
1517        let mut cursor = self.cursor();
1518
1519        if cursor.x == 0 {
1520            if cursor.y > 0 {
1521                cursor.y = cursor.y.saturating_sub(1);
1522                cursor.x = self.line_width(cursor.y);
1523            }
1524        } else {
1525            cursor.x = cursor.x.saturating_sub(n);
1526        }
1527
1528        self.set_move_col(Some(cursor.x));
1529        self.set_cursor(cursor, extend_selection)
1530    }
1531
1532    /// Move the cursor right. Scrolls the cursor to visible.
1533    /// Returns true if there was any real change.
1534    pub fn move_right(&mut self, n: upos_type, extend_selection: bool) -> bool {
1535        let mut cursor = self.cursor();
1536
1537        let c_line_width = self.line_width(cursor.y);
1538        if cursor.x == c_line_width {
1539            if cursor.y + 1 < self.len_lines() {
1540                cursor.y += 1;
1541                cursor.x = 0;
1542            }
1543        } else {
1544            cursor.x = min(cursor.x + n, c_line_width)
1545        }
1546
1547        self.set_move_col(Some(cursor.x));
1548        self.set_cursor(cursor, extend_selection)
1549    }
1550
1551    /// Move the cursor up. Scrolls the cursor to visible.
1552    /// Returns true if there was any real change.
1553    pub fn move_up(&mut self, n: upos_type, extend_selection: bool) -> bool {
1554        let mut cursor = self.cursor();
1555
1556        cursor.y = cursor.y.saturating_sub(n);
1557        let c_line_width = self.line_width(cursor.y);
1558        if let Some(move_col) = self.move_col() {
1559            cursor.x = min(move_col, c_line_width);
1560        } else {
1561            cursor.x = min(cursor.x, c_line_width);
1562        }
1563
1564        self.set_cursor(cursor, extend_selection)
1565    }
1566
1567    /// Move the cursor down. Scrolls the cursor to visible.
1568    /// Returns true if there was any real change.
1569    pub fn move_down(&mut self, n: upos_type, extend_selection: bool) -> bool {
1570        let mut cursor = self.cursor();
1571
1572        cursor.y = min(cursor.y + n, self.len_lines() - 1);
1573        let c_line_width = self.line_width(cursor.y);
1574        if let Some(move_col) = self.move_col() {
1575            cursor.x = min(move_col, c_line_width);
1576        } else {
1577            cursor.x = min(cursor.x, c_line_width);
1578        }
1579
1580        self.set_cursor(cursor, extend_selection)
1581    }
1582
1583    /// Move the cursor to the start of the line.
1584    /// Scrolls the cursor to visible.
1585    /// Returns true if there was any real change.
1586    pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1587        let mut cursor = self.cursor();
1588
1589        cursor.x = 'f: {
1590            for (idx, g) in self.line_graphemes(cursor.y).enumerate() {
1591                if g != " " && g != "\t" {
1592                    if cursor.x != idx as upos_type {
1593                        break 'f idx as upos_type;
1594                    } else {
1595                        break 'f 0;
1596                    }
1597                }
1598            }
1599            0
1600        };
1601
1602        self.set_move_col(Some(cursor.x));
1603        self.set_cursor(cursor, extend_selection)
1604    }
1605
1606    /// Move the cursor to the end of the line. Scrolls to visible, if
1607    /// necessary.
1608    /// Returns true if there was any real change.
1609    pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1610        let mut cursor = self.cursor();
1611
1612        cursor.x = self.line_width(cursor.y);
1613
1614        self.set_move_col(Some(cursor.x));
1615        self.set_cursor(cursor, extend_selection)
1616    }
1617
1618    /// Move the cursor to the document start.
1619    pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1620        let cursor = TextPosition::new(0, 0);
1621
1622        self.set_cursor(cursor, extend_selection)
1623    }
1624
1625    /// Move the cursor to the document end.
1626    pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1627        let len = self.len_lines();
1628
1629        let cursor = TextPosition::new(0, len - 1);
1630
1631        self.set_cursor(cursor, extend_selection)
1632    }
1633
1634    /// Move the cursor to the start of the visible area.
1635    pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1636        let (ox, oy) = self.offset();
1637
1638        let cursor = TextPosition::new(ox as upos_type, oy as upos_type);
1639
1640        self.set_cursor(cursor, extend_selection)
1641    }
1642
1643    /// Move the cursor to the end of the visible area.
1644    pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1645        let (ox, oy) = self.offset();
1646        let (ox, oy) = (ox as upos_type, oy as upos_type);
1647        let len = self.len_lines();
1648
1649        let cursor =
1650            TextPosition::new(ox, min(oy + self.vertical_page() as upos_type - 1, len - 1));
1651
1652        self.set_cursor(cursor, extend_selection)
1653    }
1654
1655    /// Move the cursor to the next word.
1656    pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1657        let cursor = self.cursor();
1658
1659        let word = self.next_word_end(cursor);
1660
1661        self.set_cursor(word, extend_selection)
1662    }
1663
1664    /// Move the cursor to the previous word.
1665    pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1666        let cursor = self.cursor();
1667
1668        let word = self.prev_word_start(cursor);
1669
1670        self.set_cursor(word, extend_selection)
1671    }
1672}
1673
1674impl HasScreenCursor for TextAreaState {
1675    /// Cursor position on the screen.
1676    fn screen_cursor(&self) -> Option<(u16, u16)> {
1677        if self.is_focused() {
1678            if self.has_selection() {
1679                None
1680            } else {
1681                let cursor = self.cursor();
1682                let (ox, oy) = self.offset();
1683                let (ox, oy) = (ox as upos_type, oy as upos_type);
1684
1685                if cursor.y < oy {
1686                    None
1687                } else if cursor.y >= oy + (self.inner.height + self.dark_offset.1) as upos_type {
1688                    None
1689                } else {
1690                    if cursor.x < ox {
1691                        None
1692                    } else if cursor.x > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1693                        None
1694                    } else {
1695                        let sy = self.row_to_screen(cursor);
1696                        let sx = self.col_to_screen(cursor);
1697
1698                        if let Some((sx, sy)) = sx.iter().zip(sy.iter()).next() {
1699                            Some((self.inner.x + *sx, self.inner.y + *sy))
1700                        } else {
1701                            None
1702                        }
1703                    }
1704                }
1705            }
1706        } else {
1707            None
1708        }
1709    }
1710}
1711
1712impl RelocatableState for TextAreaState {
1713    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1714        // clip offset for some corrections.
1715        self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1716        self.area = relocate_area(self.area, shift, clip);
1717        self.inner = relocate_area(self.inner, shift, clip);
1718    }
1719}
1720
1721impl TextAreaState {
1722    /// Converts from a widget relative screen coordinate to a line.
1723    /// It limits its result to a valid row.
1724    pub fn screen_to_row(&self, scy: i16) -> upos_type {
1725        let (_, oy) = self.offset();
1726        let oy = oy as upos_type + self.dark_offset.1 as upos_type;
1727
1728        if scy < 0 {
1729            oy.saturating_sub((scy as ipos_type).unsigned_abs())
1730        } else if scy as u16 >= (self.inner.height + self.dark_offset.1) {
1731            min(oy + scy as upos_type, self.len_lines().saturating_sub(1))
1732        } else {
1733            let scy = oy + scy as upos_type;
1734            let len = self.len_lines();
1735            if scy < len {
1736                scy
1737            } else {
1738                len.saturating_sub(1)
1739            }
1740        }
1741    }
1742
1743    /// Converts from a widget relative screen coordinate to a grapheme index.
1744    /// It limits its result to a valid column.
1745    ///
1746    /// * row is a row-index into the value, not a screen-row. It can be calculated
1747    ///   with screen_to_row().
1748    /// * x is the relative screen position.
1749    pub fn screen_to_col(&self, row: upos_type, scx: i16) -> upos_type {
1750        self.try_screen_to_col(row, scx).expect("valid_row")
1751    }
1752
1753    /// Converts from a widget relative screen coordinate to a grapheme index.
1754    /// It limits its result to a valid column.
1755    ///
1756    /// * row is a row-index into the value, not a screen-row. It can be calculated
1757    ///   with screen_to_row().
1758    /// * x is the relative screen position.
1759    pub fn try_screen_to_col(&self, row: upos_type, scx: i16) -> Result<upos_type, TextError> {
1760        let (ox, _) = self.offset();
1761
1762        let ox = ox as upos_type + self.dark_offset.0 as upos_type;
1763
1764        if scx < 0 {
1765            Ok(ox.saturating_sub((scx as ipos_type).unsigned_abs()))
1766        } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
1767            Ok(min(ox + scx as upos_type, self.line_width(row)))
1768        } else {
1769            let scx = scx as u16;
1770
1771            let line = self.try_glyphs(
1772                row..row + 1,
1773                ox as u16,
1774                self.inner.width + self.dark_offset.0,
1775            )?;
1776
1777            let mut col = ox;
1778            for g in line {
1779                if scx < g.screen_pos().0 + g.screen_width() {
1780                    break;
1781                }
1782                col = g.pos().x + 1;
1783            }
1784            Ok(col)
1785        }
1786    }
1787
1788    /// Converts the row of the position to a screen position
1789    /// relative to the widget area.
1790    pub fn row_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
1791        let pos = pos.into();
1792        let (_, oy) = self.offset();
1793
1794        if pos.y < oy as upos_type {
1795            return None;
1796        }
1797
1798        let screen_y = pos.y - oy as upos_type;
1799
1800        if screen_y >= self.dark_offset.1 as upos_type {
1801            Some(screen_y as u16 - self.dark_offset.1)
1802        } else {
1803            None
1804        }
1805    }
1806
1807    /// Converts a grapheme based position to a screen position
1808    /// relative to the widget area.
1809    pub fn col_to_screen(&self, pos: impl Into<TextPosition>) -> Option<u16> {
1810        self.try_col_to_screen(pos).expect("valid_pos")
1811    }
1812
1813    /// Converts a grapheme based position to a screen position
1814    /// relative to the widget area.
1815    pub fn try_col_to_screen(
1816        &self,
1817        pos: impl Into<TextPosition>,
1818    ) -> Result<Option<u16>, TextError> {
1819        let pos = pos.into();
1820        let (ox, _) = self.offset();
1821
1822        if pos.x < ox as upos_type {
1823            return Ok(None);
1824        }
1825
1826        let line = self.try_glyphs(
1827            pos.y..pos.y + 1,
1828            ox as u16,
1829            self.inner.width + self.dark_offset.0,
1830        )?;
1831        let mut screen_x = 0;
1832        for g in line {
1833            if g.pos().x == pos.x {
1834                break;
1835            }
1836            screen_x = g.screen_pos().0 + g.screen_width();
1837        }
1838
1839        if screen_x >= self.dark_offset.0 {
1840            Ok(Some(screen_x - self.dark_offset.0))
1841        } else {
1842            Ok(None)
1843        }
1844    }
1845
1846    /// Set the cursor position from screen coordinates.
1847    ///
1848    /// The cursor positions are relative to the inner rect.
1849    /// They may be negative too, this allows setting the cursor
1850    /// to a position that is currently scrolled away.
1851    pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
1852        let (scx, scy) = (cursor.0, cursor.1);
1853
1854        let cy = self.screen_to_row(scy);
1855        let cx = self.screen_to_col(cy, scx);
1856
1857        self.set_cursor(TextPosition::new(cx, cy), extend_selection)
1858    }
1859
1860    /// Set the cursor position from screen coordinates,
1861    /// rounds the position to the next word start/end.
1862    ///
1863    /// The cursor positions are relative to the inner rect.
1864    /// They may be negative too, this allows setting the cursor
1865    /// to a position that is currently scrolled away.
1866    pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
1867        let (scx, scy) = (cursor.0, cursor.1);
1868        let anchor = self.anchor();
1869
1870        let cy = self.screen_to_row(scy);
1871        let cx = self.screen_to_col(cy, scx);
1872        let cursor = TextPosition::new(cx, cy);
1873
1874        let cursor = if cursor < anchor {
1875            self.word_start(cursor)
1876        } else {
1877            self.word_end(cursor)
1878        };
1879
1880        // extend anchor
1881        if !self.is_word_boundary(anchor) {
1882            if cursor < anchor {
1883                self.set_cursor(self.word_end(anchor), false);
1884            } else {
1885                self.set_cursor(self.word_start(anchor), false);
1886            }
1887        }
1888
1889        self.set_cursor(cursor, extend_selection)
1890    }
1891}
1892
1893impl TextAreaState {
1894    /// Maximum offset that is accessible with scrolling.
1895    ///
1896    /// This is shorter than the length of the content by whatever fills the last page.
1897    /// This is the base for the scrollbar content_length.
1898    pub fn vertical_max_offset(&self) -> usize {
1899        self.vscroll.max_offset()
1900    }
1901
1902    /// Current vertical offset.
1903    pub fn vertical_offset(&self) -> usize {
1904        self.vscroll.offset()
1905    }
1906
1907    /// Vertical page-size at the current offset.
1908    pub fn vertical_page(&self) -> usize {
1909        self.vscroll.page_len()
1910    }
1911
1912    /// Suggested scroll per scroll-event.
1913    pub fn vertical_scroll(&self) -> usize {
1914        self.vscroll.scroll_by()
1915    }
1916
1917    /// Maximum offset that is accessible with scrolling.
1918    ///
1919    /// This is currently set to usize::MAX.
1920    pub fn horizontal_max_offset(&self) -> usize {
1921        self.hscroll.max_offset()
1922    }
1923
1924    /// Current horizontal offset.
1925    pub fn horizontal_offset(&self) -> usize {
1926        self.hscroll.offset()
1927    }
1928
1929    /// Horizontal page-size at the current offset.
1930    pub fn horizontal_page(&self) -> usize {
1931        self.hscroll.page_len()
1932    }
1933
1934    /// Suggested scroll per scroll-event.
1935    pub fn horizontal_scroll(&self) -> usize {
1936        self.hscroll.scroll_by()
1937    }
1938
1939    /// Change the vertical offset.
1940    ///
1941    /// Due to overscroll it's possible that this is an invalid offset for the widget.
1942    /// The widget must deal with this situation.
1943    ///
1944    /// The widget returns true if the offset changed at all.
1945    #[allow(unused_assignments)]
1946    pub fn set_vertical_offset(&mut self, row_offset: usize) -> bool {
1947        self.scroll_to_cursor = false;
1948        self.vscroll.set_offset(row_offset)
1949    }
1950
1951    /// Change the horizontal offset.
1952    ///
1953    /// Due to overscroll it's possible that this is an invalid offset for the widget.
1954    /// The widget must deal with this situation.
1955    ///
1956    /// The widget returns true if the offset changed at all.
1957    #[allow(unused_assignments)]
1958    pub fn set_horizontal_offset(&mut self, col_offset: usize) -> bool {
1959        self.scroll_to_cursor = false;
1960        self.hscroll.set_offset(col_offset)
1961    }
1962
1963    /// Scroll to position.
1964    pub fn scroll_to_row(&mut self, pos: usize) -> bool {
1965        self.scroll_to_cursor = false;
1966        self.vscroll.set_offset(pos)
1967    }
1968
1969    /// Scroll to position.
1970    pub fn scroll_to_col(&mut self, pos: usize) -> bool {
1971        self.scroll_to_cursor = false;
1972        self.hscroll.set_offset(pos)
1973    }
1974
1975    /// Scrolling
1976    pub fn scroll_up(&mut self, delta: usize) -> bool {
1977        self.vscroll.scroll_up(delta)
1978    }
1979
1980    /// Scrolling
1981    pub fn scroll_down(&mut self, delta: usize) -> bool {
1982        self.vscroll.scroll_down(delta)
1983    }
1984
1985    /// Scrolling
1986    pub fn scroll_left(&mut self, delta: usize) -> bool {
1987        self.hscroll.scroll_left(delta)
1988    }
1989
1990    /// Scrolling
1991    pub fn scroll_right(&mut self, delta: usize) -> bool {
1992        self.hscroll.scroll_right(delta)
1993    }
1994}
1995
1996impl TextAreaState {
1997    /// Scroll that the cursor is visible.
1998    /// All move-fn do this automatically.
1999    pub fn scroll_cursor_to_visible(&mut self) {
2000        self.scroll_to_cursor = true;
2001    }
2002}
2003
2004impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2005    fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2006        // small helper ...
2007        fn tc(r: bool) -> TextOutcome {
2008            if r {
2009                TextOutcome::TextChanged
2010            } else {
2011                TextOutcome::Unchanged
2012            }
2013        }
2014
2015        let mut r = if self.is_focused() {
2016            match event {
2017                ct_event!(key press c)
2018                | ct_event!(key press SHIFT-c)
2019                | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2020                ct_event!(keycode press Tab) => {
2021                    // ignore tab from focus
2022                    tc(if !self.focus.gained() {
2023                        self.insert_tab()
2024                    } else {
2025                        false
2026                    })
2027                }
2028                ct_event!(keycode press SHIFT-BackTab) => {
2029                    // ignore tab from focus
2030                    tc(if !self.focus.gained() {
2031                        self.insert_backtab()
2032                    } else {
2033                        false
2034                    })
2035                }
2036                ct_event!(keycode press Enter) => tc(self.insert_newline()),
2037                ct_event!(keycode press Backspace) => tc(self.delete_prev_char()),
2038                ct_event!(keycode press Delete) => tc(self.delete_next_char()),
2039                ct_event!(keycode press CONTROL-Backspace)
2040                | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2041                ct_event!(keycode press CONTROL-Delete) | ct_event!(keycode press ALT-Delete) => {
2042                    tc(self.delete_next_word())
2043                }
2044                ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2045                ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2046                ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2047                ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2048                ct_event!(key press CONTROL-'z') => tc(self.undo()),
2049                ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2050
2051                ct_event!(key release _)
2052                | ct_event!(key release SHIFT-_)
2053                | ct_event!(key release CONTROL_ALT-_)
2054                | ct_event!(keycode release Tab)
2055                | ct_event!(keycode release Enter)
2056                | ct_event!(keycode release Backspace)
2057                | ct_event!(keycode release Delete)
2058                | ct_event!(keycode release CONTROL-Backspace)
2059                | ct_event!(keycode release ALT-Backspace)
2060                | ct_event!(keycode release CONTROL-Delete)
2061                | ct_event!(key release CONTROL-'x')
2062                | ct_event!(key release CONTROL-'v')
2063                | ct_event!(key release CONTROL-'d')
2064                | ct_event!(key release CONTROL-'y')
2065                | ct_event!(key release CONTROL-'z')
2066                | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2067                _ => TextOutcome::Continue,
2068            }
2069        } else {
2070            TextOutcome::Continue
2071        };
2072        if r == TextOutcome::Continue {
2073            r = self.handle(event, ReadOnly);
2074        }
2075        r
2076    }
2077}
2078
2079impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
2080    fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2081        let mut r = if self.is_focused() {
2082            match event {
2083                ct_event!(keycode press Left) => self.move_left(1, false).into(),
2084                ct_event!(keycode press Right) => self.move_right(1, false).into(),
2085                ct_event!(keycode press Up) => self.move_up(1, false).into(),
2086                ct_event!(keycode press Down) => self.move_down(1, false).into(),
2087                ct_event!(keycode press PageUp) => self
2088                    .move_up(self.vertical_page() as upos_type, false)
2089                    .into(),
2090                ct_event!(keycode press PageDown) => self
2091                    .move_down(self.vertical_page() as upos_type, false)
2092                    .into(),
2093                ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
2094                ct_event!(keycode press End) => self.move_to_line_end(false).into(),
2095                ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
2096                ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
2097                ct_event!(keycode press CONTROL-Up) => false.into(),
2098                ct_event!(keycode press CONTROL-Down) => false.into(),
2099                ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
2100                ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
2101                ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
2102                ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
2103
2104                ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
2105                ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
2106                ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
2107                ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
2108                ct_event!(keycode press ALT-PageUp) => {
2109                    self.scroll_up(max(self.vertical_page() / 2, 1)).into()
2110                }
2111                ct_event!(keycode press ALT-PageDown) => {
2112                    self.scroll_down(max(self.vertical_page() / 2, 1)).into()
2113                }
2114                ct_event!(keycode press ALT_SHIFT-PageUp) => {
2115                    self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
2116                }
2117                ct_event!(keycode press ALT_SHIFT-PageDown) => {
2118                    self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
2119                }
2120
2121                ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
2122                ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
2123                ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
2124                ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
2125                ct_event!(keycode press SHIFT-PageUp) => {
2126                    self.move_up(self.vertical_page() as upos_type, true).into()
2127                }
2128                ct_event!(keycode press SHIFT-PageDown) => self
2129                    .move_down(self.vertical_page() as upos_type, true)
2130                    .into(),
2131                ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
2132                ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
2133                ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
2134                ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
2135                ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
2136                ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
2137                ct_event!(key press CONTROL-'a') => self.select_all().into(),
2138                ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
2139
2140                ct_event!(keycode release Left)
2141                | ct_event!(keycode release Right)
2142                | ct_event!(keycode release Up)
2143                | ct_event!(keycode release Down)
2144                | ct_event!(keycode release PageUp)
2145                | ct_event!(keycode release PageDown)
2146                | ct_event!(keycode release Home)
2147                | ct_event!(keycode release End)
2148                | ct_event!(keycode release CONTROL-Left)
2149                | ct_event!(keycode release CONTROL-Right)
2150                | ct_event!(keycode release CONTROL-Up)
2151                | ct_event!(keycode release CONTROL-Down)
2152                | ct_event!(keycode release CONTROL-PageUp)
2153                | ct_event!(keycode release CONTROL-PageDown)
2154                | ct_event!(keycode release CONTROL-Home)
2155                | ct_event!(keycode release CONTROL-End)
2156                | ct_event!(keycode release ALT-Left)
2157                | ct_event!(keycode release ALT-Right)
2158                | ct_event!(keycode release ALT-Up)
2159                | ct_event!(keycode release ALT-Down)
2160                | ct_event!(keycode release ALT-PageUp)
2161                | ct_event!(keycode release ALT-PageDown)
2162                | ct_event!(keycode release ALT_SHIFT-PageUp)
2163                | ct_event!(keycode release ALT_SHIFT-PageDown)
2164                | ct_event!(keycode release SHIFT-Left)
2165                | ct_event!(keycode release SHIFT-Right)
2166                | ct_event!(keycode release SHIFT-Up)
2167                | ct_event!(keycode release SHIFT-Down)
2168                | ct_event!(keycode release SHIFT-PageUp)
2169                | ct_event!(keycode release SHIFT-PageDown)
2170                | ct_event!(keycode release SHIFT-Home)
2171                | ct_event!(keycode release SHIFT-End)
2172                | ct_event!(keycode release CONTROL_SHIFT-Left)
2173                | ct_event!(keycode release CONTROL_SHIFT-Right)
2174                | ct_event!(keycode release CONTROL_SHIFT-Home)
2175                | ct_event!(keycode release CONTROL_SHIFT-End)
2176                | ct_event!(key release CONTROL-'a')
2177                | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2178                _ => TextOutcome::Continue,
2179            }
2180        } else {
2181            TextOutcome::Continue
2182        };
2183
2184        if r == TextOutcome::Continue {
2185            r = self.handle(event, MouseOnly);
2186        }
2187        r
2188    }
2189}
2190
2191impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
2192    fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
2193        flow!(match event {
2194            ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2195                let cx = m.column as i16 - self.inner.x as i16;
2196                let cy = m.row as i16 - self.inner.y as i16;
2197                self.set_screen_cursor((cx, cy), true).into()
2198            }
2199            ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2200                let cx = m.column as i16 - self.inner.x as i16;
2201                let cy = m.row as i16 - self.inner.y as i16;
2202                self.set_screen_cursor_words((cx, cy), true).into()
2203            }
2204            ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2205                let ty = self.screen_to_row(m.row as i16 - self.inner.y as i16);
2206                let tx = self.screen_to_col(ty, m.column as i16 - self.inner.x as i16);
2207                let test = TextPosition::new(tx, ty);
2208                let start = self.word_start(test);
2209                let end = self.word_end(test);
2210                self.set_selection(start, end).into()
2211            }
2212            ct_event!(mouse down Left for column,row) => {
2213                if self.inner.contains((*column, *row).into()) {
2214                    let cx = (column - self.inner.x) as i16;
2215                    let cy = (row - self.inner.y) as i16;
2216                    self.set_screen_cursor((cx, cy), false).into()
2217                } else {
2218                    TextOutcome::Continue
2219                }
2220            }
2221            ct_event!(mouse down SHIFT-Left for column,row) => {
2222                if self.inner.contains((*column, *row).into()) {
2223                    let cx = (column - self.inner.x) as i16;
2224                    let cy = (row - self.inner.y) as i16;
2225                    self.set_screen_cursor((cx, cy), true).into()
2226                } else {
2227                    TextOutcome::Continue
2228                }
2229            }
2230            ct_event!(mouse down CONTROL-Left for column,row) => {
2231                if self.inner.contains((*column, *row).into()) {
2232                    let cx = (column - self.inner.x) as i16;
2233                    let cy = (row - self.inner.y) as i16;
2234                    self.set_screen_cursor((cx, cy), true).into()
2235                } else {
2236                    TextOutcome::Continue
2237                }
2238            }
2239            ct_event!(mouse down ALT-Left for column,row) => {
2240                if self.inner.contains((*column, *row).into()) {
2241                    let cx = (column - self.inner.x) as i16;
2242                    let cy = (row - self.inner.y) as i16;
2243                    self.set_screen_cursor_words((cx, cy), true).into()
2244                } else {
2245                    TextOutcome::Continue
2246                }
2247            }
2248            _ => TextOutcome::Continue,
2249        });
2250
2251        let mut sas = ScrollAreaState::new()
2252            .area(self.inner)
2253            .h_scroll(&mut self.hscroll)
2254            .v_scroll(&mut self.vscroll);
2255        let r = match sas.handle(event, MouseOnly) {
2256            ScrollOutcome::Up(v) => self.scroll_up(v),
2257            ScrollOutcome::Down(v) => self.scroll_down(v),
2258            ScrollOutcome::Left(v) => self.scroll_left(v),
2259            ScrollOutcome::Right(v) => self.scroll_right(v),
2260            ScrollOutcome::VPos(v) => self.set_vertical_offset(v),
2261            ScrollOutcome::HPos(v) => self.set_horizontal_offset(v),
2262            _ => false,
2263        };
2264        if r {
2265            return TextOutcome::Changed;
2266        }
2267
2268        TextOutcome::Continue
2269    }
2270}
2271
2272/// Handle all events.
2273/// Text events are only processed if focus is true.
2274/// Mouse events are processed if they are in range.
2275pub fn handle_events(
2276    state: &mut TextAreaState,
2277    focus: bool,
2278    event: &crossterm::event::Event,
2279) -> TextOutcome {
2280    state.focus.set(focus);
2281    state.handle(event, Regular)
2282}
2283
2284/// Handle only navigation events.
2285/// Text events are only processed if focus is true.
2286/// Mouse events are processed if they are in range.
2287pub fn handle_readonly_events(
2288    state: &mut TextAreaState,
2289    focus: bool,
2290    event: &crossterm::event::Event,
2291) -> TextOutcome {
2292    state.focus.set(focus);
2293    state.handle(event, ReadOnly)
2294}
2295
2296/// Handle only mouse-events.
2297pub fn handle_mouse_events(
2298    state: &mut TextAreaState,
2299    event: &crossterm::event::Event,
2300) -> TextOutcome {
2301    state.handle(event, MouseOnly)
2302}