1use crate::_private::NonExhaustive;
17use crate::clipboard::{Clipboard, global_clipboard};
18use crate::core::core_op::*;
19use crate::core::{TextCore, TextString};
20use crate::event::{ReadOnly, TextOutcome};
21use crate::glyph2::{Glyph2, TextWrap2};
22use crate::text_store::TextStore;
23use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
24use crate::{
25 HasScreenCursor, TextError, TextFocusGained, TextFocusLost, TextPosition, TextRange, TextStyle,
26 ipos_type, upos_type,
27};
28use crossterm::event::KeyModifiers;
29use rat_event::util::MouseFlags;
30use rat_event::{HandleEvent, MouseOnly, Regular, ct_event};
31use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
32use rat_reloc::{RelocatableState, relocate_dark_offset};
33use ratatui::buffer::Buffer;
34use ratatui::layout::{Rect, Size};
35use ratatui::prelude::BlockExt;
36use ratatui::style::{Style, Stylize};
37use ratatui::widgets::{Block, StatefulWidget, Widget};
38use std::borrow::Cow;
39use std::cell::Cell;
40use std::cmp::min;
41use std::collections::HashMap;
42use std::ops::Range;
43use std::rc::Rc;
44
45#[derive(Debug, Default, Clone)]
51pub struct TextInput<'a> {
52 style: Style,
53 block: Option<Block<'a>>,
54 focus_style: Option<Style>,
55 select_style: Option<Style>,
56 invalid_style: Option<Style>,
57
58 on_focus_gained: TextFocusGained,
59 on_focus_lost: TextFocusLost,
60 passwd: bool,
61
62 text_style: HashMap<usize, Style>,
63}
64
65#[derive(Debug)]
67pub struct TextInputState {
68 pub area: Rect,
71 pub inner: Rect,
74 pub rendered: Size,
78
79 pub offset: upos_type,
82 pub dark_offset: (u16, u16),
85 pub scroll_to_cursor: Rc<Cell<bool>>,
87
88 pub value: TextCore<TextString>,
90 pub invalid: bool,
93 pub passwd: bool,
96 pub overwrite: Rc<Cell<bool>>,
100 pub on_focus_gained: Rc<Cell<TextFocusGained>>,
103 pub on_focus_lost: Rc<Cell<TextFocusLost>>,
106
107 pub focus: FocusFlag,
110
111 pub mouse: MouseFlags,
114
115 pub non_exhaustive: NonExhaustive,
117}
118
119impl<'a> TextInput<'a> {
120 pub fn new() -> Self {
122 Self::default()
123 }
124
125 #[inline]
127 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
128 if let Some(styles) = styles {
129 self.styles(styles)
130 } else {
131 self
132 }
133 }
134
135 #[inline]
137 pub fn styles(mut self, styles: TextStyle) -> Self {
138 self.style = styles.style;
139 if styles.block.is_some() {
140 self.block = styles.block;
141 }
142 if let Some(border_style) = styles.border_style {
143 self.block = self.block.map(|v| v.border_style(border_style));
144 }
145 if let Some(title_style) = styles.title_style {
146 self.block = self.block.map(|v| v.title_style(title_style));
147 }
148 self.block = self.block.map(|v| v.style(self.style));
149
150 if styles.focus.is_some() {
151 self.focus_style = styles.focus;
152 }
153 if styles.select.is_some() {
154 self.select_style = styles.select;
155 }
156 if styles.invalid.is_some() {
157 self.invalid_style = styles.invalid;
158 }
159 if let Some(of) = styles.on_focus_gained {
160 self.on_focus_gained = of;
161 }
162 if let Some(of) = styles.on_focus_lost {
163 self.on_focus_lost = of;
164 }
165 self
166 }
167
168 #[inline]
170 pub fn style(mut self, style: impl Into<Style>) -> Self {
171 let style = style.into();
172 self.style = style.clone();
173 self.block = self.block.map(|v| v.style(style));
174 self
175 }
176
177 #[inline]
179 pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
180 self.focus_style = Some(style.into());
181 self
182 }
183
184 #[inline]
186 pub fn select_style(mut self, style: impl Into<Style>) -> Self {
187 self.select_style = Some(style.into());
188 self
189 }
190
191 #[inline]
194 pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
195 self.invalid_style = Some(style.into());
196 self
197 }
198
199 pub fn text_style_idx(mut self, idx: usize, style: Style) -> Self {
204 self.text_style.insert(idx, style);
205 self
206 }
207
208 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
213 for (i, s) in styles.into_iter().enumerate() {
214 self.text_style.insert(i, s);
215 }
216 self
217 }
218
219 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
224 for (i, s) in styles.into_iter() {
225 self.text_style.insert(i, s.into());
226 }
227 self
228 }
229
230 #[inline]
232 pub fn block(mut self, block: Block<'a>) -> Self {
233 self.block = Some(block);
234 self
235 }
236
237 #[inline]
239 pub fn passwd(mut self) -> Self {
240 self.passwd = true;
241 self
242 }
243
244 #[inline]
246 pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
247 self.on_focus_gained = of;
248 self
249 }
250
251 #[inline]
253 pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
254 self.on_focus_lost = of;
255 self
256 }
257
258 pub fn width(&self) -> u16 {
260 0
261 }
262
263 pub fn height(&self) -> u16 {
265 1
266 }
267}
268
269impl<'a> StatefulWidget for &TextInput<'a> {
270 type State = TextInputState;
271
272 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
273 render_ref(self, area, buf, state);
274 }
275}
276
277impl StatefulWidget for TextInput<'_> {
278 type State = TextInputState;
279
280 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
281 render_ref(&self, area, buf, state);
282 }
283}
284
285fn render_ref(widget: &TextInput<'_>, area: Rect, buf: &mut Buffer, state: &mut TextInputState) {
286 state.area = area;
287 state.inner = widget.block.inner_if_some(area);
288 state.rendered = state.inner.as_size();
289 state.passwd = widget.passwd;
290 state.on_focus_gained.set(widget.on_focus_gained);
291 state.on_focus_lost.set(widget.on_focus_lost);
292
293 if state.scroll_to_cursor.get() {
294 let c = state.cursor();
295 let o = state.offset();
296
297 if state.rendered.width > 0 {
298 let mut no = if c < o {
299 c
300 } else if c >= o + state.rendered.width as upos_type {
301 c.saturating_sub(state.rendered.width as upos_type)
302 } else {
303 o
304 };
305 if c == no + state.rendered.width as upos_type {
308 no = no.saturating_add(1);
309 }
310 state.set_offset(no);
311 } else {
312 }
314 }
315
316 let style = widget.style;
317 let focus_style = if let Some(focus_style) = widget.focus_style {
318 focus_style
319 } else {
320 style
321 };
322 let select_style = if let Some(select_style) = widget.select_style {
323 select_style
324 } else {
325 Style::default().black().on_yellow()
326 };
327 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
328 invalid_style
329 } else {
330 Style::default().red()
331 };
332
333 let (style, select_style) = if state.focus.get() {
334 if state.invalid {
335 (
336 style.patch(focus_style).patch(invalid_style),
337 style
338 .patch(focus_style)
339 .patch(select_style)
340 .patch(invalid_style),
341 )
342 } else {
343 (
344 style.patch(focus_style),
345 style.patch(focus_style).patch(select_style),
346 )
347 }
348 } else {
349 if state.invalid {
350 (
351 style.patch(invalid_style),
352 style.patch(select_style).patch(invalid_style),
353 )
354 } else {
355 (style, style.patch(select_style))
356 }
357 };
358
359 if let Some(block) = &widget.block {
361 block.render(area, buf);
362 }
363 buf.set_style(state.inner, style);
364
365 if state.inner.width == 0 || state.inner.height == 0 {
366 return;
368 }
369
370 let ox = state.offset() as u16;
371 let show_range = {
373 let start = min(ox as upos_type, state.len());
374 let end = min(start + state.inner.width as upos_type, state.len());
375 state.bytes_at_range(start..end)
376 };
377 let selection = state.selection();
378 let mut styles = Vec::new();
379
380 if widget.passwd {
381 for g in state.glyphs2() {
383 if g.screen_width() > 0 {
384 let mut style = style;
385 state
386 .value
387 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
388 for style_nr in &styles {
389 if let Some(s) = widget.text_style.get(style_nr) {
390 style = style.patch(*s);
391 }
392 }
393 if selection.contains(&g.pos().x) {
395 style = style.patch(select_style);
396 };
397
398 let screen_pos = g.screen_pos();
400
401 if let Some(cell) =
403 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
404 {
405 cell.set_symbol("*");
406 cell.set_style(style);
407 }
408 for d in 1..g.screen_width() {
410 if let Some(cell) = buf.cell_mut((
411 state.inner.x + screen_pos.0 + d,
412 state.inner.y + screen_pos.1,
413 )) {
414 cell.reset();
415 cell.set_style(style);
416 }
417 }
418 }
419 }
420 } else {
421 for g in state.glyphs2() {
422 if g.screen_width() > 0 {
423 let mut style = style;
424 state
425 .value
426 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
427 for style_nr in &styles {
428 if let Some(s) = widget.text_style.get(style_nr) {
429 style = style.patch(*s);
430 }
431 }
432 if selection.contains(&g.pos().x) {
434 style = style.patch(select_style);
435 };
436
437 let screen_pos = g.screen_pos();
439
440 if let Some(cell) =
442 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
443 {
444 cell.set_symbol(g.glyph());
445 cell.set_style(style);
446 }
447 for d in 1..g.screen_width() {
449 if let Some(cell) = buf.cell_mut((
450 state.inner.x + screen_pos.0 + d,
451 state.inner.y + screen_pos.1,
452 )) {
453 cell.reset();
454 cell.set_style(style);
455 }
456 }
457 }
458 }
459 }
460}
461
462impl Clone for TextInputState {
463 fn clone(&self) -> Self {
464 Self {
465 area: self.area,
466 inner: self.inner,
467 rendered: self.rendered,
468 offset: self.offset,
469 dark_offset: self.dark_offset,
470 scroll_to_cursor: Rc::new(Cell::new(self.scroll_to_cursor.get())),
471 value: self.value.clone(),
472 invalid: self.invalid,
473 passwd: self.passwd,
474 overwrite: Rc::new(Cell::new(self.overwrite.get())),
475 on_focus_gained: Rc::new(Cell::new(self.on_focus_gained.get())),
476 on_focus_lost: Rc::new(Cell::new(self.on_focus_lost.get())),
477 focus: self.focus_cb(self.focus.new_instance()),
478 mouse: Default::default(),
479 non_exhaustive: NonExhaustive,
480 }
481 }
482}
483
484impl Default for TextInputState {
485 fn default() -> Self {
486 let value = TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard()));
487
488 let mut z = Self {
489 area: Default::default(),
490 inner: Default::default(),
491 rendered: Default::default(),
492 offset: Default::default(),
493 dark_offset: Default::default(),
494 scroll_to_cursor: Default::default(),
495 value,
496 invalid: Default::default(),
497 passwd: Default::default(),
498 overwrite: Default::default(),
499 on_focus_gained: Default::default(),
500 on_focus_lost: Default::default(),
501 focus: Default::default(),
502 mouse: Default::default(),
503 non_exhaustive: NonExhaustive,
504 };
505 z.focus = z.focus_cb(FocusFlag::default());
506 z
507 }
508}
509
510impl TextInputState {
511 fn focus_cb(&self, flag: FocusFlag) -> FocusFlag {
512 let on_focus_lost = self.on_focus_lost.clone();
513 let cursor = self.value.shared_cursor();
514 let scroll_cursor_to_visible = self.scroll_to_cursor.clone();
515 flag.on_lost(move || match on_focus_lost.get() {
516 TextFocusLost::None => {}
517 TextFocusLost::Position0 => {
518 scroll_cursor_to_visible.set(true);
519 let mut new_cursor = cursor.get();
520 new_cursor.cursor.x = 0;
521 new_cursor.anchor.x = 0;
522 cursor.set(new_cursor);
523 }
524 });
525 let on_focus_gained = self.on_focus_gained.clone();
526 let overwrite = self.overwrite.clone();
527 let cursor = self.value.shared_cursor();
528 let scroll_cursor_to_visible = self.scroll_to_cursor.clone();
529 flag.on_gained(move || match on_focus_gained.get() {
530 TextFocusGained::None => {}
531 TextFocusGained::Overwrite => {
532 overwrite.set(true);
533 }
534 TextFocusGained::SelectAll => {
535 scroll_cursor_to_visible.set(true);
536 let mut new_cursor = cursor.get();
537 new_cursor.anchor = TextPosition::new(0, 0);
538 new_cursor.cursor = TextPosition::new(0, 1);
539 cursor.set(new_cursor);
540 }
541 });
542
543 flag
544 }
545}
546
547impl HasFocus for TextInputState {
548 fn build(&self, builder: &mut FocusBuilder) {
549 builder.leaf_widget(self);
550 }
551
552 fn focus(&self) -> FocusFlag {
553 self.focus.clone()
554 }
555
556 fn area(&self) -> Rect {
557 self.area
558 }
559}
560
561impl TextInputState {
562 pub fn new() -> Self {
563 Self::default()
564 }
565
566 pub fn named(name: &str) -> Self {
567 let mut z = Self::default();
568 z.focus = z.focus.with_name(name);
569 z
570 }
571
572 #[inline]
574 pub fn set_invalid(&mut self, invalid: bool) {
575 self.invalid = invalid;
576 }
577
578 #[inline]
580 pub fn invalid(&self) -> bool {
581 self.invalid
582 }
583
584 #[inline]
588 pub fn set_overwrite(&mut self, overwrite: bool) {
589 self.overwrite.set(overwrite);
590 }
591
592 #[inline]
594 pub fn overwrite(&self) -> bool {
595 self.overwrite.get()
596 }
597
598 #[inline]
600 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
601 self.value.set_glyph_ctrl(show_ctrl);
602 }
603
604 pub fn show_ctrl(&self) -> bool {
606 self.value.glyph_ctrl()
607 }
608}
609
610impl TextInputState {
611 #[inline]
614 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
615 match clip {
616 None => self.value.set_clipboard(None),
617 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
618 }
619 }
620
621 #[inline]
624 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
625 self.value.clipboard()
626 }
627
628 #[inline]
630 pub fn copy_to_clip(&mut self) -> bool {
631 let Some(clip) = self.value.clipboard() else {
632 return false;
633 };
634 if self.passwd {
635 return false;
636 }
637
638 _ = clip.set_string(self.selected_text().as_ref());
639 false
640 }
641
642 #[inline]
644 pub fn cut_to_clip(&mut self) -> bool {
645 let Some(clip) = self.value.clipboard() else {
646 return false;
647 };
648 if self.passwd {
649 return false;
650 }
651
652 match clip.set_string(self.selected_text().as_ref()) {
653 Ok(_) => self.delete_range(self.selection()),
654 Err(_) => false,
655 }
656 }
657
658 #[inline]
660 pub fn paste_from_clip(&mut self) -> bool {
661 let Some(clip) = self.value.clipboard() else {
662 return false;
663 };
664
665 if let Ok(text) = clip.get_string() {
666 self.insert_str(text)
667 } else {
668 false
669 }
670 }
671}
672
673impl TextInputState {
674 #[inline]
676 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
677 match undo {
678 None => self.value.set_undo_buffer(None),
679 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
680 }
681 }
682
683 #[inline]
685 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
686 self.value.undo_buffer()
687 }
688
689 #[inline]
691 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
692 self.value.undo_buffer_mut()
693 }
694
695 #[inline]
697 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
698 self.value.recent_replay_log()
699 }
700
701 #[inline]
703 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
704 self.value.replay_log(replay)
705 }
706
707 #[inline]
709 pub fn undo(&mut self) -> bool {
710 self.value.undo()
711 }
712
713 #[inline]
715 pub fn redo(&mut self) -> bool {
716 self.value.redo()
717 }
718}
719
720impl TextInputState {
721 #[inline]
732 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
733 self.value.set_styles(styles);
734 }
735
736 #[inline]
741 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
742 self.value.add_style(range, style);
743 }
744
745 #[inline]
748 pub fn add_range_style(
749 &mut self,
750 range: Range<upos_type>,
751 style: usize,
752 ) -> Result<(), TextError> {
753 let r = self
754 .value
755 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
756 self.value.add_style(r, style);
757 Ok(())
758 }
759
760 #[inline]
762 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
763 self.value.remove_style(range, style);
764 }
765
766 #[inline]
768 pub fn remove_range_style(
769 &mut self,
770 range: Range<upos_type>,
771 style: usize,
772 ) -> Result<(), TextError> {
773 let r = self
774 .value
775 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
776 self.value.remove_style(r, style);
777 Ok(())
778 }
779
780 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
782 self.value.styles_in(range, buf)
783 }
784
785 #[inline]
787 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
788 self.value.styles_at(byte_pos, buf)
789 }
790
791 #[inline]
794 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
795 self.value.styles_at_match(byte_pos, style)
796 }
797
798 #[inline]
800 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
801 self.value.styles()
802 }
803}
804
805impl TextInputState {
806 #[inline]
808 pub fn offset(&self) -> upos_type {
809 self.offset
810 }
811
812 #[inline]
814 pub fn set_offset(&mut self, offset: upos_type) {
815 self.scroll_to_cursor.set(false);
816 self.offset = offset;
817 }
818
819 #[inline]
821 pub fn cursor(&self) -> upos_type {
822 self.value.cursor().x
823 }
824
825 #[inline]
827 pub fn anchor(&self) -> upos_type {
828 self.value.anchor().x
829 }
830
831 #[inline]
834 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
835 self.scroll_cursor_to_visible();
836 self.value
837 .set_cursor(TextPosition::new(cursor, 0), extend_selection)
838 }
839
840 #[inline]
842 pub fn has_selection(&self) -> bool {
843 self.value.has_selection()
844 }
845
846 #[inline]
848 pub fn selection(&self) -> Range<upos_type> {
849 let mut v = self.value.selection();
850 if v.start == TextPosition::new(0, 1) {
851 v.start = TextPosition::new(self.line_width(), 0);
852 }
853 if v.end == TextPosition::new(0, 1) {
854 v.end = TextPosition::new(self.line_width(), 0);
855 }
856 v.start.x..v.end.x
857 }
858
859 #[inline]
862 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
863 self.scroll_cursor_to_visible();
864 self.value
865 .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
866 }
867
868 #[inline]
871 pub fn select_all(&mut self) -> bool {
872 self.scroll_cursor_to_visible();
873 self.value.select_all()
874 }
875
876 #[inline]
878 pub fn selected_text(&self) -> &str {
879 match self.str_slice(self.selection()) {
880 Cow::Borrowed(v) => v,
881 Cow::Owned(_) => {
882 unreachable!()
883 }
884 }
885 }
886}
887
888impl TextInputState {
889 #[inline]
891 pub fn is_empty(&self) -> bool {
892 self.value.is_empty()
893 }
894
895 #[inline]
897 pub fn value<T: for<'a> From<&'a str>>(&self) -> T {
898 self.value.text().as_str().into()
899 }
900
901 #[inline]
903 pub fn text(&self) -> &str {
904 self.value.text().as_str()
905 }
906
907 #[inline]
909 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
910 self.value.str_slice_byte(range).expect("valid_range")
911 }
912
913 #[inline]
915 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
916 self.value.str_slice_byte(range)
917 }
918
919 #[inline]
921 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
922 self.value
923 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
924 .expect("valid_range")
925 }
926
927 #[inline]
929 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
930 self.value
931 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
932 }
933
934 #[inline]
936 pub fn len(&self) -> upos_type {
937 self.value.line_width(0).expect("valid_row")
938 }
939
940 #[inline]
942 pub fn len_bytes(&self) -> usize {
943 self.value.len_bytes()
944 }
945
946 #[inline]
948 pub fn line_width(&self) -> upos_type {
949 self.value.line_width(0).expect("valid_row")
950 }
951
952 #[inline]
954 pub fn text_graphemes(&self, pos: upos_type) -> <TextString as TextStore>::GraphemeIter<'_> {
955 self.value
956 .text_graphemes(TextPosition::new(pos, 0))
957 .expect("valid_pos")
958 }
959
960 #[inline]
962 pub fn try_text_graphemes(
963 &self,
964 pos: upos_type,
965 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
966 self.value.text_graphemes(TextPosition::new(pos, 0))
967 }
968
969 #[inline]
971 pub fn graphemes(
972 &self,
973 range: Range<upos_type>,
974 pos: upos_type,
975 ) -> <TextString as TextStore>::GraphemeIter<'_> {
976 self.value
977 .graphemes(
978 TextRange::new((range.start, 0), (range.end, 0)),
979 TextPosition::new(pos, 0),
980 )
981 .expect("valid_args")
982 }
983
984 #[inline]
986 pub fn try_graphemes(
987 &self,
988 range: Range<upos_type>,
989 pos: upos_type,
990 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
991 self.value.graphemes(
992 TextRange::new((range.start, 0), (range.end, 0)),
993 TextPosition::new(pos, 0),
994 )
995 }
996
997 #[inline]
1000 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
1001 self.value
1002 .byte_at(TextPosition::new(pos, 0))
1003 .expect("valid_pos")
1004 }
1005
1006 #[inline]
1009 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
1010 self.value.byte_at(TextPosition::new(pos, 0))
1011 }
1012
1013 #[inline]
1015 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
1016 self.value
1017 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
1018 .expect("valid_range")
1019 }
1020
1021 #[inline]
1023 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
1024 self.value
1025 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
1026 }
1027
1028 #[inline]
1031 pub fn byte_pos(&self, byte: usize) -> upos_type {
1032 self.value.byte_pos(byte).map(|v| v.x).expect("valid_pos")
1033 }
1034
1035 #[inline]
1038 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
1039 self.value.byte_pos(byte).map(|v| v.x)
1040 }
1041
1042 #[inline]
1044 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
1045 self.value
1046 .byte_range(bytes)
1047 .map(|v| v.start.x..v.end.x)
1048 .expect("valid_range")
1049 }
1050
1051 #[inline]
1053 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
1054 self.value.byte_range(bytes).map(|v| v.start.x..v.end.x)
1055 }
1056}
1057
1058impl TextInputState {
1059 #[inline]
1061 pub fn clear(&mut self) -> bool {
1062 if self.is_empty() {
1063 false
1064 } else {
1065 self.offset = 0;
1066 self.value.clear();
1067 true
1068 }
1069 }
1070
1071 #[inline]
1075 pub fn set_value<S: Into<String>>(&mut self, s: S) {
1076 self.offset = 0;
1077 self.value.set_text(TextString::new_string(s.into()));
1078 }
1079
1080 #[inline]
1084 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1085 self.offset = 0;
1086 self.value.set_text(TextString::new_string(s.into()));
1087 }
1088
1089 #[inline]
1091 pub fn insert_char(&mut self, c: char) -> bool {
1092 if self.has_selection() {
1093 self.value
1094 .remove_str_range(self.value.selection())
1095 .expect("valid_selection");
1096 }
1097 if c == '\n' {
1098 return false;
1099 } else {
1100 self.value
1101 .insert_char(self.value.cursor(), c)
1102 .expect("valid_cursor");
1103 }
1104 self.scroll_cursor_to_visible();
1105 true
1106 }
1107
1108 #[inline]
1110 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1111 let t = t.as_ref();
1112 if self.has_selection() {
1113 self.value
1114 .remove_str_range(self.value.selection())
1115 .expect("valid_selection");
1116 }
1117 self.value
1118 .insert_str(self.value.cursor(), t)
1119 .expect("valid_cursor");
1120 self.scroll_cursor_to_visible();
1121 true
1122 }
1123
1124 #[inline]
1126 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1127 self.try_delete_range(range).expect("valid_range")
1128 }
1129
1130 #[inline]
1132 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1133 if !range.is_empty() {
1134 self.value
1135 .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1136 self.scroll_cursor_to_visible();
1137 Ok(true)
1138 } else {
1139 Ok(false)
1140 }
1141 }
1142}
1143
1144impl TextInputState {
1145 #[inline]
1147 pub fn delete_next_char(&mut self) -> bool {
1148 if self.has_selection() {
1149 self.delete_range(self.selection())
1150 } else {
1151 let pos = self.value.cursor();
1152 let r = remove_next_char(&mut self.value, pos).expect("valid_cursor");
1153 self.scroll_cursor_to_visible();
1154 r
1155 }
1156 }
1157
1158 #[inline]
1160 pub fn delete_prev_char(&mut self) -> bool {
1161 if self.value.has_selection() {
1162 self.delete_range(self.selection())
1163 } else {
1164 let pos = self.value.cursor();
1165 let r = remove_prev_char(&mut self.value, pos).expect("valid_cursor");
1166 self.scroll_cursor_to_visible();
1167 r
1168 }
1169 }
1170
1171 pub fn next_word_start(&self, pos: upos_type) -> upos_type {
1173 self.try_next_word_start(pos).expect("valid_pos")
1174 }
1175
1176 pub fn try_next_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1178 next_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1179 }
1180
1181 pub fn next_word_end(&self, pos: upos_type) -> upos_type {
1184 self.try_next_word_end(pos).expect("valid_pos")
1185 }
1186
1187 pub fn try_next_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1190 next_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1191 }
1192
1193 pub fn prev_word_start(&self, pos: upos_type) -> upos_type {
1197 self.try_prev_word_start(pos).expect("valid_pos")
1198 }
1199
1200 pub fn try_prev_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1204 prev_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1205 }
1206
1207 pub fn prev_word_end(&self, pos: upos_type) -> upos_type {
1211 self.try_prev_word_end(pos).expect("valid_pos")
1212 }
1213
1214 pub fn try_prev_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1218 prev_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1219 }
1220
1221 pub fn is_word_boundary(&self, pos: upos_type) -> bool {
1223 self.try_is_word_boundary(pos).expect("valid_pos")
1224 }
1225
1226 pub fn try_is_word_boundary(&self, pos: upos_type) -> Result<bool, TextError> {
1228 is_word_boundary(&self.value, TextPosition::new(pos, 0))
1229 }
1230
1231 pub fn word_start(&self, pos: upos_type) -> upos_type {
1233 self.try_word_start(pos).expect("valid_pos")
1234 }
1235
1236 pub fn try_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1238 word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1239 }
1240
1241 pub fn word_end(&self, pos: upos_type) -> upos_type {
1243 self.try_word_end(pos).expect("valid_pos")
1244 }
1245
1246 pub fn try_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1248 word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1249 }
1250
1251 #[inline]
1253 pub fn delete_next_word(&mut self) -> bool {
1254 if self.has_selection() {
1255 self.delete_range(self.selection())
1256 } else {
1257 let cursor = self.cursor();
1258
1259 let start = self.next_word_start(cursor);
1260 if start != cursor {
1261 self.delete_range(cursor..start)
1262 } else {
1263 let end = self.next_word_end(cursor);
1264 self.delete_range(cursor..end)
1265 }
1266 }
1267 }
1268
1269 #[inline]
1271 pub fn delete_prev_word(&mut self) -> bool {
1272 if self.has_selection() {
1273 self.delete_range(self.selection())
1274 } else {
1275 let cursor = self.cursor();
1276
1277 let end = self.prev_word_end(cursor);
1278 if end != cursor {
1279 self.delete_range(end..cursor)
1280 } else {
1281 let start = self.prev_word_start(cursor);
1282 self.delete_range(start..cursor)
1283 }
1284 }
1285 }
1286
1287 #[inline]
1289 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1290 let c = min(self.cursor() + 1, self.len());
1291 self.set_cursor(c, extend_selection)
1292 }
1293
1294 #[inline]
1296 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1297 let c = self.cursor().saturating_sub(1);
1298 self.set_cursor(c, extend_selection)
1299 }
1300
1301 #[inline]
1303 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1304 self.set_cursor(0, extend_selection)
1305 }
1306
1307 #[inline]
1309 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1310 self.set_cursor(self.len(), extend_selection)
1311 }
1312
1313 #[inline]
1314 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1315 let cursor = self.cursor();
1316 let end = self.next_word_end(cursor);
1317 self.set_cursor(end, extend_selection)
1318 }
1319
1320 #[inline]
1321 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1322 let cursor = self.cursor();
1323 let start = self.prev_word_start(cursor);
1324 self.set_cursor(start, extend_selection)
1325 }
1326}
1327
1328impl HasScreenCursor for TextInputState {
1329 #[inline]
1331 fn screen_cursor(&self) -> Option<(u16, u16)> {
1332 if self.is_focused() {
1333 if self.has_selection() {
1334 None
1335 } else {
1336 let cx = self.cursor();
1337 let ox = self.offset();
1338
1339 if cx < ox {
1340 None
1341 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1342 None
1343 } else {
1344 self.col_to_screen(cx)
1345 .map(|sc| (self.inner.x + sc, self.inner.y))
1346 }
1347 }
1348 } else {
1349 None
1350 }
1351 }
1352}
1353
1354impl RelocatableState for TextInputState {
1355 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1356 self.area.relocate(shift, clip);
1357 self.inner.relocate(shift, clip);
1358 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1360 }
1361}
1362
1363impl TextInputState {
1364 fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1365 let (text_wrap, left_margin, right_margin, word_margin) = (
1366 TextWrap2::Shift,
1367 self.offset() as upos_type,
1368 self.offset() as upos_type + self.rendered.width as upos_type,
1369 self.offset() as upos_type + self.rendered.width as upos_type,
1370 );
1371 self.value
1372 .glyphs2(
1373 self.rendered,
1374 0,
1375 0..1,
1376 0, text_wrap,
1378 false,
1379 left_margin,
1380 right_margin,
1381 word_margin,
1382 )
1383 .expect("valid-row")
1384 }
1385
1386 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1389 let ox = self.offset();
1390
1391 let scx = scx + self.dark_offset.0 as i16;
1392
1393 if scx < 0 {
1394 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1395 } else if scx as u16 >= self.rendered.width {
1396 min(ox + scx as upos_type, self.len())
1397 } else {
1398 let scx = scx as u16;
1399
1400 let line = self.glyphs2();
1401
1402 let mut col = ox;
1403 for g in line {
1404 if g.contains_screen_x(scx) {
1405 break;
1406 }
1407 col = g.pos().x + 1;
1408 }
1409 col
1410 }
1411 }
1412
1413 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1416 let ox = self.offset();
1417
1418 if pos < ox {
1419 return None;
1420 }
1421
1422 let line = self.glyphs2();
1423 let mut screen_x = 0;
1424 for g in line {
1425 if g.pos().x == pos {
1426 break;
1427 }
1428 screen_x = g.screen_pos().0 + g.screen_width();
1429 }
1430
1431 if screen_x >= self.dark_offset.0 {
1432 Some(screen_x - self.dark_offset.0)
1433 } else {
1434 None
1435 }
1436 }
1437
1438 #[inline]
1442 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1443 let scx = cursor;
1444
1445 let cx = self.screen_to_col(scx);
1446
1447 self.set_cursor(cx, extend_selection)
1448 }
1449
1450 pub fn set_screen_cursor_words(&mut self, screen_cursor: i16, extend_selection: bool) -> bool {
1457 let anchor = self.anchor();
1458
1459 let cx = self.screen_to_col(screen_cursor);
1460 let cursor = cx;
1461
1462 let cursor = if cursor < anchor {
1463 self.word_start(cursor)
1464 } else {
1465 self.word_end(cursor)
1466 };
1467
1468 if !self.is_word_boundary(anchor) {
1470 if cursor < anchor {
1471 self.set_cursor(self.word_end(anchor), false);
1472 } else {
1473 self.set_cursor(self.word_start(anchor), false);
1474 }
1475 }
1476
1477 self.set_cursor(cursor, extend_selection)
1478 }
1479
1480 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1482 self.set_offset(self.offset.saturating_sub(delta));
1483 true
1484 }
1485
1486 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1488 self.set_offset(self.offset + delta);
1489 true
1490 }
1491
1492 pub fn scroll_cursor_to_visible(&mut self) {
1494 self.scroll_to_cursor.set(true);
1495 }
1496}
1497
1498impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextInputState {
1499 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1500 fn tc(r: bool) -> TextOutcome {
1502 if r {
1503 TextOutcome::TextChanged
1504 } else {
1505 TextOutcome::Unchanged
1506 }
1507 }
1508 fn overwrite(state: &mut TextInputState) {
1509 if state.overwrite.get() {
1510 state.overwrite.set(false);
1511 state.clear();
1512 }
1513 }
1514 fn clear_overwrite(state: &mut TextInputState) {
1515 state.overwrite.set(false);
1516 }
1517
1518 let mut r = if self.is_focused() {
1542 match event {
1543 ct_event!(key press c)
1544 | ct_event!(key press SHIFT-c)
1545 | ct_event!(key press CONTROL_ALT-c) => {
1546 overwrite(self);
1547 tc(self.insert_char(*c))
1548 }
1549 ct_event!(keycode press Backspace) | ct_event!(keycode press SHIFT-Backspace) => {
1550 clear_overwrite(self);
1551 tc(self.delete_prev_char())
1552 }
1553 ct_event!(keycode press Delete) | ct_event!(keycode press SHIFT-Delete) => {
1554 clear_overwrite(self);
1555 tc(self.delete_next_char())
1556 }
1557 ct_event!(keycode press CONTROL-Backspace)
1558 | ct_event!(keycode press ALT-Backspace) => {
1559 clear_overwrite(self);
1560 tc(self.delete_prev_word())
1561 }
1562 ct_event!(keycode press CONTROL-Delete) => {
1563 clear_overwrite(self);
1564 tc(self.delete_next_word())
1565 }
1566 ct_event!(key press CONTROL-'x') => {
1567 clear_overwrite(self);
1568 tc(self.cut_to_clip())
1569 }
1570 ct_event!(key press CONTROL-'v') => {
1571 overwrite(self);
1572 tc(self.paste_from_clip())
1573 }
1574 ct_event!(key press CONTROL-'d') => {
1575 clear_overwrite(self);
1576 tc(self.clear())
1577 }
1578 ct_event!(key press CONTROL-'z') => {
1579 clear_overwrite(self);
1580 tc(self.undo())
1581 }
1582 ct_event!(key press CONTROL_SHIFT-'Z') => {
1583 clear_overwrite(self);
1584 tc(self.redo())
1585 }
1586
1587 ct_event!(key release _)
1588 | ct_event!(key release SHIFT-_)
1589 | ct_event!(key release CONTROL_ALT-_)
1590 | ct_event!(keycode release Tab)
1591 | ct_event!(keycode release Backspace)
1592 | ct_event!(keycode release Delete)
1593 | ct_event!(keycode release CONTROL-Backspace)
1594 | ct_event!(keycode release ALT-Backspace)
1595 | ct_event!(keycode release CONTROL-Delete)
1596 | ct_event!(key release CONTROL-'x')
1597 | ct_event!(key release CONTROL-'v')
1598 | ct_event!(key release CONTROL-'d')
1599 | ct_event!(key release CONTROL-'y')
1600 | ct_event!(key release CONTROL-'z')
1601 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1602
1603 _ => TextOutcome::Continue,
1604 }
1605 } else {
1606 TextOutcome::Continue
1607 };
1608 if r == TextOutcome::Continue {
1609 r = self.handle(event, ReadOnly);
1610 }
1611 r
1612 }
1613}
1614
1615impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextInputState {
1616 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1617 fn clear_overwrite(state: &mut TextInputState) {
1618 state.overwrite.set(false);
1619 }
1620
1621 let mut r = if self.is_focused() {
1622 match event {
1623 ct_event!(keycode press Left) => {
1624 clear_overwrite(self);
1625 self.move_left(false).into()
1626 }
1627 ct_event!(keycode press Right) => {
1628 clear_overwrite(self);
1629 self.move_right(false).into()
1630 }
1631 ct_event!(keycode press CONTROL-Left) => {
1632 clear_overwrite(self);
1633 self.move_to_prev_word(false).into()
1634 }
1635 ct_event!(keycode press CONTROL-Right) => {
1636 clear_overwrite(self);
1637 self.move_to_next_word(false).into()
1638 }
1639 ct_event!(keycode press Home) => {
1640 clear_overwrite(self);
1641 self.move_to_line_start(false).into()
1642 }
1643 ct_event!(keycode press End) => {
1644 clear_overwrite(self);
1645 self.move_to_line_end(false).into()
1646 }
1647 ct_event!(keycode press SHIFT-Left) => {
1648 clear_overwrite(self);
1649 self.move_left(true).into()
1650 }
1651 ct_event!(keycode press SHIFT-Right) => {
1652 clear_overwrite(self);
1653 self.move_right(true).into()
1654 }
1655 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1656 clear_overwrite(self);
1657 self.move_to_prev_word(true).into()
1658 }
1659 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1660 clear_overwrite(self);
1661 self.move_to_next_word(true).into()
1662 }
1663 ct_event!(keycode press SHIFT-Home) => {
1664 clear_overwrite(self);
1665 self.move_to_line_start(true).into()
1666 }
1667 ct_event!(keycode press SHIFT-End) => {
1668 clear_overwrite(self);
1669 self.move_to_line_end(true).into()
1670 }
1671 ct_event!(keycode press ALT-Left) => {
1672 clear_overwrite(self);
1673 self.scroll_left(1).into()
1674 }
1675 ct_event!(keycode press ALT-Right) => {
1676 clear_overwrite(self);
1677 self.scroll_right(1).into()
1678 }
1679 ct_event!(key press CONTROL-'a') => {
1680 clear_overwrite(self);
1681 self.select_all().into()
1682 }
1683 ct_event!(key press CONTROL-'c') => {
1684 clear_overwrite(self);
1685 self.copy_to_clip().into()
1686 }
1687
1688 ct_event!(keycode release Left)
1689 | ct_event!(keycode release Right)
1690 | ct_event!(keycode release CONTROL-Left)
1691 | ct_event!(keycode release CONTROL-Right)
1692 | ct_event!(keycode release Home)
1693 | ct_event!(keycode release End)
1694 | ct_event!(keycode release SHIFT-Left)
1695 | ct_event!(keycode release SHIFT-Right)
1696 | ct_event!(keycode release CONTROL_SHIFT-Left)
1697 | ct_event!(keycode release CONTROL_SHIFT-Right)
1698 | ct_event!(keycode release SHIFT-Home)
1699 | ct_event!(keycode release SHIFT-End)
1700 | ct_event!(key release CONTROL-'a')
1701 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1702
1703 _ => TextOutcome::Continue,
1704 }
1705 } else {
1706 TextOutcome::Continue
1707 };
1708
1709 if r == TextOutcome::Continue {
1710 r = self.handle(event, MouseOnly);
1711 }
1712 r
1713 }
1714}
1715
1716impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextInputState {
1717 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1718 fn clear_overwrite(state: &mut TextInputState) {
1719 state.overwrite.set(false);
1720 }
1721
1722 match event {
1723 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1724 let c = (m.column as i16) - (self.inner.x as i16);
1725 clear_overwrite(self);
1726 self.set_screen_cursor(c, true).into()
1727 }
1728 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1729 let cx = m.column as i16 - self.inner.x as i16;
1730 clear_overwrite(self);
1731 self.set_screen_cursor_words(cx, true).into()
1732 }
1733 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1734 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1735 let start = self.word_start(tx);
1736 let end = self.word_end(tx);
1737 clear_overwrite(self);
1738 self.set_selection(start, end).into()
1739 }
1740 ct_event!(mouse down Left for column,row) => {
1741 if self.gained_focus() {
1742 TextOutcome::Unchanged
1745 } else if self.inner.contains((*column, *row).into()) {
1746 let c = (column - self.inner.x) as i16;
1747 clear_overwrite(self);
1748 self.set_screen_cursor(c, false).into()
1749 } else {
1750 TextOutcome::Continue
1751 }
1752 }
1753 ct_event!(mouse down CONTROL-Left for column,row) => {
1754 if self.inner.contains((*column, *row).into()) {
1755 let cx = (column - self.inner.x) as i16;
1756 clear_overwrite(self);
1757 self.set_screen_cursor(cx, true).into()
1758 } else {
1759 TextOutcome::Continue
1760 }
1761 }
1762 ct_event!(mouse down ALT-Left for column,row) => {
1763 if self.inner.contains((*column, *row).into()) {
1764 let cx = (column - self.inner.x) as i16;
1765 clear_overwrite(self);
1766 self.set_screen_cursor_words(cx, true).into()
1767 } else {
1768 TextOutcome::Continue
1769 }
1770 }
1771 _ => TextOutcome::Continue,
1772 }
1773 }
1774}
1775
1776pub fn handle_events(
1780 state: &mut TextInputState,
1781 focus: bool,
1782 event: &crossterm::event::Event,
1783) -> TextOutcome {
1784 state.focus.set(focus);
1785 state.handle(event, Regular)
1786}
1787
1788pub fn handle_readonly_events(
1792 state: &mut TextInputState,
1793 focus: bool,
1794 event: &crossterm::event::Event,
1795) -> TextOutcome {
1796 state.focus.set(focus);
1797 state.handle(event, ReadOnly)
1798}
1799
1800pub fn handle_mouse_events(
1802 state: &mut TextInputState,
1803 event: &crossterm::event::Event,
1804) -> TextOutcome {
1805 state.handle(event, MouseOnly)
1806}