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::{Clipboard, global_clipboard};
8use crate::event::{ReadOnly, TextOutcome};
9use crate::glyph2::{GlyphIter2, TextWrap2};
10use crate::text_core::TextCore;
11use crate::text_core::core_op;
12use crate::text_store::TextStore;
13use crate::text_store::text_rope::TextRope;
14use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
15use crate::{HasScreenCursor, TextError, TextPosition, TextRange, TextStyle, ipos_type, upos_type};
16use crossterm::event::KeyModifiers;
17use rat_event::util::MouseFlags;
18use rat_event::{HandleEvent, MouseOnly, Regular, ct_event, flow};
19use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
20use rat_reloc::{RelocatableState, relocate_dark_offset, relocate_pos_tuple};
21use rat_scrolled::event::ScrollOutcome;
22use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState};
23use ratatui::buffer::Buffer;
24use ratatui::layout::{Rect, Size};
25use ratatui::style::{Style, Stylize};
26use ratatui::widgets::{Block, StatefulWidget};
27use ropey::Rope;
28use std::borrow::Cow;
29use std::cell::Cell;
30use std::cmp::{max, min};
31use std::collections::HashMap;
32use std::ops::Range;
33use std::rc::Rc;
34
35pub mod text_area_op;
36
37/// Text area widget.
38///
39/// [Example](https://github.com/thscharler/rat-salsa/blob/master/rat-text/examples/textarea2.rs)
40///
41/// Backend used is [ropey](https://docs.rs/ropey/latest/ropey/), so large
42/// texts are no problem. Editing time increases with the number of
43/// styles applied. Everything below a million styles should be fine.
44///
45/// For emoji support this uses
46/// [unicode_display_width](https://docs.rs/unicode-display-width/latest/unicode_display_width/index.html)
47/// which helps with those double-width emojis. Input of emojis
48/// strongly depends on the terminal. It may or may not work.
49/// And even with display there are sometimes strange glitches
50/// that I haven't found yet.
51///
52/// Keyboard and mouse are implemented for crossterm, but it should be
53/// easy to extend to other event-types. Every interaction is available
54/// as function on the state.
55///
56/// Scrolling doesn't depend on the cursor, but the editing and move
57/// functions take care that the cursor stays visible.
58///
59/// Text wrapping is available, hard line-breaks at the right margin,
60/// or decent word-wrapping.
61///
62/// You can directly access the underlying Rope for readonly purposes, and
63/// conversion from/to byte/char positions are available. That should probably be
64/// enough to write a parser that generates some styling.
65///
66/// The cursor must set externally on the ratatui Frame as usual.
67/// [screen_cursor](TextAreaState::screen_cursor) gives you the correct value.
68/// There is the inverse too [set_screen_cursor](TextAreaState::set_screen_cursor)
69/// For more interactions you can use [screen_to_pos](TextAreaState::screen_to_pos),
70/// and [pos_to_screen](TextAreaState::pos_to_screen). They calculate everything,
71/// even in the presence of more complex graphemes and those double-width emojis.
72///
73/// # Stateful
74/// This widget implements [`StatefulWidget`], you can use it with
75/// [`TextAreaState`] to handle common actions.
76#[derive(Debug, Default, Clone)]
77pub struct TextArea<'a> {
78    style: Style,
79    block: Option<Block<'a>>,
80    hscroll: Option<Scroll<'a>>,
81    h_max_offset: Option<upos_type>,
82    h_overscroll: Option<upos_type>,
83    vscroll: Option<Scroll<'a>>,
84
85    focus_style: Option<Style>,
86    select_style: Option<Style>,
87    text_style: HashMap<usize, Style>,
88
89    text_wrap: Option<TextWrap>,
90}
91
92/// State & event handling.
93#[derive(Debug)]
94pub struct TextAreaState {
95    /// The whole area with block.
96    /// __read only__ renewed with each render.
97    pub area: Rect,
98    /// Area inside a possible block.
99    /// __read only__ renewed with each render.
100    pub inner: Rect,
101    /// Rendered dimension. This may differ from (inner.width, inner.height)
102    /// if the text area has been relocated/clipped. This holds the
103    /// original rendered dimension before any relocation/clipping.
104    pub rendered: Size,
105    /// Cursor position on the screen.
106    pub screen_cursor: Option<(u16, u16)>,
107
108    /// Horizontal scroll.
109    /// When text-break is active this value is ignored.
110    /// __read+write__
111    pub hscroll: ScrollState,
112    /// Vertical offset.
113    /// __read+write__
114    pub vscroll: ScrollState,
115    /// When text-break is active, this is the grapheme-offset
116    /// into the first visible text-row where the display
117    /// actually starts.
118    /// __read+write__ but it's not advised.
119    pub sub_row_offset: upos_type,
120    /// Dark offset due to clipping. Set during render.
121    /// __read only__ ignore this value.
122    pub dark_offset: (u16, u16),
123    /// The scroll offset will be adjusted to display
124    /// the cursor. This will be the minimal adjustment,
125    /// the cursor will stay at the same screen position if
126    /// it's already visible or appear at the start/end if it's not.
127    /// __read only__ use [scroll_cursor_to_visible](TextAreaState::scroll_cursor_to_visible).
128    pub scroll_to_cursor: Rc<Cell<bool>>,
129
130    /// Text edit core
131    pub value: TextCore<TextRope>,
132
133    /// Memory-column for up/down movement.
134    ///
135    /// Up/down movement tries to place the cursor at this column,
136    /// but might have to clip it, because the current line is too short.
137    ///
138    /// This is kept as a relative screen-position. It may be less
139    /// than 0, if the widget has been relocated.
140    /// __read only__
141    pub move_col: Option<i16>,
142    /// auto indent active
143    /// __read only__
144    pub auto_indent: bool,
145    /// quote selection active
146    /// __read only__
147    pub auto_quote: bool,
148    /// text breaking
149    /// __read only__
150    pub text_wrap: TextWrap,
151    /// new-line bytes
152    /// __read only__
153    pub newline: String,
154    /// tab-width
155    /// __read only__
156    pub tab_width: u32,
157    /// expand tabs
158    /// __read only__
159    pub expand_tabs: bool,
160
161    /// Current focus state.
162    /// __read + write__ But don't write it.
163    pub focus: FocusFlag,
164
165    /// Mouse selection in progress.
166    /// __read+write__
167    pub mouse: MouseFlags,
168
169    pub non_exhaustive: NonExhaustive,
170}
171
172impl Clone for TextAreaState {
173    fn clone(&self) -> Self {
174        Self {
175            area: self.area,
176            inner: self.inner,
177            rendered: self.rendered,
178            screen_cursor: self.screen_cursor,
179            hscroll: self.hscroll.clone(),
180            vscroll: self.vscroll.clone(),
181            sub_row_offset: self.sub_row_offset,
182            dark_offset: self.dark_offset,
183            scroll_to_cursor: Rc::new(Cell::new(self.scroll_to_cursor.get())),
184            value: self.value.clone(),
185            move_col: None,
186            auto_indent: self.auto_indent,
187            auto_quote: self.auto_quote,
188            text_wrap: self.text_wrap,
189            newline: self.newline.clone(),
190            tab_width: self.tab_width,
191            expand_tabs: self.expand_tabs,
192            focus: self.focus.new_instance(),
193            mouse: Default::default(),
194            non_exhaustive: NonExhaustive,
195        }
196    }
197}
198
199/// Text breaking.
200#[derive(Debug, Default, Clone, Copy)]
201#[non_exhaustive]
202pub enum TextWrap {
203    /// Don't break, shift text to the left.
204    #[default]
205    Shift,
206    /// Hard break at the right border.
207    Hard,
208    /// Wraps the text at word boundaries.
209    ///
210    /// The parameter gives an area before the right border where
211    /// breaks are preferred. The first space that falls in this
212    /// region will break. Otherwise, the last space before will be
213    /// used, or the word will be hard-wrapped.
214    ///
215    /// Space is the word-separator. Words will be broken if they
216    /// contain a hyphen, a soft-hyphen or a zero-width-space.
217    Word(u16),
218}
219
220impl<'a> TextArea<'a> {
221    /// New widget.
222    pub fn new() -> Self {
223        Self::default()
224    }
225
226    /// Set the combined style.
227    #[inline]
228    pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
229        if let Some(styles) = styles {
230            self.styles(styles)
231        } else {
232            self
233        }
234    }
235
236    /// Set the combined style.
237    #[inline]
238    pub fn styles(mut self, styles: TextStyle) -> Self {
239        self.style = styles.style;
240        if styles.block.is_some() {
241            self.block = styles.block;
242        }
243        if let Some(border_style) = styles.border_style {
244            self.block = self.block.map(|v| v.border_style(border_style));
245        }
246        if let Some(title_style) = styles.title_style {
247            self.block = self.block.map(|v| v.title_style(title_style));
248        }
249        self.block = self.block.map(|v| v.style(self.style));
250
251        if let Some(styles) = styles.scroll {
252            self.hscroll = self.hscroll.map(|v| v.styles(styles.clone()));
253            self.vscroll = self.vscroll.map(|v| v.styles(styles));
254        }
255        if styles.focus.is_some() {
256            self.focus_style = styles.focus;
257        }
258        if styles.select.is_some() {
259            self.select_style = styles.select;
260        }
261
262        self
263    }
264
265    /// Base style.
266    pub fn style(mut self, style: Style) -> Self {
267        self.style = style;
268        self.block = self.block.map(|v| v.style(style));
269        self
270    }
271
272    /// Style when focused.
273    pub fn focus_style(mut self, style: Style) -> Self {
274        self.focus_style = Some(style);
275        self.block = self.block.map(|v| v.style(self.style));
276        self
277    }
278
279    /// Selection style.
280    pub fn select_style(mut self, style: Style) -> Self {
281        self.select_style = Some(style);
282        self
283    }
284
285    /// Indexed text-style.
286    ///
287    /// Use [TextAreaState::add_style()] to refer a text range to
288    /// one of these styles.
289    pub fn text_style_idx(mut self, idx: usize, style: Style) -> Self {
290        self.text_style.insert(idx, style);
291        self
292    }
293
294    /// List of text-styles.
295    ///
296    /// Use [TextAreaState::add_style()] to refer a text range to
297    /// one of these styles.
298    pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
299        for (i, s) in styles.into_iter().enumerate() {
300            self.text_style.insert(i, s);
301        }
302        self
303    }
304
305    /// Map of style_id -> text_style.
306    ///
307    /// Use [TextAreaState::add_style()] to refer a text range to
308    /// one of these styles.
309    pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
310        for (i, s) in styles.into_iter() {
311            self.text_style.insert(i, s.into());
312        }
313        self
314    }
315
316    /// Block.
317    #[inline]
318    pub fn block(mut self, block: Block<'a>) -> Self {
319        self.block = Some(block);
320        self
321    }
322
323    /// Set both scrollbars.
324    pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
325        self.hscroll = Some(scroll.clone().override_horizontal());
326        self.vscroll = Some(scroll.override_vertical());
327        self
328    }
329
330    /// Set the horizontal scrollbar.
331    pub fn hscroll(mut self, scroll: Scroll<'a>) -> Self {
332        self.hscroll = Some(scroll.override_horizontal());
333        self
334    }
335
336    /// Set the text wrapping.
337    pub fn text_wrap(mut self, wrap: TextWrap) -> Self {
338        self.text_wrap = Some(wrap);
339        self
340    }
341
342    /// Maximum offset the horizontal scrollbar.
343    ///
344    /// This widget doesn't try to find a correct maximum value
345    /// to show with the horizontal scroll bar, but uses this
346    /// fixed value instead. This is the maximum offset that can
347    /// be reached by using the scrollbar.
348    ///
349    /// Finding the maximum line length for a text is rather
350    /// expensive, so this widget doesn't even try.
351    ///
352    /// This doesn't limit the column that can be reached with
353    /// cursor positioning, just what can be done via the scrollbar.
354    ///
355    /// See [self.set_horizontal_overscroll]
356    ///
357    /// Default is 255.
358    pub fn set_horizontal_max_offset(mut self, offset: u32) -> Self {
359        self.h_max_offset = Some(offset);
360        self
361    }
362
363    /// Maximum overscroll that can be reached by using the horizontal
364    /// scrollbar and dragging beyond the area of the widget.
365    ///
366    /// See [self.set_horizontal_max_offset]
367    ///
368    /// Default is 16384.
369    pub fn set_horizontal_overscroll(mut self, overscroll: u32) -> Self {
370        self.h_overscroll = Some(overscroll);
371        self
372    }
373
374    /// Set the vertical scrollbar.
375    pub fn vscroll(mut self, scroll: Scroll<'a>) -> Self {
376        self.vscroll = Some(scroll.override_vertical());
377        self
378    }
379
380    /// Preferred width: 0
381    pub fn width(&self) -> u16 {
382        0
383    }
384
385    /// Preferred height: 1
386    pub fn height(&self) -> u16 {
387        1
388    }
389}
390
391impl<'a> StatefulWidget for &TextArea<'a> {
392    type State = TextAreaState;
393
394    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
395        render_text_area(self, area, buf, state);
396    }
397}
398
399impl StatefulWidget for TextArea<'_> {
400    type State = TextAreaState;
401
402    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
403        render_text_area(&self, area, buf, state);
404    }
405}
406
407fn render_text_area(
408    widget: &TextArea<'_>,
409    area: Rect,
410    buf: &mut Buffer,
411    state: &mut TextAreaState,
412) {
413    state.area = area;
414    state.screen_cursor = None;
415    if let Some(text_wrap) = widget.text_wrap {
416        state.text_wrap = text_wrap;
417    }
418
419    let style = widget.style;
420    let focus_style = if let Some(focus_style) = widget.focus_style {
421        focus_style
422    } else {
423        style
424    };
425    let select_style = if let Some(select_style) = widget.select_style {
426        select_style
427    } else {
428        Style::default().black().on_yellow()
429    };
430    let (style, select_style) = if state.is_focused() {
431        (
432            style.patch(focus_style),
433            style.patch(focus_style).patch(select_style),
434        )
435    } else {
436        (style, style.patch(select_style))
437    };
438
439    // sync scroll and cursor
440    state.area = area;
441    state.screen_cursor = None;
442    state.inner = ScrollArea::new()
443        .block(widget.block.as_ref())
444        .h_scroll(widget.hscroll.as_ref())
445        .v_scroll(widget.vscroll.as_ref())
446        .inner(area, Some(&state.hscroll), Some(&state.vscroll));
447    state.rendered = state.inner.as_size();
448
449    if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
450        state.hscroll.set_max_offset(0);
451        state.hscroll.set_overscroll_by(None);
452    } else {
453        if let Some(h_max_offset) = widget.h_max_offset {
454            state.hscroll.set_max_offset(h_max_offset as usize);
455        }
456        if let Some(h_overscroll) = widget.h_overscroll {
457            state.hscroll.set_overscroll_by(Some(h_overscroll as usize));
458        }
459    }
460    state.hscroll.set_page_len(state.inner.width as usize);
461
462    if let TextWrap::Hard | TextWrap::Word(_) = state.text_wrap {
463        state
464            .vscroll
465            .set_max_offset(state.len_lines().saturating_sub(1) as usize);
466    } else {
467        state.vscroll.set_max_offset(
468            state
469                .len_lines()
470                .saturating_sub(state.inner.height as upos_type) as usize,
471        );
472    }
473    state.vscroll.set_page_len(state.inner.height as usize);
474
475    if state.scroll_to_cursor.get() {
476        state.scroll_to_pos(state.cursor());
477    }
478
479    // scroll + background
480    ScrollArea::new()
481        .block(widget.block.as_ref())
482        .h_scroll(widget.hscroll.as_ref())
483        .v_scroll(widget.vscroll.as_ref())
484        .style(style)
485        .render(
486            area,
487            buf,
488            &mut ScrollAreaState::new()
489                .h_scroll(&mut state.hscroll)
490                .v_scroll(&mut state.vscroll),
491        );
492
493    if state.inner.width == 0 || state.inner.height == 0 {
494        // noop
495        return;
496    }
497    if state.vscroll.offset() > state.value.len_lines() as usize {
498        // noop
499        return;
500    }
501
502    let (shift_left, sub_row_offset, start_row) = state.clean_offset();
503    let page_rows = start_row
504        ..min(
505            start_row + state.inner.height as upos_type,
506            state.value.len_lines(),
507        );
508    let page_bytes = state
509        .try_bytes_at_range(TextRange::new(
510            (sub_row_offset, page_rows.start),
511            (0, page_rows.end),
512        ))
513        .expect("valid_rows");
514    // let mut screen_cursor = None;
515    let selection = state.selection();
516    let mut styles = Vec::new();
517
518    for g in state
519        .glyphs2(shift_left, sub_row_offset, page_rows)
520        .expect("valid_offset")
521    {
522        // relative screen-pos of the glyph
523        let screen_pos = g.screen_pos();
524
525        if screen_pos.1 >= state.inner.height {
526            break;
527        }
528
529        if g.screen_width() > 0 {
530            let mut style = style;
531            // text-styles
532            state
533                .value
534                .styles_at_page(g.text_bytes().start, page_bytes.clone(), &mut styles);
535            for style_nr in &styles {
536                if let Some(s) = widget.text_style.get(style_nr) {
537                    style = style.patch(*s);
538                }
539            }
540            // selection
541            if selection.contains_pos(g.pos()) {
542                style = style.patch(select_style);
543            };
544
545            // render glyph
546            if let Some(cell) =
547                buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
548            {
549                cell.set_symbol(g.glyph());
550                cell.set_style(style);
551            }
552            // clear the reset of the cells to avoid interferences.
553            for d in 1..g.screen_width() {
554                if let Some(cell) = buf.cell_mut((
555                    state.inner.x + screen_pos.0 + d,
556                    state.inner.y + screen_pos.1,
557                )) {
558                    cell.reset();
559                    cell.set_style(style);
560                }
561            }
562        }
563    }
564
565    state.screen_cursor = state.pos_to_screen(state.cursor());
566}
567
568impl Default for TextAreaState {
569    fn default() -> Self {
570        #[cfg(windows)]
571        const LINE_ENDING: &str = "\r\n";
572
573        #[cfg(not(windows))]
574        const LINE_ENDING: &str = "\n";
575
576        let mut s = Self {
577            area: Default::default(),
578            inner: Default::default(),
579            rendered: Default::default(),
580            screen_cursor: Default::default(),
581            hscroll: Default::default(),
582            vscroll: Default::default(),
583            sub_row_offset: 0,
584            dark_offset: Default::default(),
585            scroll_to_cursor: Default::default(),
586            value: TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard())),
587            move_col: Default::default(),
588            auto_indent: true,
589            auto_quote: true,
590            text_wrap: TextWrap::Shift,
591            newline: LINE_ENDING.to_string(),
592            tab_width: 8,
593            expand_tabs: true,
594            focus: Default::default(),
595            mouse: Default::default(),
596            non_exhaustive: NonExhaustive,
597        };
598        s.hscroll.set_max_offset(255);
599        s.hscroll.set_overscroll_by(Some(16384));
600        s
601    }
602}
603
604impl HasFocus for TextAreaState {
605    fn build(&self, builder: &mut FocusBuilder) {
606        builder.leaf_widget(self);
607    }
608
609    fn focus(&self) -> FocusFlag {
610        self.focus.clone()
611    }
612
613    fn area(&self) -> Rect {
614        self.area
615    }
616
617    fn navigable(&self) -> Navigation {
618        Navigation::Reach
619    }
620}
621
622impl TextAreaState {
623    /// New State.
624    #[inline]
625    pub fn new() -> Self {
626        Self::default()
627    }
628
629    /// New state with a focus name.
630    #[inline]
631    pub fn named(name: &str) -> Self {
632        Self {
633            focus: FocusFlag::new().with_name(name),
634            ..Default::default()
635        }
636    }
637
638    /// Sets the line ending used for insert.
639    /// There is no auto-detection or conversion done for set_value().
640    ///
641    /// Caution: If this doesn't match the line ending used in the value, you
642    /// will get a value with mixed line endings.
643    #[inline]
644    pub fn set_newline(&mut self, br: impl Into<String>) {
645        self.newline = br.into();
646    }
647
648    /// Line ending used for insert.
649    #[inline]
650    pub fn newline(&self) -> &str {
651        &self.newline
652    }
653
654    /// Sets auto-indent on new-line.
655    #[inline]
656    pub fn set_auto_indent(&mut self, indent: bool) {
657        self.auto_indent = indent;
658    }
659
660    /// Activates 'add quotes to selection'.
661    #[inline]
662    pub fn set_auto_quote(&mut self, quote: bool) {
663        self.auto_quote = quote;
664    }
665
666    /// Set tab-width.
667    #[inline]
668    pub fn set_tab_width(&mut self, tabs: u32) {
669        self.tab_width = tabs;
670    }
671
672    /// Tab-width
673    #[inline]
674    pub fn tab_width(&self) -> u32 {
675        self.tab_width
676    }
677
678    /// Expand tabs to spaces. Only for new inputs.
679    #[inline]
680    pub fn set_expand_tabs(&mut self, expand: bool) {
681        self.expand_tabs = expand;
682    }
683
684    /// Expand tabs to spaces. Only for new inputs.
685    #[inline]
686    pub fn expand_tabs(&self) -> bool {
687        self.expand_tabs
688    }
689
690    /// Show glyphs for control characters.
691    #[inline]
692    pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
693        self.value.set_glyph_ctrl(show_ctrl);
694    }
695
696    /// Show glyphs for control characters.
697    pub fn show_ctrl(&self) -> bool {
698        self.value.glyph_ctrl()
699    }
700
701    /// Show glyphs for text-wrapping.
702    /// Shows inserted line-breaks, zero-width space (U+200B) or the soft hyphen (U+00AD).
703    #[inline]
704    pub fn set_wrap_ctrl(&mut self, wrap_ctrl: bool) {
705        self.value.set_wrap_ctrl(wrap_ctrl);
706    }
707
708    /// Show glyphs for text-wrapping.
709    /// Shows inserted line-breaks, zero-width space (U+200B) or the soft hyphen (U+00AD).
710    pub fn wrap_ctrl(&self) -> bool {
711        self.value.wrap_ctrl()
712    }
713
714    /// Text wrapping mode.
715    ///
716    /// * TextWrap::Shift means no wrapping.
717    /// * TextWrap::Hard hard-wraps at the right border.
718    /// * TextWrap::Word(n) does word wrapping.
719    ///   n gives the size of the break-region close to the right border.
720    ///   The first space that falls in this region is taken as a break.
721    ///   If that is not possible this will break at the last space before.
722    ///   If there is no space it will hard-wrap the word.
723    ///
724    ///   Space is used as word separator. Hyphen will be used to break
725    ///   a word, and soft-hyphen and zero-width-space will be recognized too.
726    pub fn set_text_wrap(&mut self, text_wrap: TextWrap) {
727        self.text_wrap = text_wrap;
728    }
729
730    /// Text wrapping.
731    pub fn text_wrap(&self) -> TextWrap {
732        self.text_wrap
733    }
734
735    /// Extra column information for cursor movement.
736    ///
737    /// The cursor position is capped to the current line length, so if you
738    /// move up one row, you might end at a position left of the current column.
739    /// If you move up once more you want to return to the original position.
740    /// That's what is stored here.
741    ///
742    /// This stores the relative screen-column, it may be less than 0
743    /// if the widget has been relocated.
744    #[inline]
745    pub fn set_move_col(&mut self, col: Option<i16>) {
746        self.move_col = col;
747    }
748
749    /// Extra column information for cursor movement.
750    #[inline]
751    pub fn move_col(&mut self) -> Option<i16> {
752        self.move_col
753    }
754}
755
756impl TextAreaState {
757    /// Clipboard used.
758    /// Default is to use the global_clipboard().
759    #[inline]
760    pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
761        match clip {
762            None => self.value.set_clipboard(None),
763            Some(v) => self.value.set_clipboard(Some(Box::new(v))),
764        }
765    }
766
767    /// Clipboard used.
768    /// Default is to use the global_clipboard().
769    #[inline]
770    pub fn clipboard(&self) -> Option<&dyn Clipboard> {
771        self.value.clipboard()
772    }
773
774    /// Copy selection to clipboard.
775    #[inline]
776    pub fn copy_to_clip(&mut self) -> bool {
777        let Some(clip) = self.value.clipboard() else {
778            return false;
779        };
780
781        _ = clip.set_string(self.selected_text().as_ref());
782        false
783    }
784
785    /// Cut selection to clipboard.
786    #[inline]
787    pub fn cut_to_clip(&mut self) -> bool {
788        let Some(clip) = self.value.clipboard() else {
789            return false;
790        };
791
792        match clip.set_string(self.selected_text().as_ref()) {
793            Ok(_) => self.delete_range(self.selection()),
794            Err(_) => false,
795        }
796    }
797
798    /// Paste from clipboard.
799    #[inline]
800    pub fn paste_from_clip(&mut self) -> bool {
801        let Some(clip) = self.value.clipboard() else {
802            return false;
803        };
804
805        if let Ok(text) = clip.get_string() {
806            self.insert_str(text)
807        } else {
808            false
809        }
810    }
811}
812
813impl TextAreaState {
814    /// Set the undo buffer.
815    ///
816    /// Default is an undo-buffer with 99 undoes.
817    ///
818    /// Adjacent edits will be merged automatically into a single undo.
819    /// (Type multiple characters, they will be undone in one go.)
820    #[inline]
821    pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
822        match undo {
823            None => self.value.set_undo_buffer(None),
824            Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
825        }
826    }
827
828    /// Access the undo buffer.
829    #[inline]
830    pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
831        self.value.undo_buffer()
832    }
833
834    /// Access the undo buffer.
835    #[inline]
836    pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
837        self.value.undo_buffer_mut()
838    }
839
840    /// Begin a sequence of changes that should be undone in one go.
841    ///
842    /// Call begin_undo_seq(), then call any edit-functions. When
843    /// you are done call end_undo_seq(). Any changes will be undone
844    /// in a single step.
845    #[inline]
846    pub fn begin_undo_seq(&mut self) {
847        self.value.begin_undo_seq()
848    }
849
850    /// End a sequence of changes that should be undone in one go.
851    #[inline]
852    pub fn end_undo_seq(&mut self) {
853        self.value.end_undo_seq()
854    }
855
856    /// Get all recent replay recordings. This log can be sent
857    /// to a second TextAreaState and can be applied with replay_log().
858    ///
859    /// There are some [caveats](UndoBuffer::enable_replay_log).
860    #[inline]
861    pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
862        self.value.recent_replay_log()
863    }
864
865    /// Apply the replay recording.
866    ///
867    /// There are some [caveats](UndoBuffer::enable_replay_log).
868    #[inline]
869    pub fn replay_log(&mut self, replay: &[UndoEntry]) {
870        self.value.replay_log(replay)
871    }
872
873    /// Do one undo.
874    #[inline]
875    pub fn undo(&mut self) -> bool {
876        self.value.undo()
877    }
878
879    /// Do one redo.
880    #[inline]
881    pub fn redo(&mut self) -> bool {
882        self.value.redo()
883    }
884}
885
886impl TextAreaState {
887    /// Set and replace all styles.
888    ///
889    /// The ranges are byte-ranges into the text. There is no
890    /// verification that the ranges fit the text.
891    ///
892    /// Each byte-range maps to an index into the styles set
893    /// with the widget.
894    ///
895    /// Any style-idx that don't have a match there are just
896    /// ignored. You can use this to store other range based information.
897    /// The ranges are corrected during edits, no need to recalculate
898    /// everything after each keystroke.
899    ///
900    /// But this is only a very basic correction based on
901    /// insertions and deletes. If you use this for syntax-highlighting
902    /// you probably need to rebuild the styles.
903    #[inline]
904    pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
905        self.value.set_styles(styles);
906    }
907
908    /// Set and replace all styles.
909    ///
910    /// The ranges are TextRanges into the text.
911    /// Each byte-range maps to an index into the styles set
912    /// with the widget.
913    ///
914    /// Any style-idx that don't have a match there are just
915    /// ignored. You can use this to store other range based information.
916    /// The ranges are corrected during edits, no need to recalculate
917    /// everything after each keystroke.
918    #[inline]
919    pub fn set_range_styles(&mut self, styles: Vec<(TextRange, usize)>) -> Result<(), TextError> {
920        let mut mapped = Vec::with_capacity(styles.len());
921        for (r, s) in styles {
922            let rr = self.value.bytes_at_range(r)?;
923            mapped.push((rr, s));
924        }
925        self.value.set_styles(mapped);
926        Ok(())
927    }
928
929    /// Add a style for a byte-range.
930    ///
931    /// The style-idx refers to one of the styles set with the widget.
932    /// Missing styles are just ignored.
933    #[inline]
934    pub fn add_style(&mut self, range: Range<usize>, style: usize) {
935        self.value.add_style(range, style);
936    }
937
938    /// Add a style for a [TextRange]. The style-nr refers to one
939    /// of the styles set with the widget.
940    /// Missing styles are just ignored.
941    #[inline]
942    pub fn add_range_style(
943        &mut self,
944        range: impl Into<TextRange>,
945        style: usize,
946    ) -> Result<(), TextError> {
947        let r = self.value.bytes_at_range(range.into())?;
948        self.value.add_style(r, style);
949        Ok(())
950    }
951
952    /// Remove the exact byte-range and style.
953    #[inline]
954    pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
955        self.value.remove_style(range, style);
956    }
957
958    /// Remove all ranges for the given style.
959    #[inline]
960    pub fn remove_style_fully(&mut self, style: usize) {
961        self.value.remove_style_fully(style);
962    }
963
964    /// Remove the exact TextRange and style.
965    #[inline]
966    pub fn remove_range_style(
967        &mut self,
968        range: impl Into<TextRange>,
969        style: usize,
970    ) -> Result<(), TextError> {
971        let r = self.value.bytes_at_range(range.into())?;
972        self.value.remove_style(r, style);
973        Ok(())
974    }
975
976    /// Find all styles that touch the given range.
977    pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
978        self.value.styles_in(range, buf)
979    }
980
981    /// Find all styles that touch the given range.
982    pub fn styles_in_match(
983        &self,
984        range: Range<usize>,
985        style: usize,
986        buf: &mut Vec<(Range<usize>, usize)>,
987    ) {
988        self.value.styles_in_match(range, style, buf);
989    }
990
991    /// All styles active at the given position.
992    #[inline]
993    pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
994        self.value.styles_at(byte_pos, buf)
995    }
996
997    /// Check if the given style applies at the position and
998    /// return the complete range for the style.
999    #[inline]
1000    pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
1001        self.value.styles_at_match(byte_pos, style)
1002    }
1003
1004    /// List of all styles.
1005    #[inline]
1006    pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
1007        self.value.styles()
1008    }
1009}
1010
1011impl TextAreaState {
1012    /// Current offset for scrolling.
1013    #[inline]
1014    pub fn offset(&self) -> (upos_type, upos_type) {
1015        (
1016            self.hscroll.offset() as upos_type,
1017            self.vscroll.offset() as upos_type,
1018        )
1019    }
1020
1021    /// Set the offset for scrolling.
1022    ///
1023    /// The offset uses usize, but it shouldn't exceed (u32::MAX, u32::MAX).
1024    /// This is due to the internal ScrollState that only knows usize.
1025    #[inline]
1026    pub fn set_offset(&mut self, offset: (upos_type, upos_type)) -> bool {
1027        self.scroll_to_cursor.set(false);
1028        let c = self.hscroll.set_offset(offset.0 as usize);
1029        let r = self.vscroll.set_offset(offset.1 as usize);
1030        r || c
1031    }
1032
1033    /// Scrolling uses the offset() for the start of the displayed text.
1034    /// When text-wrapping is active, this is not enough. This gives
1035    /// an extra offset into the first row where rendering should start.
1036    ///
1037    /// You probably don't want to set this value. Use set_cursor()
1038    /// instead, it will automatically scroll to make the cursor visible.
1039    ///
1040    /// If you really want, pos_to_line_start() can help to find the
1041    /// start-position of a visual row. The x-value of the returned
1042    /// TextPosition is a valid value for this function.
1043    ///
1044    pub fn set_sub_row_offset(&mut self, sub_row_offset: upos_type) -> bool {
1045        self.scroll_to_cursor.set(false);
1046        let old = self.sub_row_offset;
1047        self.sub_row_offset = sub_row_offset;
1048        sub_row_offset != old
1049    }
1050
1051    /// Returns the extra offset into the first row where rendering
1052    /// starts. This is only valid if text-wrapping is active.
1053    ///
1054    /// Returns the index of the column.
1055    pub fn sub_row_offset(&self) -> upos_type {
1056        self.sub_row_offset
1057    }
1058
1059    /// This returns the triple (hscroll.offset, sub_row_offset, vscroll.offset )
1060    /// all trimmed to upos_type. sub_row_offset will only have a value if
1061    /// there is some text-wrapping active. hscroll.offset will only have
1062    /// an offset if there is *no* text-wrapping active.
1063    fn clean_offset(&self) -> (upos_type, upos_type, upos_type) {
1064        let ox = self.hscroll.offset as upos_type;
1065        let mut oy = self.vscroll.offset as upos_type;
1066
1067        // reset invalid offset
1068        if oy >= self.len_lines() {
1069            oy = 0;
1070        }
1071
1072        match self.text_wrap {
1073            TextWrap::Shift => (ox, 0, oy),
1074            TextWrap::Hard | TextWrap::Word(_) => {
1075                // sub_row_offset can be any value. limit somewhat.
1076                if let Ok(max_col) = self.try_line_width(oy) {
1077                    (0, min(self.sub_row_offset, max_col), oy)
1078                } else {
1079                    (0, 0, oy)
1080                }
1081            }
1082        }
1083    }
1084
1085    /// Cursor position.
1086    #[inline]
1087    pub fn cursor(&self) -> TextPosition {
1088        self.value.cursor()
1089    }
1090
1091    /// Set the cursor position and scroll the cursor to a visible offset.
1092    #[inline]
1093    pub fn set_cursor(&mut self, cursor: impl Into<TextPosition>, extend_selection: bool) -> bool {
1094        self.scroll_cursor_to_visible();
1095        self.value.set_cursor(cursor.into(), extend_selection)
1096    }
1097
1098    /// Selection anchor.
1099    #[inline]
1100    pub fn anchor(&self) -> TextPosition {
1101        self.value.anchor()
1102    }
1103
1104    /// Has a selection?
1105    #[inline]
1106    pub fn has_selection(&self) -> bool {
1107        self.value.has_selection()
1108    }
1109
1110    /// Current selection.
1111    #[inline]
1112    pub fn selection(&self) -> TextRange {
1113        self.value.selection()
1114    }
1115
1116    /// Set the selection, anchor and cursor are capped to a valid value.
1117    /// Scrolls the cursor to a visible position.
1118    #[inline]
1119    pub fn set_selection(
1120        &mut self,
1121        anchor: impl Into<TextPosition>,
1122        cursor: impl Into<TextPosition>,
1123    ) -> bool {
1124        self.scroll_cursor_to_visible();
1125        self.value.set_selection(anchor.into(), cursor.into())
1126    }
1127
1128    /// Select all.
1129    /// Scrolls the cursor to a visible position.
1130    #[inline]
1131    pub fn select_all(&mut self) -> bool {
1132        self.scroll_cursor_to_visible();
1133        self.value.select_all()
1134    }
1135
1136    /// Selected text.
1137    #[inline]
1138    pub fn selected_text(&self) -> Cow<'_, str> {
1139        self.value
1140            .str_slice(self.value.selection())
1141            .expect("valid_selection")
1142    }
1143}
1144
1145impl TextAreaState {
1146    /// Clear everything.
1147    #[inline]
1148    pub fn clear(&mut self) -> bool {
1149        if !self.is_empty() {
1150            self.value.clear();
1151            true
1152        } else {
1153            false
1154        }
1155    }
1156
1157    /// Set the text value.
1158    ///
1159    /// Resets all internal state.
1160    #[inline]
1161    pub fn set_text<S: AsRef<str>>(&mut self, s: S) {
1162        self.scroll_to_cursor.set(false);
1163        self.vscroll.set_offset(0);
1164        self.hscroll.set_offset(0);
1165        self.set_sub_row_offset(0);
1166        self.set_move_col(None);
1167
1168        self.value.set_text(TextRope::new_text(s.as_ref()));
1169    }
1170
1171    /// Copy of the text-value.
1172    #[inline]
1173    pub fn text(&self) -> String {
1174        self.value.text().string()
1175    }
1176
1177    /// Set the text value as a Rope.
1178    /// Resets all internal state.
1179    #[inline]
1180    pub fn set_rope(&mut self, r: Rope) {
1181        self.scroll_to_cursor.set(false);
1182        self.vscroll.set_offset(0);
1183        self.hscroll.set_offset(0);
1184        self.set_sub_row_offset(0);
1185        self.set_move_col(None);
1186
1187        self.value.set_text(TextRope::new_rope(r));
1188    }
1189
1190    /// Access the underlying rope.
1191    #[inline]
1192    pub fn rope(&self) -> &Rope {
1193        self.value.text().rope()
1194    }
1195
1196    /// Set the text value.
1197    ///
1198    /// Resets all internal state.
1199    #[inline]
1200    pub fn set_value<S: AsRef<str>>(&mut self, s: S) {
1201        self.set_text(s);
1202    }
1203
1204    /// Copy of the text-value.
1205    #[inline]
1206    pub fn value(&self) -> String {
1207        self.text()
1208    }
1209
1210    /// Empty.
1211    #[inline]
1212    pub fn is_empty(&self) -> bool {
1213        self.value.is_empty()
1214    }
1215
1216    /// Text slice as `Cow<str>`. Uses a byte range.
1217    ///
1218    /// Panics for an invalid range.
1219    #[inline]
1220    pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
1221        self.value.str_slice_byte(range).expect("valid_range")
1222    }
1223
1224    /// Text slice as `Cow<str>`. Uses a byte range.
1225    #[inline]
1226    pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
1227        self.value.str_slice_byte(range)
1228    }
1229
1230    /// Text slice as `Cow<str>`
1231    ///
1232    /// Panics for an invalid range.
1233    #[inline]
1234    pub fn str_slice(&self, range: impl Into<TextRange>) -> Cow<'_, str> {
1235        self.value.str_slice(range.into()).expect("valid_range")
1236    }
1237
1238    /// Text slice as `Cow<str>`
1239    #[inline]
1240    pub fn try_str_slice(&self, range: impl Into<TextRange>) -> Result<Cow<'_, str>, TextError> {
1241        self.value.str_slice(range.into())
1242    }
1243
1244    /// Line count.
1245    #[inline]
1246    pub fn len_lines(&self) -> upos_type {
1247        self.value.len_lines()
1248    }
1249
1250    /// Length in bytes.
1251    #[inline]
1252    pub fn len_bytes(&self) -> usize {
1253        self.value.len_bytes()
1254    }
1255
1256    /// Line width as grapheme count.
1257    ///
1258    /// Panics for an invalid row.
1259    #[inline]
1260    pub fn line_width(&self, row: upos_type) -> upos_type {
1261        self.try_line_width(row).expect("valid_row")
1262    }
1263
1264    /// Line width as grapheme count.
1265    #[inline]
1266    pub fn try_line_width(&self, row: upos_type) -> Result<upos_type, TextError> {
1267        self.value.line_width(row)
1268    }
1269
1270    /// Line as `Cow<str>`.
1271    /// This contains the \n at the end.
1272    ///
1273    /// Panics for an invalid row.
1274    #[inline]
1275    pub fn line_at(&self, row: upos_type) -> Cow<'_, str> {
1276        self.value.line_at(row).expect("valid_row")
1277    }
1278
1279    /// Line as `Cow<str>`.
1280    /// This contains the \n at the end.
1281    #[inline]
1282    pub fn try_line_at(&self, row: upos_type) -> Result<Cow<'_, str>, TextError> {
1283        self.value.line_at(row)
1284    }
1285
1286    /// Iterate over text-lines, starting at offset.
1287    ///
1288    /// Panics for an invalid row.
1289    #[inline]
1290    pub fn lines_at(&self, row: upos_type) -> impl Iterator<Item = Cow<'_, str>> {
1291        self.value.lines_at(row).expect("valid_row")
1292    }
1293
1294    /// Iterate over text-lines, starting at offset.
1295    #[inline]
1296    pub fn try_lines_at(
1297        &self,
1298        row: upos_type,
1299    ) -> Result<impl Iterator<Item = Cow<'_, str>>, TextError> {
1300        self.value.lines_at(row)
1301    }
1302
1303    /// Grapheme iterator for a given line.
1304    /// This contains the \n at the end.
1305    ///
1306    /// Panics for an invalid row.
1307    #[inline]
1308    pub fn line_graphemes(&self, row: upos_type) -> <TextRope as TextStore>::GraphemeIter<'_> {
1309        self.value.line_graphemes(row).expect("valid_row")
1310    }
1311
1312    /// Grapheme iterator for a given line.
1313    /// This contains the \n at the end.
1314    #[inline]
1315    pub fn try_line_graphemes(
1316        &self,
1317        row: upos_type,
1318    ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1319        self.value.line_graphemes(row)
1320    }
1321
1322    /// Get a cursor over all the text with the current position set at pos.
1323    ///
1324    /// Panics for an invalid pos.
1325    #[inline]
1326    pub fn text_graphemes(
1327        &self,
1328        pos: impl Into<TextPosition>,
1329    ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1330        self.value.text_graphemes(pos.into()).expect("valid_pos")
1331    }
1332
1333    /// Get a cursor over all the text with the current position set at pos.
1334    #[inline]
1335    pub fn try_text_graphemes(
1336        &self,
1337        pos: impl Into<TextPosition>,
1338    ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1339        self.value.text_graphemes(pos.into())
1340    }
1341
1342    /// Get a cursor over the text-range the current position set at pos.
1343    ///
1344    /// Panics for an invalid pos.
1345    #[inline]
1346    pub fn graphemes(
1347        &self,
1348        range: impl Into<TextRange>,
1349        pos: impl Into<TextPosition>,
1350    ) -> <TextRope as TextStore>::GraphemeIter<'_> {
1351        self.value
1352            .graphemes(range.into(), pos.into())
1353            .expect("valid_args")
1354    }
1355
1356    /// Get a cursor over the text-range the current position set at pos.
1357    #[inline]
1358    pub fn try_graphemes(
1359        &self,
1360        range: impl Into<TextRange>,
1361        pos: impl Into<TextPosition>,
1362    ) -> Result<<TextRope as TextStore>::GraphemeIter<'_>, TextError> {
1363        self.value.graphemes(range.into(), pos.into())
1364    }
1365
1366    /// Grapheme position to byte position.
1367    /// This is the (start,end) position of the single grapheme after pos.
1368    ///
1369    /// Panics for an invalid pos.
1370    #[inline]
1371    pub fn byte_at(&self, pos: impl Into<TextPosition>) -> Range<usize> {
1372        self.value.byte_at(pos.into()).expect("valid_pos")
1373    }
1374
1375    /// Grapheme position to byte position.
1376    /// This is the (start,end) position of the single grapheme after pos.
1377    #[inline]
1378    pub fn try_byte_at(&self, pos: impl Into<TextPosition>) -> Result<Range<usize>, TextError> {
1379        self.value.byte_at(pos.into())
1380    }
1381
1382    /// Grapheme range to byte range.
1383    ///
1384    /// Panics for an invalid range.
1385    #[inline]
1386    pub fn bytes_at_range(&self, range: impl Into<TextRange>) -> Range<usize> {
1387        self.value
1388            .bytes_at_range(range.into())
1389            .expect("valid_range")
1390    }
1391
1392    /// Grapheme range to byte range.
1393    #[inline]
1394    pub fn try_bytes_at_range(
1395        &self,
1396        range: impl Into<TextRange>,
1397    ) -> Result<Range<usize>, TextError> {
1398        self.value.bytes_at_range(range.into())
1399    }
1400
1401    /// Byte position to grapheme position.
1402    /// Returns the position that contains the given byte index.
1403    ///
1404    /// Panics for an invalid byte pos.
1405    #[inline]
1406    pub fn byte_pos(&self, byte: usize) -> TextPosition {
1407        self.value.byte_pos(byte).expect("valid_pos")
1408    }
1409
1410    /// Byte position to grapheme position.
1411    /// Returns the position that contains the given byte index.
1412    #[inline]
1413    pub fn try_byte_pos(&self, byte: usize) -> Result<TextPosition, TextError> {
1414        self.value.byte_pos(byte)
1415    }
1416
1417    /// Byte range to grapheme range.
1418    ///
1419    /// Panics for an invalid range.
1420    #[inline]
1421    pub fn byte_range(&self, bytes: Range<usize>) -> TextRange {
1422        self.value.byte_range(bytes).expect("valid_range")
1423    }
1424
1425    /// Byte range to grapheme range.
1426    #[inline]
1427    pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<TextRange, TextError> {
1428        self.value.byte_range(bytes)
1429    }
1430}
1431
1432impl TextAreaState {
1433    /// Insert a character at the cursor position.
1434    /// Removes the selection and inserts the char.
1435    ///
1436    /// This function can handle '\t' and '\n' well.
1437    /// They call insert_tab() and insert_newline() respectively.
1438    #[inline]
1439    pub fn insert_char(&mut self, c: char) -> bool {
1440        match c {
1441            '\n' => text_area_op::insert_newline(self),
1442            '\t' => text_area_op::insert_tab(self),
1443            c => text_area_op::insert_char(self, c),
1444        }
1445    }
1446
1447    /// Inserts tab at the current position. This respects the
1448    /// tab-width set.
1449    ///
1450    /// If there is a text-selection the text-rows will be indented instead.
1451    /// This can be deactivated with auto_indent=false.
1452    #[inline]
1453    pub fn insert_tab(&mut self) -> bool {
1454        text_area_op::insert_tab(self)
1455    }
1456
1457    /// Dedent the selected text by tab-width. If there is no
1458    /// selection this does nothing.
1459    ///
1460    /// This can be deactivated with auto_indent=false.
1461    #[inline]
1462    pub fn insert_backtab(&mut self) -> bool {
1463        text_area_op::insert_backtab(self)
1464    }
1465
1466    /// Insert text at the cursor position.
1467    /// Removes the selection and inserts the text.
1468    #[inline]
1469    pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1470        text_area_op::insert_str(self, t.as_ref())
1471    }
1472
1473    /// Insert a line break at the cursor position.
1474    ///
1475    /// If auto_indent is set the new line starts with the same
1476    /// indent as the current.
1477    #[inline]
1478    pub fn insert_newline(&mut self) -> bool {
1479        text_area_op::insert_newline(self)
1480    }
1481
1482    /// Deletes the given range.
1483    ///
1484    /// Panics for an invalid range.
1485    #[inline]
1486    pub fn delete_range(&mut self, range: impl Into<TextRange>) -> bool {
1487        text_area_op::delete_range(self, range.into()).expect("valid_range")
1488    }
1489
1490    /// Deletes the given range.
1491    #[inline]
1492    pub fn try_delete_range(&mut self, range: impl Into<TextRange>) -> Result<bool, TextError> {
1493        text_area_op::delete_range(self, range.into())
1494    }
1495
1496    /// Duplicates the selection or the current line.
1497    /// Returns true if there was any real change.
1498    #[inline]
1499    pub fn duplicate_text(&mut self) -> bool {
1500        text_area_op::duplicate_text(self)
1501    }
1502
1503    /// Deletes the current line.
1504    /// Returns true if there was any real change.
1505    #[inline]
1506    pub fn delete_line(&mut self) -> bool {
1507        text_area_op::delete_line(self)
1508    }
1509
1510    /// Deletes the next char or the current selection.
1511    /// Returns true if there was any real change.
1512    #[inline]
1513    pub fn delete_next_char(&mut self) -> bool {
1514        text_area_op::delete_next_char(self)
1515    }
1516
1517    /// Deletes the previous char or the selection.
1518    /// Returns true if there was any real change.
1519    #[inline]
1520    pub fn delete_prev_char(&mut self) -> bool {
1521        text_area_op::delete_prev_char(self)
1522    }
1523
1524    /// Find the start of the next word. If the position is at the start
1525    /// or inside a word, the same position is returned.
1526    ///
1527    /// Panics for an invalid pos.
1528    #[inline]
1529    pub fn next_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1530        core_op::next_word_start(&self.value, pos.into()).expect("valid_pos")
1531    }
1532
1533    /// Find the start of the next word. If the position is at the start
1534    /// or inside a word, the same position is returned.
1535    #[inline]
1536    pub fn try_next_word_start(
1537        &self,
1538        pos: impl Into<TextPosition>,
1539    ) -> Result<TextPosition, TextError> {
1540        core_op::next_word_start(&self.value, pos.into())
1541    }
1542
1543    /// Find the end of the next word. Skips whitespace first, then goes on
1544    /// until it finds the next whitespace.
1545    ///
1546    /// Panics for an invalid pos.
1547    #[inline]
1548    pub fn next_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1549        core_op::next_word_end(&self.value, pos.into()).expect("valid_pos")
1550    }
1551
1552    /// Find the end of the next word. Skips whitespace first, then goes on
1553    /// until it finds the next whitespace.
1554    #[inline]
1555    pub fn try_next_word_end(
1556        &self,
1557        pos: impl Into<TextPosition>,
1558    ) -> Result<TextPosition, TextError> {
1559        core_op::next_word_end(&self.value, pos.into())
1560    }
1561
1562    /// Find the start of the prev word. Skips whitespace first, then goes on
1563    /// until it finds the next whitespace.
1564    ///
1565    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1566    /// both return start<=end!
1567    ///
1568    /// Panics for an invalid range.
1569    #[inline]
1570    pub fn prev_word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1571        core_op::prev_word_start(&self.value, pos.into()).expect("valid_pos")
1572    }
1573
1574    /// Find the start of the prev word. Skips whitespace first, then goes on
1575    /// until it finds the next whitespace.
1576    ///
1577    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1578    /// both return start<=end!
1579    #[inline]
1580    pub fn try_prev_word_start(
1581        &self,
1582        pos: impl Into<TextPosition>,
1583    ) -> Result<TextPosition, TextError> {
1584        core_op::prev_word_start(&self.value, pos.into())
1585    }
1586
1587    /// Find the end of the previous word. Word is everything that is not whitespace.
1588    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1589    /// both return start<=end!
1590    ///
1591    /// Panics for an invalid range.
1592    #[inline]
1593    pub fn prev_word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1594        core_op::prev_word_end(&self.value, pos.into()).expect("valid_pos")
1595    }
1596
1597    /// Find the end of the previous word. Word is everything that is not whitespace.
1598    /// Attention: start/end are mirrored here compared to next_word_start/next_word_end,
1599    /// both return start<=end!
1600    #[inline]
1601    pub fn try_prev_word_end(
1602        &self,
1603        pos: impl Into<TextPosition>,
1604    ) -> Result<TextPosition, TextError> {
1605        core_op::prev_word_end(&self.value, pos.into())
1606    }
1607
1608    /// Is the position at a word boundary?
1609    ///
1610    /// Panics for an invalid range.
1611    #[inline]
1612    pub fn is_word_boundary(&self, pos: impl Into<TextPosition>) -> bool {
1613        core_op::is_word_boundary(&self.value, pos.into()).expect("valid_pos")
1614    }
1615
1616    /// Is the position at a word boundary?
1617    #[inline]
1618    pub fn try_is_word_boundary(&self, pos: impl Into<TextPosition>) -> Result<bool, TextError> {
1619        core_op::is_word_boundary(&self.value, pos.into())
1620    }
1621
1622    /// Find the start of the word at pos.
1623    /// Returns pos if the position is not inside a word.
1624    ///
1625    /// Panics for an invalid range.
1626    #[inline]
1627    pub fn word_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
1628        core_op::word_start(&self.value, pos.into()).expect("valid_pos")
1629    }
1630
1631    /// Find the start of the word at pos.
1632    /// Returns pos if the position is not inside a word.
1633    #[inline]
1634    pub fn try_word_start(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1635        core_op::word_start(&self.value, pos.into())
1636    }
1637
1638    /// Find the end of the word at pos.
1639    /// Returns pos if the position is not inside a word.
1640    ///
1641    /// Panics for an invalid range.
1642    #[inline]
1643    pub fn word_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
1644        core_op::word_end(&self.value, pos.into()).expect("valid_pos")
1645    }
1646
1647    /// Find the end of the word at pos.
1648    /// Returns pos if the position is not inside a word.
1649    #[inline]
1650    pub fn try_word_end(&self, pos: impl Into<TextPosition>) -> Result<TextPosition, TextError> {
1651        core_op::word_end(&self.value, pos.into())
1652    }
1653
1654    /// Delete the next word. This alternates deleting the whitespace between words and
1655    /// the words themselves.
1656    ///
1657    /// If there is a selection, removes only the selected text.
1658    #[inline]
1659    pub fn delete_next_word(&mut self) -> bool {
1660        text_area_op::delete_next_word(self)
1661    }
1662
1663    /// Deletes the previous word. This alternates deleting the whitespace
1664    /// between words and the words themselves.
1665    ///
1666    /// If there is a selection, removes only the selected text.
1667    #[inline]
1668    pub fn delete_prev_word(&mut self) -> bool {
1669        text_area_op::delete_prev_word(self)
1670    }
1671
1672    /// Search for a regex.
1673    ///
1674    /// Uses match_style for highlighting the matches.
1675    /// This doesn't change the cursor/selection, use [move_to_next_match] or
1676    /// [move_to_prev_match] for this.
1677    ///
1678    /// Return
1679    ///
1680    /// Returns true if the search found anything.
1681    ///
1682    pub fn search(&mut self, re: &str) -> Result<bool, TextError> {
1683        match text_area_op::search(self, re, MATCH_STYLE) {
1684            Ok(r) => Ok(r),
1685            Err(_) => Err(TextError::InvalidSearch),
1686        }
1687    }
1688
1689    /// Move to the next match.
1690    pub fn move_to_next_match(&mut self) -> bool {
1691        text_area_op::move_to_next_match(self, MATCH_STYLE)
1692    }
1693
1694    /// Move to the next match.
1695    pub fn move_to_prev_match(&mut self) -> bool {
1696        text_area_op::move_to_prev_match(self, MATCH_STYLE)
1697    }
1698
1699    /// Clear the search.
1700    pub fn clear_search(&mut self) {
1701        text_area_op::clear_search(self, MATCH_STYLE)
1702    }
1703
1704    /// Move the cursor left. Scrolls the cursor to visible.
1705    /// Returns true if there was any real change.
1706    #[inline]
1707    pub fn move_left(&mut self, n: u16, extend_selection: bool) -> bool {
1708        text_area_op::move_left(self, n, extend_selection)
1709    }
1710
1711    /// Move the cursor right. Scrolls the cursor to visible.
1712    /// Returns true if there was any real change.
1713    #[inline]
1714    pub fn move_right(&mut self, n: u16, extend_selection: bool) -> bool {
1715        text_area_op::move_right(self, n, extend_selection)
1716    }
1717
1718    /// Move the cursor up. Scrolls the cursor to visible.
1719    /// Returns true if there was any real change.
1720    #[inline]
1721    pub fn move_up(&mut self, n: u16, extend_selection: bool) -> bool {
1722        text_area_op::move_up(self, n, extend_selection)
1723    }
1724
1725    /// Move the cursor down. Scrolls the cursor to visible.
1726    /// Returns true if there was any real change.
1727    #[inline]
1728    pub fn move_down(&mut self, n: u16, extend_selection: bool) -> bool {
1729        text_area_op::move_down(self, n, extend_selection)
1730    }
1731
1732    /// Move the cursor to the start of the line.
1733    /// Scrolls the cursor to visible.
1734    /// Returns true if there was any real change.
1735    #[inline]
1736    pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1737        text_area_op::move_to_line_start(self, extend_selection)
1738    }
1739
1740    /// Move the cursor to the end of the line. Scrolls to visible, if
1741    /// necessary.
1742    /// Returns true if there was any real change.
1743    #[inline]
1744    pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1745        text_area_op::move_to_line_end(self, extend_selection)
1746    }
1747
1748    /// Move the cursor to the document start.
1749    #[inline]
1750    pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
1751        text_area_op::move_to_start(self, extend_selection)
1752    }
1753
1754    /// Move the cursor to the document end.
1755    #[inline]
1756    pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
1757        text_area_op::move_to_end(self, extend_selection)
1758    }
1759
1760    /// Move the cursor to the start of the visible area.
1761    #[inline]
1762    pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
1763        text_area_op::move_to_screen_start(self, extend_selection)
1764    }
1765
1766    /// Move the cursor to the end of the visible area.
1767    #[inline]
1768    pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
1769        text_area_op::move_to_screen_end(self, extend_selection)
1770    }
1771
1772    /// Move the cursor to the next word.
1773    #[inline]
1774    pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1775        text_area_op::move_to_next_word(self, extend_selection)
1776    }
1777
1778    /// Move the cursor to the previous word.
1779    #[inline]
1780    pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1781        text_area_op::move_to_prev_word(self, extend_selection)
1782    }
1783}
1784
1785impl HasScreenCursor for TextAreaState {
1786    /// Cursor position on the screen.
1787    #[allow(clippy::question_mark)]
1788    fn screen_cursor(&self) -> Option<(u16, u16)> {
1789        if self.is_focused() {
1790            if self.has_selection() {
1791                None
1792            } else {
1793                let Some(scr_cursor) = self.screen_cursor else {
1794                    return None;
1795                };
1796
1797                if !(scr_cursor.0 >= self.inner.x
1798                    && scr_cursor.0 <= self.inner.right()
1799                    && scr_cursor.1 >= self.inner.y
1800                    && scr_cursor.1 < self.inner.bottom())
1801                {
1802                    return None;
1803                }
1804                Some(scr_cursor)
1805            }
1806        } else {
1807            None
1808        }
1809    }
1810}
1811
1812impl RelocatableState for TextAreaState {
1813    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1814        // clip offset for some corrections.
1815        self.area.relocate(shift, clip);
1816        self.inner.relocate(shift, clip);
1817        self.hscroll.relocate(shift, clip);
1818        self.vscroll.relocate(shift, clip);
1819        self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1820        if let Some(screen_cursor) = self.screen_cursor {
1821            self.screen_cursor = relocate_pos_tuple(screen_cursor, shift, clip);
1822        }
1823    }
1824}
1825
1826impl TextAreaState {
1827    // validated text-wrap parameters.
1828    fn text_wrap_2(&self, shift_left: upos_type) -> (TextWrap2, upos_type, upos_type, upos_type) {
1829        match self.text_wrap {
1830            TextWrap::Shift => (
1831                TextWrap2::Shift,
1832                shift_left,
1833                shift_left + self.rendered.width as upos_type,
1834                shift_left + self.rendered.width as upos_type,
1835            ),
1836            TextWrap::Hard => (
1837                TextWrap2::Hard,
1838                0,
1839                self.rendered.width as upos_type,
1840                self.rendered.width as upos_type,
1841            ),
1842            TextWrap::Word(margin) => (
1843                TextWrap2::Word,
1844                0,
1845                self.rendered.width as upos_type,
1846                self.rendered.width.saturating_sub(margin) as upos_type,
1847            ),
1848        }
1849    }
1850
1851    // Fill the cache for the given rows.
1852    // Build up the complete information for the given rows.
1853    fn fill_cache(
1854        &self,
1855        shift_left: upos_type,
1856        sub_row_offset: upos_type,
1857        rows: Range<upos_type>,
1858    ) -> Result<(), TextError> {
1859        let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1860        self.value.fill_cache(
1861            self.rendered,
1862            sub_row_offset,
1863            rows,
1864            self.tab_width,
1865            text_wrap,
1866            self.wrap_ctrl() | self.show_ctrl(),
1867            left_margin,
1868            right_margin,
1869            word_margin,
1870        )
1871    }
1872
1873    // glyph iterator
1874    fn glyphs2(
1875        &self,
1876        shift_left: upos_type,
1877        sub_row_offset: upos_type,
1878        rows: Range<upos_type>,
1879    ) -> Result<GlyphIter2<'_, <TextRope as TextStore>::GraphemeIter<'_>>, TextError> {
1880        let (text_wrap, left_margin, right_margin, word_margin) = self.text_wrap_2(shift_left);
1881        self.value.glyphs2(
1882            self.rendered,
1883            sub_row_offset,
1884            rows,
1885            self.tab_width,
1886            text_wrap,
1887            self.wrap_ctrl() | self.show_ctrl(),
1888            left_margin,
1889            right_margin,
1890            word_margin,
1891        )
1892    }
1893
1894    // ensure cache is up to date for n pages.
1895    //
1896    // * scr: (sub_row_offset, offset_row, page_len)
1897    fn stc_fill_screen_cache(&self, scr: (upos_type, upos_type, upos_type)) {
1898        let y2 = scr.1 + scr.2;
1899        self.fill_cache(0, scr.0, scr.1..min(y2, self.len_lines()))
1900            .expect("valid-rows");
1901    }
1902
1903    // screen row for the given position.
1904    // only if within `scr.2` lines.
1905    //
1906    // requires: stc_fill_screen_cache for this `scr` value.
1907    //
1908    // * scr: (sub_row_offset, offset_row, page_len)
1909    fn stc_screen_row(
1910        &self,
1911        scr: (upos_type, upos_type, upos_type),
1912        pos: TextPosition,
1913    ) -> Option<upos_type> {
1914        if pos < TextPosition::new(scr.0, scr.1) {
1915            return None;
1916        }
1917
1918        let line_breaks = self.value.cache().line_break.borrow();
1919        let range_start = TextPosition::new(scr.0, scr.1);
1920        let y2 = scr.1 + scr.2;
1921        let range_end = TextPosition::new(0, min(y2, self.len_lines()));
1922
1923        let mut start_pos = TextPosition::new(scr.0, scr.1);
1924        let mut scr_row = 0;
1925        for (_key, cache) in line_breaks.range(range_start..range_end) {
1926            if pos < cache.start_pos {
1927                return Some(scr_row);
1928            }
1929            scr_row += 1;
1930            start_pos = cache.start_pos;
1931        }
1932
1933        // very last position on the very last row without a \n
1934        if pos == start_pos {
1935            return Some(scr_row);
1936        }
1937
1938        None
1939    }
1940
1941    // start offset for given screen-row.
1942    // only if within `scr.2` lines.
1943    //
1944    // requires: stc_fill_screen_cache for this `scr` value.
1945    //
1946    // * scr: (sub_row_offset, offset_row, page_len)
1947    fn stc_sub_row_offset(
1948        &self,
1949        scr: (upos_type, upos_type, upos_type),
1950        mut scr_row: upos_type,
1951    ) -> (upos_type, upos_type) {
1952        let line_breaks = self.value.cache().line_break.borrow();
1953        let range_start = TextPosition::new(scr.0, scr.1);
1954        let y2 = scr.1 + scr.2;
1955        let range_end = TextPosition::new(0, min(y2, self.len_lines()));
1956
1957        let mut start_pos = (scr.0, scr.1);
1958        for (_key, cache) in line_breaks.range(range_start..range_end) {
1959            if scr_row == 0 {
1960                return start_pos;
1961            }
1962            scr_row -= 1;
1963            start_pos = (cache.start_pos.x, cache.start_pos.y);
1964        }
1965
1966        // actual data is shorter than expected.
1967        start_pos
1968    }
1969}
1970
1971impl TextAreaState {
1972    /// Find the text-position for the widget-relative screen-position.
1973    #[allow(clippy::needless_return)]
1974    pub fn relative_screen_to_pos(&self, scr_pos: (i16, i16)) -> Option<TextPosition> {
1975        let scr_pos = (
1976            scr_pos.0 + self.dark_offset.0 as i16,
1977            scr_pos.1 + self.dark_offset.1 as i16,
1978        );
1979
1980        match self.text_wrap {
1981            TextWrap::Shift => {
1982                let (ox, _, oy) = self.clean_offset();
1983
1984                if oy >= self.len_lines() {
1985                    return None;
1986                }
1987
1988                if scr_pos.1 < 0 {
1989                    // before the first visible line. fall back to col 0.
1990                    return Some(TextPosition::new(
1991                        0,
1992                        oy.saturating_add_signed(scr_pos.1 as ipos_type),
1993                    ));
1994                } else if (oy + scr_pos.1 as upos_type) >= self.len_lines() {
1995                    // after the last visible line. fall back to col 0.
1996                    return Some(TextPosition::new(0, self.len_lines().saturating_sub(1)));
1997                }
1998
1999                let pos_y = oy + scr_pos.1 as upos_type;
2000
2001                if scr_pos.0 < 0 {
2002                    return Some(TextPosition::new(
2003                        ox.saturating_add_signed(scr_pos.0 as ipos_type),
2004                        pos_y,
2005                    ));
2006                } else if scr_pos.0 as u16 >= self.rendered.width {
2007                    return Some(TextPosition::new(
2008                        min(ox + scr_pos.0 as upos_type, self.line_width(pos_y)),
2009                        pos_y,
2010                    ));
2011                } else {
2012                    let mut start_pos = TextPosition::new(0, pos_y);
2013                    for g in self
2014                        .glyphs2(ox, 0, pos_y..min(pos_y + 1, self.len_lines()))
2015                        .expect("valid-position")
2016                    {
2017                        if g.contains_screen_x(scr_pos.0 as u16) {
2018                            return Some(TextPosition::new(g.pos().x, pos_y));
2019                        }
2020                        start_pos = g.pos();
2021                    }
2022                    Some(start_pos)
2023                }
2024            }
2025            TextWrap::Hard | TextWrap::Word(_) => {
2026                let (_, sub_row_offset, oy) = self.clean_offset();
2027
2028                if oy >= self.len_lines() {
2029                    return None;
2030                }
2031
2032                if scr_pos.1 < 0 {
2033                    // Guess a starting position for an alternate screen that
2034                    // would contain the given screen-position.
2035                    // By locating our actual offset within that screen we can
2036                    // calculate the correct screen-position for that alternate
2037                    // screen. And then find the correct text-position for
2038                    // that again.
2039
2040                    // estimate start row
2041                    let ry = oy.saturating_add_signed(scr_pos.1 as ipos_type - 1);
2042
2043                    self.fill_cache(0, 0, ry..oy).expect("valid-rows");
2044
2045                    let n_start_pos = 'f: {
2046                        let line_break = self.value.cache().line_break.borrow();
2047                        let start_range = TextPosition::new(0, ry);
2048                        let end_range = TextPosition::new(sub_row_offset, oy);
2049
2050                        let mut nrows = scr_pos.1.unsigned_abs();
2051                        for (_break_pos, cache) in line_break.range(start_range..end_range).rev() {
2052                            if nrows == 0 {
2053                                break 'f cache.start_pos;
2054                            }
2055                            nrows -= 1;
2056                        }
2057                        TextPosition::new(0, ry)
2058                    };
2059
2060                    // find the exact col
2061                    if scr_pos.0 < 0 {
2062                        return Some(n_start_pos);
2063                    }
2064
2065                    let min_row = n_start_pos.y;
2066                    let max_row = min(n_start_pos.y + 1, self.len_lines());
2067                    for g in self
2068                        .glyphs2(0, n_start_pos.x, min_row..max_row)
2069                        .expect("valid-rows")
2070                    {
2071                        if g.contains_screen_x(scr_pos.0 as u16) {
2072                            return Some(g.pos());
2073                        }
2074                    }
2075
2076                    // beyond the last line
2077                    return Some(n_start_pos);
2078                } else {
2079                    let scr_pos = (max(0, scr_pos.0) as u16, scr_pos.1 as u16);
2080
2081                    // row-0 equals the current offset. done.
2082                    let n_start_pos = if scr_pos.1 == 0 {
2083                        TextPosition::new(sub_row_offset, oy)
2084                    } else {
2085                        // start at the offset and find the screen-position.
2086                        self.fill_cache(
2087                            0,
2088                            sub_row_offset,
2089                            oy..min(oy + scr_pos.1 as upos_type, self.len_lines()),
2090                        )
2091                        .expect("valid-rows");
2092
2093                        'f: {
2094                            let text_range = self.value.cache().line_break.borrow();
2095                            let start_range = TextPosition::new(sub_row_offset, oy);
2096                            let end_range = TextPosition::new(0, self.len_lines());
2097
2098                            let mut nrows = scr_pos.1 - 1;
2099                            let mut start_pos = TextPosition::new(sub_row_offset, oy);
2100                            for (_break_pos, cache) in text_range.range(start_range..end_range) {
2101                                if nrows == 0 {
2102                                    break 'f cache.start_pos;
2103                                }
2104                                start_pos = cache.start_pos;
2105                                nrows -= 1;
2106                            }
2107                            start_pos
2108                        }
2109                    };
2110
2111                    let min_row = n_start_pos.y;
2112                    let max_row = min(n_start_pos.y + 1, self.len_lines());
2113                    for g in self
2114                        .glyphs2(0, n_start_pos.x, min_row..max_row)
2115                        .expect("valid-rows")
2116                    {
2117                        if g.contains_screen_x(scr_pos.0) {
2118                            return Some(g.pos());
2119                        }
2120                    }
2121
2122                    // beyond the last line
2123                    return Some(n_start_pos);
2124                }
2125            }
2126        }
2127    }
2128
2129    /// Find the text-position for an absolute screen-position.
2130    pub fn screen_to_pos(&self, scr_pos: (u16, u16)) -> Option<TextPosition> {
2131        let scr_pos = (
2132            scr_pos.0 as i16 - self.inner.x as i16,
2133            scr_pos.1 as i16 - self.inner.y as i16,
2134        );
2135        self.relative_screen_to_pos(scr_pos)
2136    }
2137
2138    /// Return the screen_position for the given text position
2139    /// relative to the origin of the widget.
2140    ///
2141    /// This may be outside the visible area, if the text-area
2142    /// has been relocated. It may even be outside the screen,
2143    /// so this returns an (i16, i16).
2144    ///
2145    /// If the text-position is outside the rendered area,
2146    /// this will return None.
2147    #[allow(clippy::explicit_counter_loop)]
2148    pub fn pos_to_relative_screen(&self, pos: impl Into<TextPosition>) -> Option<(i16, i16)> {
2149        let pos = pos.into();
2150        match self.text_wrap {
2151            TextWrap::Shift => {
2152                let (ox, _, oy) = self.clean_offset();
2153
2154                if oy > self.len_lines() {
2155                    return None;
2156                }
2157                if pos.y < oy {
2158                    return None;
2159                }
2160                if pos.y > self.len_lines() {
2161                    return None;
2162                }
2163                if pos.y - oy >= self.rendered.height as u32 {
2164                    return None;
2165                }
2166
2167                let screen_y = (pos.y - oy) as u16;
2168
2169                let screen_x = 'f: {
2170                    for g in self
2171                        .glyphs2(ox, 0, pos.y..min(pos.y + 1, self.len_lines()))
2172                        .expect("valid-row")
2173                    {
2174                        if g.pos().x == pos.x {
2175                            break 'f g.screen_pos().0;
2176                        } else if g.line_break() {
2177                            break 'f g.screen_pos().0;
2178                        }
2179                    }
2180                    // last row
2181                    0
2182                };
2183                assert!(screen_x <= self.rendered.width);
2184
2185                Some((
2186                    screen_x as i16 - self.dark_offset.0 as i16,
2187                    screen_y as i16 - self.dark_offset.1 as i16,
2188                ))
2189            }
2190            TextWrap::Hard | TextWrap::Word(_) => {
2191                let (_, sub_row_offset, oy) = self.clean_offset();
2192
2193                if oy > self.len_lines() {
2194                    return None;
2195                }
2196                if pos.y < oy {
2197                    return None;
2198                }
2199                if pos.y > self.len_lines() {
2200                    return None;
2201                }
2202
2203                let page = self.rendered.height as upos_type;
2204                let scr = (sub_row_offset, oy, page);
2205                self.stc_fill_screen_cache(scr);
2206                let (screen_y, start_pos) = if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2207                    if pos_row >= page {
2208                        // beyond page
2209                        return None;
2210                    }
2211                    let start_pos = self.stc_sub_row_offset(scr, pos_row);
2212                    (pos_row, start_pos)
2213                } else {
2214                    // out of bounds
2215                    return None;
2216                };
2217
2218                let screen_x = 'f: {
2219                    for g in self
2220                        .glyphs2(
2221                            0,
2222                            start_pos.0,
2223                            start_pos.1..min(start_pos.1 + 1, self.len_lines()),
2224                        )
2225                        .expect("valid-row")
2226                    {
2227                        if g.pos().x == pos.x {
2228                            break 'f g.screen_pos().0;
2229                        } else if g.line_break() {
2230                            break 'f g.screen_pos().0;
2231                        }
2232                    }
2233                    // no glyphs on this line
2234                    0
2235                };
2236                assert!(screen_x <= self.rendered.width);
2237
2238                let scr = (
2239                    screen_x as i16 - self.dark_offset.0 as i16,
2240                    screen_y as i16 - self.dark_offset.1 as i16,
2241                );
2242                Some(scr)
2243            }
2244        }
2245    }
2246
2247    /// Find the absolute screen-position for a text-position.
2248    ///
2249    #[inline]
2250    pub fn pos_to_screen(&self, pos: impl Into<TextPosition>) -> Option<(u16, u16)> {
2251        let scr_pos = self.pos_to_relative_screen(pos.into())?;
2252        if scr_pos.0 + self.inner.x as i16 > 0 && scr_pos.1 + self.inner.y as i16 > 0 {
2253            Some((
2254                (scr_pos.0 + self.inner.x as i16) as u16,
2255                (scr_pos.1 + self.inner.y as i16) as u16,
2256            ))
2257        } else {
2258            None
2259        }
2260    }
2261
2262    /// Return the starting position for the visible line containing the given position.
2263    pub fn pos_to_line_start(&self, pos: impl Into<TextPosition>) -> TextPosition {
2264        let pos = pos.into();
2265        match self.text_wrap {
2266            TextWrap::Shift => {
2267                //
2268                TextPosition::new(0, pos.y)
2269            }
2270            TextWrap::Hard | TextWrap::Word(_) => {
2271                self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2272                    .expect("valid-row");
2273
2274                let mut start_pos = TextPosition::new(0, pos.y);
2275                for (break_pos, _) in self.value.cache().line_break.borrow().range(
2276                    TextPosition::new(0, pos.y)
2277                        ..TextPosition::new(0, min(pos.y + 1, self.len_lines())),
2278                ) {
2279                    if pos >= start_pos && &pos <= break_pos {
2280                        break;
2281                    }
2282                    start_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2283                }
2284
2285                start_pos
2286            }
2287        }
2288    }
2289
2290    /// Return the end position for the visible line containing the given position.
2291    pub fn pos_to_line_end(&self, pos: impl Into<TextPosition>) -> TextPosition {
2292        let pos = pos.into();
2293
2294        self.fill_cache(0, 0, pos.y..min(pos.y + 1, self.len_lines()))
2295            .expect("valid-row");
2296
2297        let mut end_pos = TextPosition::new(0, pos.y);
2298        for (break_pos, _) in self
2299            .value
2300            .cache()
2301            .line_break
2302            .borrow()
2303            .range(TextPosition::new(0, pos.y)..TextPosition::new(0, pos.y + 1))
2304        {
2305            if pos >= end_pos && &pos <= break_pos {
2306                end_pos = *break_pos;
2307                break;
2308            }
2309            end_pos = TextPosition::new(break_pos.x + 1, break_pos.y);
2310        }
2311
2312        end_pos
2313    }
2314
2315    /// Set the cursor position from screen coordinates.
2316    ///
2317    /// The cursor positions are relative to the inner rect.
2318    /// They may be negative too, this allows setting the cursor
2319    /// to a position that is currently scrolled away.
2320    pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2321        let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2322            return false;
2323        };
2324        if let Some(scr_cursor) = self.pos_to_relative_screen(cursor) {
2325            self.set_move_col(Some(scr_cursor.0));
2326        }
2327        self.set_cursor(cursor, extend_selection)
2328    }
2329
2330    /// Set the cursor position from screen coordinates,
2331    /// rounds the position to the next word start/end.
2332    ///
2333    /// The cursor positions are relative to the inner rect.
2334    /// They may be negative too, this allows setting the cursor
2335    /// to a position that is currently scrolled away.
2336    pub fn set_screen_cursor_words(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
2337        let Some(cursor) = self.relative_screen_to_pos(cursor) else {
2338            return false;
2339        };
2340
2341        let anchor = self.anchor();
2342        let cursor = if cursor < anchor {
2343            self.word_start(cursor)
2344        } else {
2345            self.word_end(cursor)
2346        };
2347
2348        // extend anchor
2349        if !self.is_word_boundary(anchor) {
2350            if cursor < anchor {
2351                self.set_cursor(self.word_end(anchor), false);
2352            } else {
2353                self.set_cursor(self.word_start(anchor), false);
2354            }
2355        }
2356
2357        self.set_cursor(cursor, extend_selection)
2358    }
2359}
2360
2361impl TextAreaState {
2362    /// Maximum offset that is accessible with scrolling.
2363    ///
2364    /// This is set to `len_lines - page_size` for shift-mode,
2365    /// and to `len_lines` for any wrapping-mode.
2366    pub fn vertical_max_offset(&self) -> upos_type {
2367        self.vscroll.max_offset() as upos_type
2368    }
2369
2370    /// Current vertical offset.
2371    pub fn vertical_offset(&self) -> upos_type {
2372        self.vscroll.offset() as upos_type
2373    }
2374
2375    /// Rendered height of the widget.
2376    pub fn vertical_page(&self) -> upos_type {
2377        self.vscroll.page_len() as upos_type
2378    }
2379
2380    /// Suggested scroll per scroll-event.
2381    pub fn vertical_scroll(&self) -> upos_type {
2382        self.vscroll.scroll_by() as upos_type
2383    }
2384
2385    /// Maximum horizontal offset.
2386    ///
2387    /// This is set to 0 when text-wrapping is active.
2388    /// Otherwise, it can be set manually, but is always
2389    /// ignored by all scroll-functions. This widget
2390    /// doesn't try to find an overall text-width.
2391    ///
2392    /// It __will__ be used to render the scrollbar though.
2393    pub fn horizontal_max_offset(&self) -> upos_type {
2394        self.hscroll.max_offset() as upos_type
2395    }
2396
2397    /// Current horizontal offset.
2398    pub fn horizontal_offset(&self) -> upos_type {
2399        self.hscroll.offset() as upos_type
2400    }
2401
2402    /// Rendered width of the text-area.
2403    pub fn horizontal_page(&self) -> upos_type {
2404        self.hscroll.page_len() as upos_type
2405    }
2406
2407    /// Suggested scroll-by per scroll-event.
2408    pub fn horizontal_scroll(&self) -> upos_type {
2409        self.hscroll.scroll_by() as upos_type
2410    }
2411
2412    /// Change the vertical offset.
2413    /// There is no limit to this offset.
2414    ///
2415    /// Return
2416    ///
2417    /// `true` if the offset changed at all.
2418    pub fn set_vertical_offset(&mut self, row_offset: upos_type) -> bool {
2419        self.scroll_to_cursor.set(false);
2420        self.sub_row_offset = 0;
2421        self.vscroll.set_offset(row_offset as usize)
2422    }
2423
2424    /// Change the horizontal offset.
2425    ///
2426    /// There is no limit to this offset. If there is text-wrapping
2427    /// this offset will be ignored.
2428    ///
2429    /// Return
2430    ///
2431    /// `true` if the offset changed at all.
2432    pub fn set_horizontal_offset(&mut self, col_offset: upos_type) -> bool {
2433        self.scroll_to_cursor.set(false);
2434        self.hscroll.set_offset(col_offset as usize)
2435    }
2436
2437    /// Scroll that the cursor is visible.
2438    ///
2439    /// This positioning happens with the next render.
2440    ///
2441    /// All move-fn do this automatically.
2442    pub fn scroll_cursor_to_visible(&mut self) {
2443        self.scroll_to_cursor.set(true);
2444    }
2445
2446    /// Scrolls to make the given position visible.
2447    ///
2448    /// Caveat
2449    ///
2450    /// This function works correctly after the first render.
2451    pub fn scroll_to_pos(&mut self, pos: impl Into<TextPosition>) -> bool {
2452        let old_offset = self.clean_offset();
2453
2454        let pos = pos.into();
2455
2456        'f: {
2457            match self.text_wrap {
2458                TextWrap::Shift => {
2459                    let (ox, _, oy) = old_offset;
2460
2461                    let height = self.rendered.height as upos_type;
2462                    let width = self.rendered.width as upos_type;
2463                    let width = if self.show_ctrl() || self.wrap_ctrl() {
2464                        width.saturating_sub(1)
2465                    } else {
2466                        width
2467                    };
2468
2469                    let noy = if pos.y < oy.saturating_sub(height) {
2470                        pos.y.saturating_sub(height * 4 / 10)
2471                    } else if pos.y < oy {
2472                        pos.y
2473                    } else if pos.y >= oy + 2 * height {
2474                        pos.y.saturating_sub(height * 6 / 10)
2475                    } else if pos.y >= oy + height {
2476                        pos.y.saturating_sub(height.saturating_sub(1))
2477                    } else {
2478                        oy
2479                    };
2480
2481                    let nox = if pos.x < ox {
2482                        pos.x
2483                    } else if pos.x >= ox + width {
2484                        pos.x.saturating_sub(width) + 1
2485                    } else {
2486                        ox
2487                    };
2488
2489                    self.set_offset((nox, noy));
2490                    self.set_sub_row_offset(0);
2491                }
2492                TextWrap::Hard | TextWrap::Word(_) => {
2493                    let (_ox, sub_row_offset, oy) = old_offset;
2494                    let page = self.rendered.height as upos_type;
2495
2496                    // on visible or close by
2497                    let scr = (0, oy.saturating_sub(page), 3 * page);
2498                    self.stc_fill_screen_cache(scr);
2499                    if let Some(off_row) =
2500                        self.stc_screen_row(scr, TextPosition::new(sub_row_offset, oy))
2501                    {
2502                        if let Some(pos_row) = self.stc_screen_row(scr, pos) {
2503                            if pos_row < off_row && pos_row >= off_row.saturating_sub(page) {
2504                                let noff_row = pos_row;
2505                                let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2506                                self.set_offset((0, noy));
2507                                self.set_sub_row_offset(nsub_row_offset);
2508                                break 'f;
2509                            } else if pos_row >= off_row + page && pos_row < off_row + 2 * page {
2510                                let noff_row = pos_row.saturating_sub(page.saturating_sub(1));
2511                                let (nsub_row_offset, noy) = self.stc_sub_row_offset(scr, noff_row);
2512                                self.set_offset((0, noy));
2513                                self.set_sub_row_offset(nsub_row_offset);
2514                                break 'f;
2515                            } else if pos_row >= off_row && pos_row < off_row + page {
2516                                break 'f;
2517                            }
2518                        }
2519                    }
2520
2521                    // long jump. center position.
2522                    let alt_scr = (0, pos.y.saturating_sub(page), 3 * page);
2523                    self.stc_fill_screen_cache(alt_scr);
2524                    if let Some(alt_scr_row) = self.stc_screen_row(alt_scr, pos) {
2525                        let noff_row = alt_scr_row.saturating_sub(page * 5 / 10);
2526                        let (nsub_row_offset, noy) = self.stc_sub_row_offset(alt_scr, noff_row);
2527                        self.set_offset((0, noy));
2528                        self.set_sub_row_offset(nsub_row_offset);
2529                    } else {
2530                        self.set_offset((0, pos.y));
2531                        self.set_sub_row_offset(0);
2532                    }
2533                }
2534            }
2535        }
2536
2537        old_offset != self.clean_offset()
2538    }
2539
2540    /// Scrolls to make the given row visible.
2541    ///
2542    /// Adjusts the offset just enough to make this happen.
2543    /// Does nothing if the position is already visible.
2544    ///
2545    /// Return
2546    ///
2547    /// `true` if the offset changed.
2548    pub fn scroll_to_row(&mut self, pos: upos_type) -> bool {
2549        self.scroll_to_cursor.set(false);
2550
2551        match self.text_wrap {
2552            TextWrap::Shift => self.vscroll.scroll_to_pos(pos as usize),
2553            TextWrap::Hard | TextWrap::Word(_) => self
2554                .vscroll
2555                .set_offset(self.vscroll.limited_offset(pos as usize)),
2556        }
2557    }
2558
2559    /// Scroll to make the given column visible.
2560    ///
2561    /// This scroll-offset is ignored if there is any text-wrapping.
2562    ///
2563    /// Return
2564    ///
2565    /// `true` if the offset changed.
2566    pub fn scroll_to_col(&mut self, pos: upos_type) -> bool {
2567        self.scroll_to_cursor.set(false);
2568        self.hscroll.set_offset(pos as usize)
2569    }
2570
2571    /// Scroll up by `delta` rows.
2572    ///
2573    /// Return
2574    ///
2575    /// `true` if the offset changes.
2576    pub fn scroll_up(&mut self, delta: upos_type) -> bool {
2577        if let Some(pos) = self.relative_screen_to_pos((0, -(delta as i16))) {
2578            self.sub_row_offset = pos.x;
2579            self.vscroll.set_offset(pos.y as usize);
2580            true
2581        } else {
2582            false
2583        }
2584    }
2585
2586    /// Scroll down by `delta` rows.
2587    ///
2588    /// Return
2589    ///
2590    /// `true` if the offset changes.
2591    pub fn scroll_down(&mut self, delta: upos_type) -> bool {
2592        if let Some(pos) = self.relative_screen_to_pos((0, delta as i16)) {
2593            self.sub_row_offset = pos.x;
2594            self.vscroll.set_offset(pos.y as usize);
2595            true
2596        } else {
2597            false
2598        }
2599    }
2600
2601    /// Scroll left by `delta` columns.
2602    ///
2603    /// This ignores the max_offset, as that is never correct anyway.
2604    ///
2605    /// __Return__
2606    ///
2607    /// `true` if the offset changes.
2608    ///
2609    /// __Caveat__
2610    ///
2611    /// Does nothing if text-wrapping is active.
2612    pub fn scroll_left(&mut self, delta: upos_type) -> bool {
2613        self.hscroll
2614            .set_offset(self.hscroll.offset.saturating_add(delta as usize))
2615    }
2616
2617    /// Scroll right by `delta` columns.
2618    ///
2619    /// This ignores the max_offset, as that is never correct anyway.
2620    ///
2621    /// __Return__
2622    ///
2623    /// `true`if the offset changes.
2624    ///
2625    /// __Caveat__
2626    ///
2627    /// Does nothing if text-wrapping is active.
2628    pub fn scroll_right(&mut self, delta: upos_type) -> bool {
2629        self.hscroll
2630            .set_offset(self.hscroll.offset.saturating_sub(delta as usize))
2631    }
2632}
2633
2634impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextAreaState {
2635    fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
2636        // small helper ...
2637        fn tc(r: bool) -> TextOutcome {
2638            if r {
2639                TextOutcome::TextChanged
2640            } else {
2641                TextOutcome::Unchanged
2642            }
2643        }
2644
2645        let mut r =
2646            if self.is_focused() {
2647                match event {
2648                    ct_event!(key press c)
2649                    | ct_event!(key press SHIFT-c)
2650                    | ct_event!(key press CONTROL_ALT-c) => tc(self.insert_char(*c)),
2651                    ct_event!(keycode press Tab) => {
2652                        // ignore tab from focus
2653                        tc(if !self.focus.gained() {
2654                            self.insert_tab()
2655                        } else {
2656                            false
2657                        })
2658                    }
2659                    ct_event!(keycode press SHIFT-BackTab) => {
2660                        // ignore tab from focus
2661                        tc(if !self.focus.gained() {
2662                            self.insert_backtab()
2663                        } else {
2664                            false
2665                        })
2666                    }
2667                    ct_event!(keycode press Enter) => tc(self.insert_newline()),
2668                    ct_event!(keycode press Backspace)
2669                    | ct_event!(keycode press SHIFT-Backspace) => tc(self.delete_prev_char()),
2670                    ct_event!(keycode press Delete) | ct_event!(keycode press SHIFT-Delete) => {
2671                        tc(self.delete_next_char())
2672                    }
2673                    ct_event!(keycode press CONTROL-Backspace)
2674                    | ct_event!(keycode press ALT-Backspace) => tc(self.delete_prev_word()),
2675                    ct_event!(keycode press CONTROL-Delete)
2676                    | ct_event!(keycode press ALT-Delete) => tc(self.delete_next_word()),
2677                    ct_event!(key press CONTROL-'x') => tc(self.cut_to_clip()),
2678                    ct_event!(key press CONTROL-'v') => tc(self.paste_from_clip()),
2679                    ct_event!(key press CONTROL-'d') => tc(self.duplicate_text()),
2680                    ct_event!(key press CONTROL-'y') => tc(self.delete_line()),
2681                    ct_event!(key press CONTROL-'z') => tc(self.undo()),
2682                    ct_event!(key press CONTROL_SHIFT-'Z') => tc(self.redo()),
2683
2684                    ct_event!(key release _)
2685                    | ct_event!(key release SHIFT-_)
2686                    | ct_event!(key release CONTROL_ALT-_)
2687                    | ct_event!(keycode release Tab)
2688                    | ct_event!(keycode release Enter)
2689                    | ct_event!(keycode release Backspace)
2690                    | ct_event!(keycode release Delete)
2691                    | ct_event!(keycode release CONTROL-Backspace)
2692                    | ct_event!(keycode release ALT-Backspace)
2693                    | ct_event!(keycode release CONTROL-Delete)
2694                    | ct_event!(key release CONTROL-'x')
2695                    | ct_event!(key release CONTROL-'v')
2696                    | ct_event!(key release CONTROL-'d')
2697                    | ct_event!(key release CONTROL-'y')
2698                    | ct_event!(key release CONTROL-'z')
2699                    | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
2700                    _ => TextOutcome::Continue,
2701                }
2702            } else {
2703                TextOutcome::Continue
2704            };
2705        if r == TextOutcome::Continue {
2706            r = self.handle(event, ReadOnly);
2707        }
2708        r
2709    }
2710}
2711
2712/// Style-id for search matches.
2713pub const MATCH_STYLE: usize = 100_001;
2714
2715impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextAreaState {
2716    fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
2717        let mut r = if self.is_focused() {
2718            match event {
2719                ct_event!(keycode press Left) => self.move_left(1, false).into(),
2720                ct_event!(keycode press Right) => self.move_right(1, false).into(),
2721                ct_event!(keycode press Up) => self.move_up(1, false).into(),
2722                ct_event!(keycode press Down) => self.move_down(1, false).into(),
2723                ct_event!(keycode press PageUp) => {
2724                    self.move_up(self.vertical_page() as u16, false).into()
2725                }
2726                ct_event!(keycode press PageDown) => {
2727                    self.move_down(self.vertical_page() as u16, false).into()
2728                }
2729                ct_event!(keycode press Home) => self.move_to_line_start(false).into(),
2730                ct_event!(keycode press End) => self.move_to_line_end(false).into(),
2731                ct_event!(keycode press CONTROL-Left) => self.move_to_prev_word(false).into(),
2732                ct_event!(keycode press CONTROL-Right) => self.move_to_next_word(false).into(),
2733                ct_event!(keycode press CONTROL-Up) => false.into(),
2734                ct_event!(keycode press CONTROL-Down) => false.into(),
2735                ct_event!(keycode press CONTROL-PageUp) => self.move_to_screen_start(false).into(),
2736                ct_event!(keycode press CONTROL-PageDown) => self.move_to_screen_end(false).into(),
2737                ct_event!(keycode press CONTROL-Home) => self.move_to_start(false).into(),
2738                ct_event!(keycode press CONTROL-End) => self.move_to_end(false).into(),
2739
2740                ct_event!(keycode press ALT-Left) => self.scroll_left(1).into(),
2741                ct_event!(keycode press ALT-Right) => self.scroll_right(1).into(),
2742                ct_event!(keycode press ALT-Up) => self.scroll_up(1).into(),
2743                ct_event!(keycode press ALT-Down) => self.scroll_down(1).into(),
2744                ct_event!(keycode press ALT-PageUp) => {
2745                    self.scroll_up(max(self.vertical_page() / 2, 1)).into()
2746                }
2747                ct_event!(keycode press ALT-PageDown) => {
2748                    self.scroll_down(max(self.vertical_page() / 2, 1)).into()
2749                }
2750                ct_event!(keycode press ALT_SHIFT-PageUp) => {
2751                    self.scroll_left(max(self.horizontal_page() / 5, 1)).into()
2752                }
2753                ct_event!(keycode press ALT_SHIFT-PageDown) => {
2754                    self.scroll_right(max(self.horizontal_page() / 5, 1)).into()
2755                }
2756
2757                ct_event!(keycode press SHIFT-Left) => self.move_left(1, true).into(),
2758                ct_event!(keycode press SHIFT-Right) => self.move_right(1, true).into(),
2759                ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
2760                ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
2761                ct_event!(keycode press SHIFT-PageUp) => {
2762                    self.move_up(self.vertical_page() as u16, true).into()
2763                }
2764                ct_event!(keycode press SHIFT-PageDown) => {
2765                    self.move_down(self.vertical_page() as u16, true).into()
2766                }
2767                ct_event!(keycode press SHIFT-Home) => self.move_to_line_start(true).into(),
2768                ct_event!(keycode press SHIFT-End) => self.move_to_line_end(true).into(),
2769                ct_event!(keycode press CONTROL_SHIFT-Left) => self.move_to_prev_word(true).into(),
2770                ct_event!(keycode press CONTROL_SHIFT-Right) => self.move_to_next_word(true).into(),
2771                ct_event!(keycode press CONTROL_SHIFT-Home) => self.move_to_start(true).into(),
2772                ct_event!(keycode press CONTROL_SHIFT-End) => self.move_to_end(true).into(),
2773                ct_event!(key press CONTROL-'a') => self.select_all().into(),
2774                ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
2775
2776                ct_event!(keycode press F(3)) => self.move_to_next_match().into(),
2777                ct_event!(keycode press SHIFT-F(3)) => self.move_to_prev_match().into(),
2778
2779                ct_event!(keycode release Left)
2780                | ct_event!(keycode release Right)
2781                | ct_event!(keycode release Up)
2782                | ct_event!(keycode release Down)
2783                | ct_event!(keycode release PageUp)
2784                | ct_event!(keycode release PageDown)
2785                | ct_event!(keycode release Home)
2786                | ct_event!(keycode release End)
2787                | ct_event!(keycode release CONTROL-Left)
2788                | ct_event!(keycode release CONTROL-Right)
2789                | ct_event!(keycode release CONTROL-Up)
2790                | ct_event!(keycode release CONTROL-Down)
2791                | ct_event!(keycode release CONTROL-PageUp)
2792                | ct_event!(keycode release CONTROL-PageDown)
2793                | ct_event!(keycode release CONTROL-Home)
2794                | ct_event!(keycode release CONTROL-End)
2795                | ct_event!(keycode release ALT-Left)
2796                | ct_event!(keycode release ALT-Right)
2797                | ct_event!(keycode release ALT-Up)
2798                | ct_event!(keycode release ALT-Down)
2799                | ct_event!(keycode release ALT-PageUp)
2800                | ct_event!(keycode release ALT-PageDown)
2801                | ct_event!(keycode release ALT_SHIFT-PageUp)
2802                | ct_event!(keycode release ALT_SHIFT-PageDown)
2803                | ct_event!(keycode release SHIFT-Left)
2804                | ct_event!(keycode release SHIFT-Right)
2805                | ct_event!(keycode release SHIFT-Up)
2806                | ct_event!(keycode release SHIFT-Down)
2807                | ct_event!(keycode release SHIFT-PageUp)
2808                | ct_event!(keycode release SHIFT-PageDown)
2809                | ct_event!(keycode release SHIFT-Home)
2810                | ct_event!(keycode release SHIFT-End)
2811                | ct_event!(keycode release CONTROL_SHIFT-Left)
2812                | ct_event!(keycode release CONTROL_SHIFT-Right)
2813                | ct_event!(keycode release CONTROL_SHIFT-Home)
2814                | ct_event!(keycode release CONTROL_SHIFT-End)
2815                | ct_event!(key release CONTROL-'a')
2816                | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
2817                _ => TextOutcome::Continue,
2818            }
2819        } else {
2820            TextOutcome::Continue
2821        };
2822
2823        if r == TextOutcome::Continue {
2824            r = self.handle(event, MouseOnly);
2825        }
2826        r
2827    }
2828}
2829
2830impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextAreaState {
2831    fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
2832        flow!(match event {
2833            ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
2834                let cx = m.column as i16 - self.inner.x as i16;
2835                let cy = m.row as i16 - self.inner.y as i16;
2836                self.set_screen_cursor((cx, cy), true).into()
2837            }
2838            ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
2839                let cx = m.column as i16 - self.inner.x as i16;
2840                let cy = m.row as i16 - self.inner.y as i16;
2841                self.set_screen_cursor_words((cx, cy), true).into()
2842            }
2843            ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
2844                if let Some(test) = self.screen_to_pos((m.column, m.row)) {
2845                    let start = self.word_start(test);
2846                    let end = self.word_end(test);
2847                    self.set_selection(start, end).into()
2848                } else {
2849                    TextOutcome::Unchanged
2850                }
2851            }
2852            ct_event!(mouse down Left for column,row) => {
2853                if self.inner.contains((*column, *row).into()) {
2854                    let cx = (column - self.inner.x) as i16;
2855                    let cy = (row - self.inner.y) as i16;
2856                    self.set_screen_cursor((cx, cy), false).into()
2857                } else {
2858                    TextOutcome::Continue
2859                }
2860            }
2861            ct_event!(mouse down SHIFT-Left for column,row) => {
2862                if self.inner.contains((*column, *row).into()) {
2863                    let cx = (column - self.inner.x) as i16;
2864                    let cy = (row - self.inner.y) as i16;
2865                    self.set_screen_cursor((cx, cy), true).into()
2866                } else {
2867                    TextOutcome::Continue
2868                }
2869            }
2870            ct_event!(mouse down CONTROL-Left for column,row) => {
2871                if self.inner.contains((*column, *row).into()) {
2872                    let cx = (column - self.inner.x) as i16;
2873                    let cy = (row - self.inner.y) as i16;
2874                    self.set_screen_cursor((cx, cy), true).into()
2875                } else {
2876                    TextOutcome::Continue
2877                }
2878            }
2879            ct_event!(mouse down ALT-Left for column,row) => {
2880                if self.inner.contains((*column, *row).into()) {
2881                    let cx = (column - self.inner.x) as i16;
2882                    let cy = (row - self.inner.y) as i16;
2883                    self.set_screen_cursor_words((cx, cy), true).into()
2884                } else {
2885                    TextOutcome::Continue
2886                }
2887            }
2888            _ => TextOutcome::Continue,
2889        });
2890
2891        let mut sas = ScrollAreaState::new()
2892            .area(self.inner)
2893            .h_scroll(&mut self.hscroll)
2894            .v_scroll(&mut self.vscroll);
2895        let r = match sas.handle(event, MouseOnly) {
2896            ScrollOutcome::Up(v) => self.scroll_up(v as upos_type),
2897            ScrollOutcome::Down(v) => self.scroll_down(v as upos_type),
2898            ScrollOutcome::Left(v) => self.scroll_left(v as upos_type),
2899            ScrollOutcome::Right(v) => self.scroll_right(v as upos_type),
2900            ScrollOutcome::VPos(v) => self.scroll_to_row(v as upos_type),
2901            ScrollOutcome::HPos(v) => self.scroll_to_col(v as upos_type),
2902            _ => false,
2903        };
2904        if r {
2905            return TextOutcome::Changed;
2906        }
2907
2908        TextOutcome::Continue
2909    }
2910}
2911
2912/// Handle all events.
2913/// Text events are only processed if focus is true.
2914/// Mouse events are processed if they are in range.
2915pub fn handle_events(
2916    state: &mut TextAreaState,
2917    focus: bool,
2918    event: &crossterm::event::Event,
2919) -> TextOutcome {
2920    state.focus.set(focus);
2921    state.handle(event, Regular)
2922}
2923
2924/// Handle only navigation events.
2925/// Text events are only processed if focus is true.
2926/// Mouse events are processed if they are in range.
2927pub fn handle_readonly_events(
2928    state: &mut TextAreaState,
2929    focus: bool,
2930    event: &crossterm::event::Event,
2931) -> TextOutcome {
2932    state.focus.set(focus);
2933    state.handle(event, ReadOnly)
2934}
2935
2936/// Handle only mouse-events.
2937pub fn handle_mouse_events(
2938    state: &mut TextAreaState,
2939    event: &crossterm::event::Event,
2940) -> TextOutcome {
2941    state.handle(event, MouseOnly)
2942}