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