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 block: Option<Block<'a>>,
53 style: Style,
54 focus_style: Option<Style>,
55 select_style: Option<Style>,
56 invalid_style: Option<Style>,
57 on_focus_gained: TextFocusGained,
58 on_focus_lost: TextFocusLost,
59 passwd: bool,
60 text_style: HashMap<usize, Style>,
61}
62
63#[derive(Debug)]
65pub struct TextInputState {
66 pub area: Rect,
69 pub inner: Rect,
72 pub rendered: Size,
76
77 pub offset: upos_type,
80 pub dark_offset: (u16, u16),
83 pub scroll_to_cursor: Rc<Cell<bool>>,
85
86 pub value: TextCore<TextString>,
88 pub invalid: bool,
91 pub passwd: bool,
94 pub overwrite: Rc<Cell<bool>>,
98 pub on_focus_gained: Rc<Cell<TextFocusGained>>,
101 pub on_focus_lost: Rc<Cell<TextFocusLost>>,
104
105 pub focus: FocusFlag,
108
109 pub mouse: MouseFlags,
112
113 pub non_exhaustive: NonExhaustive,
115}
116
117impl<'a> TextInput<'a> {
118 pub fn new() -> Self {
120 Self::default()
121 }
122
123 #[inline]
125 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
126 if let Some(styles) = styles {
127 self.styles(styles)
128 } else {
129 self
130 }
131 }
132
133 #[inline]
135 pub fn styles(mut self, styles: TextStyle) -> Self {
136 self.style = styles.style;
137 if styles.focus.is_some() {
138 self.focus_style = styles.focus;
139 }
140 if styles.select.is_some() {
141 self.select_style = styles.select;
142 }
143 if styles.invalid.is_some() {
144 self.invalid_style = styles.invalid;
145 }
146 if let Some(of) = styles.on_focus_gained {
147 self.on_focus_gained = of;
148 }
149 if let Some(of) = styles.on_focus_lost {
150 self.on_focus_lost = of;
151 }
152 self.block = self.block.map(|v| v.style(self.style));
153 if let Some(border_style) = styles.border_style {
154 self.block = self.block.map(|v| v.border_style(border_style));
155 }
156 if styles.block.is_some() {
157 self.block = styles.block;
158 }
159 self
160 }
161
162 #[inline]
164 pub fn style(mut self, style: impl Into<Style>) -> Self {
165 self.style = style.into();
166 self
167 }
168
169 #[inline]
171 pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
172 self.focus_style = Some(style.into());
173 self
174 }
175
176 #[inline]
178 pub fn select_style(mut self, style: impl Into<Style>) -> Self {
179 self.select_style = Some(style.into());
180 self
181 }
182
183 #[inline]
186 pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
187 self.invalid_style = Some(style.into());
188 self
189 }
190
191 pub fn text_style_idx(mut self, idx: usize, style: Style) -> Self {
196 self.text_style.insert(idx, style);
197 self
198 }
199
200 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
205 for (i, s) in styles.into_iter().enumerate() {
206 self.text_style.insert(i, s);
207 }
208 self
209 }
210
211 pub fn text_style_map<T: Into<Style>>(mut self, styles: HashMap<usize, T>) -> Self {
216 for (i, s) in styles.into_iter() {
217 self.text_style.insert(i, s.into());
218 }
219 self
220 }
221
222 #[inline]
224 pub fn block(mut self, block: Block<'a>) -> Self {
225 self.block = Some(block);
226 self
227 }
228
229 #[inline]
231 pub fn passwd(mut self) -> Self {
232 self.passwd = true;
233 self
234 }
235
236 #[inline]
238 pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
239 self.on_focus_gained = of;
240 self
241 }
242
243 #[inline]
245 pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
246 self.on_focus_lost = of;
247 self
248 }
249
250 pub fn width(&self) -> u16 {
252 0
253 }
254
255 pub fn height(&self) -> u16 {
257 1
258 }
259}
260
261impl<'a> StatefulWidget for &TextInput<'a> {
262 type State = TextInputState;
263
264 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
265 render_ref(self, area, buf, state);
266 }
267}
268
269impl StatefulWidget for TextInput<'_> {
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
277fn render_ref(widget: &TextInput<'_>, area: Rect, buf: &mut Buffer, state: &mut TextInputState) {
278 state.area = area;
279 state.inner = widget.block.inner_if_some(area);
280 state.rendered = state.inner.as_size();
281 state.passwd = widget.passwd;
282 state.on_focus_gained.set(widget.on_focus_gained);
283 state.on_focus_lost.set(widget.on_focus_lost);
284
285 if state.scroll_to_cursor.get() {
286 let c = state.cursor();
287 let o = state.offset();
288
289 if state.rendered.width > 0 {
290 let mut no = if c < o {
291 c
292 } else if c >= o + state.rendered.width as upos_type {
293 c.saturating_sub(state.rendered.width as upos_type)
294 } else {
295 o
296 };
297 if c == no + state.rendered.width as upos_type {
300 no = no.saturating_add(1);
301 }
302 state.set_offset(no);
303 } else {
304 }
306 }
307
308 let style = widget.style;
309 let focus_style = if let Some(focus_style) = widget.focus_style {
310 focus_style
311 } else {
312 style
313 };
314 let select_style = if let Some(select_style) = widget.select_style {
315 select_style
316 } else {
317 Style::default().black().on_yellow()
318 };
319 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
320 invalid_style
321 } else {
322 Style::default().red()
323 };
324
325 let (style, select_style) = if state.focus.get() {
326 if state.invalid {
327 (
328 style.patch(focus_style).patch(invalid_style),
329 style
330 .patch(focus_style)
331 .patch(select_style)
332 .patch(invalid_style),
333 )
334 } else {
335 (
336 style.patch(focus_style),
337 style.patch(focus_style).patch(select_style),
338 )
339 }
340 } else {
341 if state.invalid {
342 (
343 style.patch(invalid_style),
344 style.patch(select_style).patch(invalid_style),
345 )
346 } else {
347 (style, style.patch(select_style))
348 }
349 };
350
351 if let Some(block) = &widget.block {
353 block.render(area, buf);
354 }
355 buf.set_style(state.inner, style);
356
357 if state.inner.width == 0 || state.inner.height == 0 {
358 return;
360 }
361
362 let ox = state.offset() as u16;
363 let show_range = {
365 let start = min(ox as upos_type, state.len());
366 let end = min(start + state.inner.width as upos_type, state.len());
367 state.bytes_at_range(start..end)
368 };
369 let selection = state.selection();
370 let mut styles = Vec::new();
371
372 if widget.passwd {
373 for g in state.glyphs2() {
375 if g.screen_width() > 0 {
376 let mut style = style;
377 state
378 .value
379 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
380 for style_nr in &styles {
381 if let Some(s) = widget.text_style.get(style_nr) {
382 style = style.patch(*s);
383 }
384 }
385 if selection.contains(&g.pos().x) {
387 style = style.patch(select_style);
388 };
389
390 let screen_pos = g.screen_pos();
392
393 if let Some(cell) =
395 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
396 {
397 cell.set_symbol("*");
398 cell.set_style(style);
399 }
400 for d in 1..g.screen_width() {
402 if let Some(cell) = buf.cell_mut((
403 state.inner.x + screen_pos.0 + d,
404 state.inner.y + screen_pos.1,
405 )) {
406 cell.reset();
407 cell.set_style(style);
408 }
409 }
410 }
411 }
412 } else {
413 for g in state.glyphs2() {
414 if g.screen_width() > 0 {
415 let mut style = style;
416 state
417 .value
418 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
419 for style_nr in &styles {
420 if let Some(s) = widget.text_style.get(style_nr) {
421 style = style.patch(*s);
422 }
423 }
424 if selection.contains(&g.pos().x) {
426 style = style.patch(select_style);
427 };
428
429 let screen_pos = g.screen_pos();
431
432 if let Some(cell) =
434 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
435 {
436 cell.set_symbol(g.glyph());
437 cell.set_style(style);
438 }
439 for d in 1..g.screen_width() {
441 if let Some(cell) = buf.cell_mut((
442 state.inner.x + screen_pos.0 + d,
443 state.inner.y + screen_pos.1,
444 )) {
445 cell.reset();
446 cell.set_style(style);
447 }
448 }
449 }
450 }
451 }
452}
453
454impl Clone for TextInputState {
455 fn clone(&self) -> Self {
456 Self {
457 area: self.area,
458 inner: self.inner,
459 rendered: self.rendered,
460 offset: self.offset,
461 dark_offset: self.dark_offset,
462 scroll_to_cursor: Rc::new(Cell::new(self.scroll_to_cursor.get())),
463 value: self.value.clone(),
464 invalid: self.invalid,
465 passwd: self.passwd,
466 overwrite: Rc::new(Cell::new(self.overwrite.get())),
467 on_focus_gained: Rc::new(Cell::new(self.on_focus_gained.get())),
468 on_focus_lost: Rc::new(Cell::new(self.on_focus_lost.get())),
469 focus: self.focus_cb(self.focus.new_instance()),
470 mouse: Default::default(),
471 non_exhaustive: NonExhaustive,
472 }
473 }
474}
475
476impl Default for TextInputState {
477 fn default() -> Self {
478 let value = TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard()));
479
480 let mut z = Self {
481 area: Default::default(),
482 inner: Default::default(),
483 rendered: Default::default(),
484 offset: Default::default(),
485 dark_offset: Default::default(),
486 scroll_to_cursor: Default::default(),
487 value,
488 invalid: Default::default(),
489 passwd: Default::default(),
490 overwrite: Default::default(),
491 on_focus_gained: Default::default(),
492 on_focus_lost: Default::default(),
493 focus: Default::default(),
494 mouse: Default::default(),
495 non_exhaustive: NonExhaustive,
496 };
497 z.focus = z.focus_cb(FocusFlag::default());
498 z
499 }
500}
501
502impl TextInputState {
503 fn focus_cb(&self, flag: FocusFlag) -> FocusFlag {
504 let on_focus_lost = self.on_focus_lost.clone();
505 let cursor = self.value.shared_cursor();
506 let scroll_cursor_to_visible = self.scroll_to_cursor.clone();
507 flag.on_lost(move || match on_focus_lost.get() {
508 TextFocusLost::None => {}
509 TextFocusLost::Position0 => {
510 scroll_cursor_to_visible.set(true);
511 let mut new_cursor = cursor.get();
512 new_cursor.cursor.x = 0;
513 new_cursor.anchor.x = 0;
514 cursor.set(new_cursor);
515 }
516 });
517 let on_focus_gained = self.on_focus_gained.clone();
518 let overwrite = self.overwrite.clone();
519 let cursor = self.value.shared_cursor();
520 let scroll_cursor_to_visible = self.scroll_to_cursor.clone();
521 flag.on_gained(move || match on_focus_gained.get() {
522 TextFocusGained::None => {}
523 TextFocusGained::Overwrite => {
524 overwrite.set(true);
525 }
526 TextFocusGained::SelectAll => {
527 scroll_cursor_to_visible.set(true);
528 let mut new_cursor = cursor.get();
529 new_cursor.anchor = TextPosition::new(0, 0);
530 new_cursor.cursor = TextPosition::new(0, 1);
531 cursor.set(new_cursor);
532 }
533 });
534
535 flag
536 }
537}
538
539impl HasFocus for TextInputState {
540 fn build(&self, builder: &mut FocusBuilder) {
541 builder.leaf_widget(self);
542 }
543
544 fn focus(&self) -> FocusFlag {
545 self.focus.clone()
546 }
547
548 fn area(&self) -> Rect {
549 self.area
550 }
551}
552
553impl TextInputState {
554 pub fn new() -> Self {
555 Self::default()
556 }
557
558 pub fn named(name: &str) -> Self {
559 let mut z = Self::default();
560 z.focus = z.focus.with_name(name);
561 z
562 }
563
564 #[inline]
566 pub fn set_invalid(&mut self, invalid: bool) {
567 self.invalid = invalid;
568 }
569
570 #[inline]
572 pub fn invalid(&self) -> bool {
573 self.invalid
574 }
575
576 #[inline]
580 pub fn set_overwrite(&mut self, overwrite: bool) {
581 self.overwrite.set(overwrite);
582 }
583
584 #[inline]
586 pub fn overwrite(&self) -> bool {
587 self.overwrite.get()
588 }
589
590 #[inline]
592 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
593 self.value.set_glyph_ctrl(show_ctrl);
594 }
595
596 pub fn show_ctrl(&self) -> bool {
598 self.value.glyph_ctrl()
599 }
600}
601
602impl TextInputState {
603 #[inline]
606 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
607 match clip {
608 None => self.value.set_clipboard(None),
609 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
610 }
611 }
612
613 #[inline]
616 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
617 self.value.clipboard()
618 }
619
620 #[inline]
622 pub fn copy_to_clip(&mut self) -> bool {
623 let Some(clip) = self.value.clipboard() else {
624 return false;
625 };
626 if self.passwd {
627 return false;
628 }
629
630 _ = clip.set_string(self.selected_text().as_ref());
631 false
632 }
633
634 #[inline]
636 pub fn cut_to_clip(&mut self) -> bool {
637 let Some(clip) = self.value.clipboard() else {
638 return false;
639 };
640 if self.passwd {
641 return false;
642 }
643
644 match clip.set_string(self.selected_text().as_ref()) {
645 Ok(_) => self.delete_range(self.selection()),
646 Err(_) => false,
647 }
648 }
649
650 #[inline]
652 pub fn paste_from_clip(&mut self) -> bool {
653 let Some(clip) = self.value.clipboard() else {
654 return false;
655 };
656
657 if let Ok(text) = clip.get_string() {
658 self.insert_str(text)
659 } else {
660 false
661 }
662 }
663}
664
665impl TextInputState {
666 #[inline]
668 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
669 match undo {
670 None => self.value.set_undo_buffer(None),
671 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
672 }
673 }
674
675 #[inline]
677 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
678 self.value.undo_buffer()
679 }
680
681 #[inline]
683 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
684 self.value.undo_buffer_mut()
685 }
686
687 #[inline]
689 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
690 self.value.recent_replay_log()
691 }
692
693 #[inline]
695 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
696 self.value.replay_log(replay)
697 }
698
699 #[inline]
701 pub fn undo(&mut self) -> bool {
702 self.value.undo()
703 }
704
705 #[inline]
707 pub fn redo(&mut self) -> bool {
708 self.value.redo()
709 }
710}
711
712impl TextInputState {
713 #[inline]
724 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
725 self.value.set_styles(styles);
726 }
727
728 #[inline]
733 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
734 self.value.add_style(range, style);
735 }
736
737 #[inline]
740 pub fn add_range_style(
741 &mut self,
742 range: Range<upos_type>,
743 style: usize,
744 ) -> Result<(), TextError> {
745 let r = self
746 .value
747 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
748 self.value.add_style(r, style);
749 Ok(())
750 }
751
752 #[inline]
754 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
755 self.value.remove_style(range, style);
756 }
757
758 #[inline]
760 pub fn remove_range_style(
761 &mut self,
762 range: Range<upos_type>,
763 style: usize,
764 ) -> Result<(), TextError> {
765 let r = self
766 .value
767 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
768 self.value.remove_style(r, style);
769 Ok(())
770 }
771
772 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
774 self.value.styles_in(range, buf)
775 }
776
777 #[inline]
779 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
780 self.value.styles_at(byte_pos, buf)
781 }
782
783 #[inline]
786 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
787 self.value.styles_at_match(byte_pos, style)
788 }
789
790 #[inline]
792 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
793 self.value.styles()
794 }
795}
796
797impl TextInputState {
798 #[inline]
800 pub fn offset(&self) -> upos_type {
801 self.offset
802 }
803
804 #[inline]
806 pub fn set_offset(&mut self, offset: upos_type) {
807 self.scroll_to_cursor.set(false);
808 self.offset = offset;
809 }
810
811 #[inline]
813 pub fn cursor(&self) -> upos_type {
814 self.value.cursor().x
815 }
816
817 #[inline]
819 pub fn anchor(&self) -> upos_type {
820 self.value.anchor().x
821 }
822
823 #[inline]
826 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
827 self.scroll_cursor_to_visible();
828 self.value
829 .set_cursor(TextPosition::new(cursor, 0), extend_selection)
830 }
831
832 #[inline]
834 pub fn has_selection(&self) -> bool {
835 self.value.has_selection()
836 }
837
838 #[inline]
840 pub fn selection(&self) -> Range<upos_type> {
841 let mut v = self.value.selection();
842 if v.start == TextPosition::new(0, 1) {
843 v.start = TextPosition::new(self.line_width(), 0);
844 }
845 if v.end == TextPosition::new(0, 1) {
846 v.end = TextPosition::new(self.line_width(), 0);
847 }
848 v.start.x..v.end.x
849 }
850
851 #[inline]
854 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
855 self.scroll_cursor_to_visible();
856 self.value
857 .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
858 }
859
860 #[inline]
863 pub fn select_all(&mut self) -> bool {
864 self.scroll_cursor_to_visible();
865 self.value.select_all()
866 }
867
868 #[inline]
870 pub fn selected_text(&self) -> &str {
871 match self.str_slice(self.selection()) {
872 Cow::Borrowed(v) => v,
873 Cow::Owned(_) => {
874 unreachable!()
875 }
876 }
877 }
878}
879
880impl TextInputState {
881 #[inline]
883 pub fn is_empty(&self) -> bool {
884 self.value.is_empty()
885 }
886
887 #[inline]
889 pub fn value<T: for<'a> From<&'a str>>(&self) -> T {
890 self.value.text().as_str().into()
891 }
892
893 #[inline]
895 pub fn text(&self) -> &str {
896 self.value.text().as_str()
897 }
898
899 #[inline]
901 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
902 self.value.str_slice_byte(range).expect("valid_range")
903 }
904
905 #[inline]
907 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
908 self.value.str_slice_byte(range)
909 }
910
911 #[inline]
913 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
914 self.value
915 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
916 .expect("valid_range")
917 }
918
919 #[inline]
921 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
922 self.value
923 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
924 }
925
926 #[inline]
928 pub fn len(&self) -> upos_type {
929 self.value.line_width(0).expect("valid_row")
930 }
931
932 #[inline]
934 pub fn len_bytes(&self) -> usize {
935 self.value.len_bytes()
936 }
937
938 #[inline]
940 pub fn line_width(&self) -> upos_type {
941 self.value.line_width(0).expect("valid_row")
942 }
943
944 #[inline]
946 pub fn text_graphemes(&self, pos: upos_type) -> <TextString as TextStore>::GraphemeIter<'_> {
947 self.value
948 .text_graphemes(TextPosition::new(pos, 0))
949 .expect("valid_pos")
950 }
951
952 #[inline]
954 pub fn try_text_graphemes(
955 &self,
956 pos: upos_type,
957 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
958 self.value.text_graphemes(TextPosition::new(pos, 0))
959 }
960
961 #[inline]
963 pub fn graphemes(
964 &self,
965 range: Range<upos_type>,
966 pos: upos_type,
967 ) -> <TextString as TextStore>::GraphemeIter<'_> {
968 self.value
969 .graphemes(
970 TextRange::new((range.start, 0), (range.end, 0)),
971 TextPosition::new(pos, 0),
972 )
973 .expect("valid_args")
974 }
975
976 #[inline]
978 pub fn try_graphemes(
979 &self,
980 range: Range<upos_type>,
981 pos: upos_type,
982 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
983 self.value.graphemes(
984 TextRange::new((range.start, 0), (range.end, 0)),
985 TextPosition::new(pos, 0),
986 )
987 }
988
989 #[inline]
992 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
993 self.value
994 .byte_at(TextPosition::new(pos, 0))
995 .expect("valid_pos")
996 }
997
998 #[inline]
1001 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
1002 self.value.byte_at(TextPosition::new(pos, 0))
1003 }
1004
1005 #[inline]
1007 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
1008 self.value
1009 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
1010 .expect("valid_range")
1011 }
1012
1013 #[inline]
1015 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
1016 self.value
1017 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
1018 }
1019
1020 #[inline]
1023 pub fn byte_pos(&self, byte: usize) -> upos_type {
1024 self.value.byte_pos(byte).map(|v| v.x).expect("valid_pos")
1025 }
1026
1027 #[inline]
1030 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
1031 self.value.byte_pos(byte).map(|v| v.x)
1032 }
1033
1034 #[inline]
1036 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
1037 self.value
1038 .byte_range(bytes)
1039 .map(|v| v.start.x..v.end.x)
1040 .expect("valid_range")
1041 }
1042
1043 #[inline]
1045 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
1046 self.value.byte_range(bytes).map(|v| v.start.x..v.end.x)
1047 }
1048}
1049
1050impl TextInputState {
1051 #[inline]
1053 pub fn clear(&mut self) -> bool {
1054 if self.is_empty() {
1055 false
1056 } else {
1057 self.offset = 0;
1058 self.value.clear();
1059 true
1060 }
1061 }
1062
1063 #[inline]
1067 pub fn set_value<S: Into<String>>(&mut self, s: S) {
1068 self.offset = 0;
1069 self.value.set_text(TextString::new_string(s.into()));
1070 }
1071
1072 #[inline]
1076 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1077 self.offset = 0;
1078 self.value.set_text(TextString::new_string(s.into()));
1079 }
1080
1081 #[inline]
1083 pub fn insert_char(&mut self, c: char) -> bool {
1084 if self.has_selection() {
1085 self.value
1086 .remove_str_range(self.value.selection())
1087 .expect("valid_selection");
1088 }
1089 if c == '\n' {
1090 return false;
1091 } else {
1092 self.value
1093 .insert_char(self.value.cursor(), c)
1094 .expect("valid_cursor");
1095 }
1096 self.scroll_cursor_to_visible();
1097 true
1098 }
1099
1100 #[inline]
1102 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1103 let t = t.as_ref();
1104 if self.has_selection() {
1105 self.value
1106 .remove_str_range(self.value.selection())
1107 .expect("valid_selection");
1108 }
1109 self.value
1110 .insert_str(self.value.cursor(), t)
1111 .expect("valid_cursor");
1112 self.scroll_cursor_to_visible();
1113 true
1114 }
1115
1116 #[inline]
1118 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1119 self.try_delete_range(range).expect("valid_range")
1120 }
1121
1122 #[inline]
1124 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1125 if !range.is_empty() {
1126 self.value
1127 .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1128 self.scroll_cursor_to_visible();
1129 Ok(true)
1130 } else {
1131 Ok(false)
1132 }
1133 }
1134}
1135
1136impl TextInputState {
1137 #[inline]
1139 pub fn delete_next_char(&mut self) -> bool {
1140 if self.has_selection() {
1141 self.delete_range(self.selection())
1142 } else {
1143 let pos = self.value.cursor();
1144 let r = remove_next_char(&mut self.value, pos).expect("valid_cursor");
1145 self.scroll_cursor_to_visible();
1146 r
1147 }
1148 }
1149
1150 #[inline]
1152 pub fn delete_prev_char(&mut self) -> bool {
1153 if self.value.has_selection() {
1154 self.delete_range(self.selection())
1155 } else {
1156 let pos = self.value.cursor();
1157 let r = remove_prev_char(&mut self.value, pos).expect("valid_cursor");
1158 self.scroll_cursor_to_visible();
1159 r
1160 }
1161 }
1162
1163 pub fn next_word_start(&self, pos: upos_type) -> upos_type {
1165 self.try_next_word_start(pos).expect("valid_pos")
1166 }
1167
1168 pub fn try_next_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1170 next_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1171 }
1172
1173 pub fn next_word_end(&self, pos: upos_type) -> upos_type {
1176 self.try_next_word_end(pos).expect("valid_pos")
1177 }
1178
1179 pub fn try_next_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1182 next_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1183 }
1184
1185 pub fn prev_word_start(&self, pos: upos_type) -> upos_type {
1189 self.try_prev_word_start(pos).expect("valid_pos")
1190 }
1191
1192 pub fn try_prev_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1196 prev_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1197 }
1198
1199 pub fn prev_word_end(&self, pos: upos_type) -> upos_type {
1203 self.try_prev_word_end(pos).expect("valid_pos")
1204 }
1205
1206 pub fn try_prev_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1210 prev_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1211 }
1212
1213 pub fn is_word_boundary(&self, pos: upos_type) -> bool {
1215 self.try_is_word_boundary(pos).expect("valid_pos")
1216 }
1217
1218 pub fn try_is_word_boundary(&self, pos: upos_type) -> Result<bool, TextError> {
1220 is_word_boundary(&self.value, TextPosition::new(pos, 0))
1221 }
1222
1223 pub fn word_start(&self, pos: upos_type) -> upos_type {
1225 self.try_word_start(pos).expect("valid_pos")
1226 }
1227
1228 pub fn try_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1230 word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1231 }
1232
1233 pub fn word_end(&self, pos: upos_type) -> upos_type {
1235 self.try_word_end(pos).expect("valid_pos")
1236 }
1237
1238 pub fn try_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1240 word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1241 }
1242
1243 #[inline]
1245 pub fn delete_next_word(&mut self) -> bool {
1246 if self.has_selection() {
1247 self.delete_range(self.selection())
1248 } else {
1249 let cursor = self.cursor();
1250
1251 let start = self.next_word_start(cursor);
1252 if start != cursor {
1253 self.delete_range(cursor..start)
1254 } else {
1255 let end = self.next_word_end(cursor);
1256 self.delete_range(cursor..end)
1257 }
1258 }
1259 }
1260
1261 #[inline]
1263 pub fn delete_prev_word(&mut self) -> bool {
1264 if self.has_selection() {
1265 self.delete_range(self.selection())
1266 } else {
1267 let cursor = self.cursor();
1268
1269 let end = self.prev_word_end(cursor);
1270 if end != cursor {
1271 self.delete_range(end..cursor)
1272 } else {
1273 let start = self.prev_word_start(cursor);
1274 self.delete_range(start..cursor)
1275 }
1276 }
1277 }
1278
1279 #[inline]
1281 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1282 let c = min(self.cursor() + 1, self.len());
1283 self.set_cursor(c, extend_selection)
1284 }
1285
1286 #[inline]
1288 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1289 let c = self.cursor().saturating_sub(1);
1290 self.set_cursor(c, extend_selection)
1291 }
1292
1293 #[inline]
1295 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1296 self.set_cursor(0, extend_selection)
1297 }
1298
1299 #[inline]
1301 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1302 self.set_cursor(self.len(), extend_selection)
1303 }
1304
1305 #[inline]
1306 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1307 let cursor = self.cursor();
1308 let end = self.next_word_end(cursor);
1309 self.set_cursor(end, extend_selection)
1310 }
1311
1312 #[inline]
1313 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1314 let cursor = self.cursor();
1315 let start = self.prev_word_start(cursor);
1316 self.set_cursor(start, extend_selection)
1317 }
1318}
1319
1320impl HasScreenCursor for TextInputState {
1321 #[inline]
1323 fn screen_cursor(&self) -> Option<(u16, u16)> {
1324 if self.is_focused() {
1325 if self.has_selection() {
1326 None
1327 } else {
1328 let cx = self.cursor();
1329 let ox = self.offset();
1330
1331 if cx < ox {
1332 None
1333 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1334 None
1335 } else {
1336 self.col_to_screen(cx)
1337 .map(|sc| (self.inner.x + sc, self.inner.y))
1338 }
1339 }
1340 } else {
1341 None
1342 }
1343 }
1344}
1345
1346impl RelocatableState for TextInputState {
1347 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1348 self.area.relocate(shift, clip);
1349 self.inner.relocate(shift, clip);
1350 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1352 }
1353}
1354
1355impl TextInputState {
1356 fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1357 let (text_wrap, left_margin, right_margin, word_margin) = (
1358 TextWrap2::Shift,
1359 self.offset() as upos_type,
1360 self.offset() as upos_type + self.rendered.width as upos_type,
1361 self.offset() as upos_type + self.rendered.width as upos_type,
1362 );
1363 self.value
1364 .glyphs2(
1365 self.rendered,
1366 0,
1367 0..1,
1368 0, text_wrap,
1370 false,
1371 left_margin,
1372 right_margin,
1373 word_margin,
1374 )
1375 .expect("valid-row")
1376 }
1377
1378 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1381 let ox = self.offset();
1382
1383 let scx = scx + self.dark_offset.0 as i16;
1384
1385 if scx < 0 {
1386 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1387 } else if scx as u16 >= self.rendered.width {
1388 min(ox + scx as upos_type, self.len())
1389 } else {
1390 let scx = scx as u16;
1391
1392 let line = self.glyphs2();
1393
1394 let mut col = ox;
1395 for g in line {
1396 if g.contains_screen_x(scx) {
1397 break;
1398 }
1399 col = g.pos().x + 1;
1400 }
1401 col
1402 }
1403 }
1404
1405 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1408 let ox = self.offset();
1409
1410 if pos < ox {
1411 return None;
1412 }
1413
1414 let line = self.glyphs2();
1415 let mut screen_x = 0;
1416 for g in line {
1417 if g.pos().x == pos {
1418 break;
1419 }
1420 screen_x = g.screen_pos().0 + g.screen_width();
1421 }
1422
1423 if screen_x >= self.dark_offset.0 {
1424 Some(screen_x - self.dark_offset.0)
1425 } else {
1426 None
1427 }
1428 }
1429
1430 #[inline]
1434 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1435 let scx = cursor;
1436
1437 let cx = self.screen_to_col(scx);
1438
1439 self.set_cursor(cx, extend_selection)
1440 }
1441
1442 pub fn set_screen_cursor_words(&mut self, screen_cursor: i16, extend_selection: bool) -> bool {
1449 let anchor = self.anchor();
1450
1451 let cx = self.screen_to_col(screen_cursor);
1452 let cursor = cx;
1453
1454 let cursor = if cursor < anchor {
1455 self.word_start(cursor)
1456 } else {
1457 self.word_end(cursor)
1458 };
1459
1460 if !self.is_word_boundary(anchor) {
1462 if cursor < anchor {
1463 self.set_cursor(self.word_end(anchor), false);
1464 } else {
1465 self.set_cursor(self.word_start(anchor), false);
1466 }
1467 }
1468
1469 self.set_cursor(cursor, extend_selection)
1470 }
1471
1472 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1474 self.set_offset(self.offset.saturating_sub(delta));
1475 true
1476 }
1477
1478 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1480 self.set_offset(self.offset + delta);
1481 true
1482 }
1483
1484 pub fn scroll_cursor_to_visible(&mut self) {
1486 self.scroll_to_cursor.set(true);
1487 }
1488}
1489
1490impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextInputState {
1491 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1492 fn tc(r: bool) -> TextOutcome {
1494 if r {
1495 TextOutcome::TextChanged
1496 } else {
1497 TextOutcome::Unchanged
1498 }
1499 }
1500 fn overwrite(state: &mut TextInputState) {
1501 if state.overwrite.get() {
1502 state.overwrite.set(false);
1503 state.clear();
1504 }
1505 }
1506 fn clear_overwrite(state: &mut TextInputState) {
1507 state.overwrite.set(false);
1508 }
1509
1510 let mut r = if self.is_focused() {
1534 match event {
1535 ct_event!(key press c)
1536 | ct_event!(key press SHIFT-c)
1537 | ct_event!(key press CONTROL_ALT-c) => {
1538 overwrite(self);
1539 tc(self.insert_char(*c))
1540 }
1541 ct_event!(keycode press Backspace) | ct_event!(keycode press SHIFT-Backspace) => {
1542 clear_overwrite(self);
1543 tc(self.delete_prev_char())
1544 }
1545 ct_event!(keycode press Delete) | ct_event!(keycode press SHIFT-Delete) => {
1546 clear_overwrite(self);
1547 tc(self.delete_next_char())
1548 }
1549 ct_event!(keycode press CONTROL-Backspace)
1550 | ct_event!(keycode press ALT-Backspace) => {
1551 clear_overwrite(self);
1552 tc(self.delete_prev_word())
1553 }
1554 ct_event!(keycode press CONTROL-Delete) => {
1555 clear_overwrite(self);
1556 tc(self.delete_next_word())
1557 }
1558 ct_event!(key press CONTROL-'x') => {
1559 clear_overwrite(self);
1560 tc(self.cut_to_clip())
1561 }
1562 ct_event!(key press CONTROL-'v') => {
1563 overwrite(self);
1564 tc(self.paste_from_clip())
1565 }
1566 ct_event!(key press CONTROL-'d') => {
1567 clear_overwrite(self);
1568 tc(self.clear())
1569 }
1570 ct_event!(key press CONTROL-'z') => {
1571 clear_overwrite(self);
1572 tc(self.undo())
1573 }
1574 ct_event!(key press CONTROL_SHIFT-'Z') => {
1575 clear_overwrite(self);
1576 tc(self.redo())
1577 }
1578
1579 ct_event!(key release _)
1580 | ct_event!(key release SHIFT-_)
1581 | ct_event!(key release CONTROL_ALT-_)
1582 | ct_event!(keycode release Tab)
1583 | ct_event!(keycode release Backspace)
1584 | ct_event!(keycode release Delete)
1585 | ct_event!(keycode release CONTROL-Backspace)
1586 | ct_event!(keycode release ALT-Backspace)
1587 | ct_event!(keycode release CONTROL-Delete)
1588 | ct_event!(key release CONTROL-'x')
1589 | ct_event!(key release CONTROL-'v')
1590 | ct_event!(key release CONTROL-'d')
1591 | ct_event!(key release CONTROL-'y')
1592 | ct_event!(key release CONTROL-'z')
1593 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1594
1595 _ => TextOutcome::Continue,
1596 }
1597 } else {
1598 TextOutcome::Continue
1599 };
1600 if r == TextOutcome::Continue {
1601 r = self.handle(event, ReadOnly);
1602 }
1603 r
1604 }
1605}
1606
1607impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextInputState {
1608 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1609 fn clear_overwrite(state: &mut TextInputState) {
1610 state.overwrite.set(false);
1611 }
1612
1613 let mut r = if self.is_focused() {
1614 match event {
1615 ct_event!(keycode press Left) => {
1616 clear_overwrite(self);
1617 self.move_left(false).into()
1618 }
1619 ct_event!(keycode press Right) => {
1620 clear_overwrite(self);
1621 self.move_right(false).into()
1622 }
1623 ct_event!(keycode press CONTROL-Left) => {
1624 clear_overwrite(self);
1625 self.move_to_prev_word(false).into()
1626 }
1627 ct_event!(keycode press CONTROL-Right) => {
1628 clear_overwrite(self);
1629 self.move_to_next_word(false).into()
1630 }
1631 ct_event!(keycode press Home) => {
1632 clear_overwrite(self);
1633 self.move_to_line_start(false).into()
1634 }
1635 ct_event!(keycode press End) => {
1636 clear_overwrite(self);
1637 self.move_to_line_end(false).into()
1638 }
1639 ct_event!(keycode press SHIFT-Left) => {
1640 clear_overwrite(self);
1641 self.move_left(true).into()
1642 }
1643 ct_event!(keycode press SHIFT-Right) => {
1644 clear_overwrite(self);
1645 self.move_right(true).into()
1646 }
1647 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1648 clear_overwrite(self);
1649 self.move_to_prev_word(true).into()
1650 }
1651 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1652 clear_overwrite(self);
1653 self.move_to_next_word(true).into()
1654 }
1655 ct_event!(keycode press SHIFT-Home) => {
1656 clear_overwrite(self);
1657 self.move_to_line_start(true).into()
1658 }
1659 ct_event!(keycode press SHIFT-End) => {
1660 clear_overwrite(self);
1661 self.move_to_line_end(true).into()
1662 }
1663 ct_event!(keycode press ALT-Left) => {
1664 clear_overwrite(self);
1665 self.scroll_left(1).into()
1666 }
1667 ct_event!(keycode press ALT-Right) => {
1668 clear_overwrite(self);
1669 self.scroll_right(1).into()
1670 }
1671 ct_event!(key press CONTROL-'a') => {
1672 clear_overwrite(self);
1673 self.select_all().into()
1674 }
1675 ct_event!(key press CONTROL-'c') => {
1676 clear_overwrite(self);
1677 self.copy_to_clip().into()
1678 }
1679
1680 ct_event!(keycode release Left)
1681 | ct_event!(keycode release Right)
1682 | ct_event!(keycode release CONTROL-Left)
1683 | ct_event!(keycode release CONTROL-Right)
1684 | ct_event!(keycode release Home)
1685 | ct_event!(keycode release End)
1686 | ct_event!(keycode release SHIFT-Left)
1687 | ct_event!(keycode release SHIFT-Right)
1688 | ct_event!(keycode release CONTROL_SHIFT-Left)
1689 | ct_event!(keycode release CONTROL_SHIFT-Right)
1690 | ct_event!(keycode release SHIFT-Home)
1691 | ct_event!(keycode release SHIFT-End)
1692 | ct_event!(key release CONTROL-'a')
1693 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1694
1695 _ => TextOutcome::Continue,
1696 }
1697 } else {
1698 TextOutcome::Continue
1699 };
1700
1701 if r == TextOutcome::Continue {
1702 r = self.handle(event, MouseOnly);
1703 }
1704 r
1705 }
1706}
1707
1708impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextInputState {
1709 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1710 fn clear_overwrite(state: &mut TextInputState) {
1711 state.overwrite.set(false);
1712 }
1713
1714 match event {
1715 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1716 let c = (m.column as i16) - (self.inner.x as i16);
1717 clear_overwrite(self);
1718 self.set_screen_cursor(c, true).into()
1719 }
1720 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1721 let cx = m.column as i16 - self.inner.x as i16;
1722 clear_overwrite(self);
1723 self.set_screen_cursor_words(cx, true).into()
1724 }
1725 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1726 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1727 let start = self.word_start(tx);
1728 let end = self.word_end(tx);
1729 clear_overwrite(self);
1730 self.set_selection(start, end).into()
1731 }
1732 ct_event!(mouse down Left for column,row) => {
1733 if self.gained_focus() {
1734 TextOutcome::Unchanged
1737 } else if self.inner.contains((*column, *row).into()) {
1738 let c = (column - self.inner.x) as i16;
1739 clear_overwrite(self);
1740 self.set_screen_cursor(c, false).into()
1741 } else {
1742 TextOutcome::Continue
1743 }
1744 }
1745 ct_event!(mouse down CONTROL-Left for column,row) => {
1746 if self.inner.contains((*column, *row).into()) {
1747 let cx = (column - self.inner.x) as i16;
1748 clear_overwrite(self);
1749 self.set_screen_cursor(cx, true).into()
1750 } else {
1751 TextOutcome::Continue
1752 }
1753 }
1754 ct_event!(mouse down ALT-Left for column,row) => {
1755 if self.inner.contains((*column, *row).into()) {
1756 let cx = (column - self.inner.x) as i16;
1757 clear_overwrite(self);
1758 self.set_screen_cursor_words(cx, true).into()
1759 } else {
1760 TextOutcome::Continue
1761 }
1762 }
1763 _ => TextOutcome::Continue,
1764 }
1765 }
1766}
1767
1768pub fn handle_events(
1772 state: &mut TextInputState,
1773 focus: bool,
1774 event: &crossterm::event::Event,
1775) -> TextOutcome {
1776 state.focus.set(focus);
1777 state.handle(event, Regular)
1778}
1779
1780pub fn handle_readonly_events(
1784 state: &mut TextInputState,
1785 focus: bool,
1786 event: &crossterm::event::Event,
1787) -> TextOutcome {
1788 state.focus.set(focus);
1789 state.handle(event, ReadOnly)
1790}
1791
1792pub fn handle_mouse_events(
1794 state: &mut TextInputState,
1795 event: &crossterm::event::Event,
1796) -> TextOutcome {
1797 state.handle(event, MouseOnly)
1798}