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_area, 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::ops::Range;
42use std::rc::Rc;
43
44#[derive(Debug, Default, Clone)]
50pub struct TextInput<'a> {
51 block: Option<Block<'a>>,
52 style: Style,
53 focus_style: Option<Style>,
54 select_style: Option<Style>,
55 invalid_style: Option<Style>,
56 on_focus_gained: TextFocusGained,
57 on_focus_lost: TextFocusLost,
58 passwd: bool,
59 text_style: Vec<Style>,
60}
61
62#[derive(Debug)]
64pub struct TextInputState {
65 pub area: Rect,
68 pub inner: Rect,
71 pub rendered: Size,
75
76 pub offset: upos_type,
79 pub dark_offset: (u16, u16),
82 pub scroll_to_cursor: Rc<Cell<bool>>,
84
85 pub value: TextCore<TextString>,
87 pub invalid: bool,
90 pub passwd: bool,
93 pub overwrite: Rc<Cell<bool>>,
97 pub on_focus_gained: Rc<Cell<TextFocusGained>>,
100 pub on_focus_lost: Rc<Cell<TextFocusLost>>,
103
104 pub focus: FocusFlag,
107
108 pub mouse: MouseFlags,
111
112 pub non_exhaustive: NonExhaustive,
114}
115
116impl<'a> TextInput<'a> {
117 pub fn new() -> Self {
119 Self::default()
120 }
121
122 #[inline]
124 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
125 if let Some(styles) = styles {
126 self.styles(styles)
127 } else {
128 self
129 }
130 }
131
132 #[inline]
134 pub fn styles(mut self, styles: TextStyle) -> Self {
135 self.style = styles.style;
136 if styles.focus.is_some() {
137 self.focus_style = styles.focus;
138 }
139 if styles.select.is_some() {
140 self.select_style = styles.select;
141 }
142 if styles.invalid.is_some() {
143 self.invalid_style = styles.invalid;
144 }
145 if let Some(of) = styles.on_focus_gained {
146 self.on_focus_gained = of;
147 }
148 if let Some(of) = styles.on_focus_lost {
149 self.on_focus_lost = of;
150 }
151 if let Some(border_style) = styles.border_style {
152 self.block = self.block.map(|v| v.border_style(border_style));
153 }
154 self.block = self.block.map(|v| v.style(self.style));
155 if styles.block.is_some() {
156 self.block = styles.block;
157 }
158 self.block = self.block.map(|v| v.style(self.style));
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<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
196 self.text_style = styles.into_iter().collect();
197 self
198 }
199
200 #[inline]
202 pub fn block(mut self, block: Block<'a>) -> Self {
203 self.block = Some(block);
204 self
205 }
206
207 #[inline]
209 pub fn passwd(mut self) -> Self {
210 self.passwd = true;
211 self
212 }
213
214 #[inline]
216 pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
217 self.on_focus_gained = of;
218 self
219 }
220
221 #[inline]
223 pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
224 self.on_focus_lost = of;
225 self
226 }
227}
228
229impl<'a> StatefulWidget for &TextInput<'a> {
230 type State = TextInputState;
231
232 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
233 render_ref(self, area, buf, state);
234 }
235}
236
237impl StatefulWidget for TextInput<'_> {
238 type State = TextInputState;
239
240 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
241 render_ref(&self, area, buf, state);
242 }
243}
244
245fn render_ref(widget: &TextInput<'_>, area: Rect, buf: &mut Buffer, state: &mut TextInputState) {
246 state.area = area;
247 state.inner = widget.block.inner_if_some(area);
248 state.rendered = state.inner.as_size();
249 state.passwd = widget.passwd;
250 state.on_focus_gained.set(widget.on_focus_gained);
251 state.on_focus_lost.set(widget.on_focus_lost);
252
253 if state.scroll_to_cursor.get() {
254 let c = state.cursor();
255 let o = state.offset();
256
257 if state.rendered.width > 0 {
258 let mut no = if c < o {
259 c
260 } else if c >= o + state.rendered.width as upos_type {
261 c.saturating_sub(state.rendered.width as upos_type)
262 } else {
263 o
264 };
265 if c == no + state.rendered.width as upos_type {
268 no = no.saturating_add(1);
269 }
270 state.set_offset(no);
271 } else {
272 }
274 }
275
276 let style = widget.style;
277 let focus_style = if let Some(focus_style) = widget.focus_style {
278 focus_style
279 } else {
280 style
281 };
282 let select_style = if let Some(select_style) = widget.select_style {
283 select_style
284 } else {
285 Style::default().black().on_yellow()
286 };
287 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
288 invalid_style
289 } else {
290 Style::default().red()
291 };
292
293 let (style, select_style) = if state.focus.get() {
294 if state.invalid {
295 (
296 style.patch(focus_style).patch(invalid_style),
297 style
298 .patch(focus_style)
299 .patch(select_style)
300 .patch(invalid_style),
301 )
302 } else {
303 (
304 style.patch(focus_style),
305 style.patch(focus_style).patch(select_style),
306 )
307 }
308 } else {
309 if state.invalid {
310 (
311 style.patch(invalid_style),
312 style.patch(select_style).patch(invalid_style),
313 )
314 } else {
315 (style, style.patch(select_style))
316 }
317 };
318
319 if let Some(block) = &widget.block {
321 block.render(area, buf);
322 }
323 buf.set_style(state.inner, style);
324
325 if state.inner.width == 0 || state.inner.height == 0 {
326 return;
328 }
329
330 let ox = state.offset() as u16;
331 let show_range = {
333 let start = min(ox as upos_type, state.len());
334 let end = min(start + state.inner.width as upos_type, state.len());
335 state.bytes_at_range(start..end)
336 };
337 let selection = state.selection();
338 let mut styles = Vec::new();
339
340 if widget.passwd {
341 for g in state.glyphs2() {
343 if g.screen_width() > 0 {
344 let mut style = style;
345 state
346 .value
347 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
348 for style_nr in &styles {
349 if let Some(s) = widget.text_style.get(*style_nr) {
350 style = style.patch(*s);
351 }
352 }
353 if selection.contains(&g.pos().x) {
355 style = style.patch(select_style);
356 };
357
358 let screen_pos = g.screen_pos();
360
361 if let Some(cell) =
363 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
364 {
365 cell.set_symbol("*");
366 cell.set_style(style);
367 }
368 for d in 1..g.screen_width() {
370 if let Some(cell) = buf.cell_mut((
371 state.inner.x + screen_pos.0 + d,
372 state.inner.y + screen_pos.1,
373 )) {
374 cell.reset();
375 cell.set_style(style);
376 }
377 }
378 }
379 }
380 } else {
381 for g in state.glyphs2() {
382 if g.screen_width() > 0 {
383 let mut style = style;
384 state
385 .value
386 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
387 for style_nr in &styles {
388 if let Some(s) = widget.text_style.get(*style_nr) {
389 style = style.patch(*s);
390 }
391 }
392 if selection.contains(&g.pos().x) {
394 style = style.patch(select_style);
395 };
396
397 let screen_pos = g.screen_pos();
399
400 if let Some(cell) =
402 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
403 {
404 cell.set_symbol(g.glyph());
405 cell.set_style(style);
406 }
407 for d in 1..g.screen_width() {
409 if let Some(cell) = buf.cell_mut((
410 state.inner.x + screen_pos.0 + d,
411 state.inner.y + screen_pos.1,
412 )) {
413 cell.reset();
414 cell.set_style(style);
415 }
416 }
417 }
418 }
419 }
420}
421
422impl Clone for TextInputState {
423 fn clone(&self) -> Self {
424 Self {
425 area: self.area,
426 inner: self.inner,
427 rendered: self.rendered,
428 offset: self.offset,
429 dark_offset: self.dark_offset,
430 scroll_to_cursor: Rc::new(Cell::new(self.scroll_to_cursor.get())),
431 value: self.value.clone(),
432 invalid: self.invalid,
433 passwd: self.passwd,
434 overwrite: Rc::new(Cell::new(self.overwrite.get())),
435 on_focus_gained: Rc::new(Cell::new(self.on_focus_gained.get())),
436 on_focus_lost: Rc::new(Cell::new(self.on_focus_lost.get())),
437 focus: self.focus_cb(FocusFlag::named(self.focus.name())),
438 mouse: Default::default(),
439 non_exhaustive: NonExhaustive,
440 }
441 }
442}
443
444impl Default for TextInputState {
445 fn default() -> Self {
446 let value = TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard()));
447
448 let mut z = Self {
449 area: Default::default(),
450 inner: Default::default(),
451 rendered: Default::default(),
452 offset: Default::default(),
453 dark_offset: Default::default(),
454 scroll_to_cursor: Default::default(),
455 value,
456 invalid: Default::default(),
457 passwd: Default::default(),
458 overwrite: Default::default(),
459 on_focus_gained: Default::default(),
460 on_focus_lost: Default::default(),
461 focus: Default::default(),
462 mouse: Default::default(),
463 non_exhaustive: NonExhaustive,
464 };
465 z.focus = z.focus_cb(FocusFlag::default());
466 z
467 }
468}
469
470impl TextInputState {
471 fn focus_cb(&self, flag: FocusFlag) -> FocusFlag {
472 let on_focus_lost = self.on_focus_lost.clone();
473 let cursor = self.value.shared_cursor();
474 let scroll_cursor_to_visible = self.scroll_to_cursor.clone();
475 flag.on_lost(move || match on_focus_lost.get() {
476 TextFocusLost::None => {}
477 TextFocusLost::Position0 => {
478 scroll_cursor_to_visible.set(true);
479 let mut new_cursor = cursor.get();
480 new_cursor.cursor.x = 0;
481 new_cursor.anchor.x = 0;
482 cursor.set(new_cursor);
483 }
484 });
485 let on_focus_gained = self.on_focus_gained.clone();
486 let overwrite = self.overwrite.clone();
487 let cursor = self.value.shared_cursor();
488 let scroll_cursor_to_visible = self.scroll_to_cursor.clone();
489 flag.on_gained(move || match on_focus_gained.get() {
490 TextFocusGained::None => {}
491 TextFocusGained::Overwrite => {
492 overwrite.set(true);
493 }
494 TextFocusGained::SelectAll => {
495 scroll_cursor_to_visible.set(true);
496 let mut new_cursor = cursor.get();
497 new_cursor.anchor = TextPosition::new(0, 0);
498 new_cursor.cursor = TextPosition::new(0, 1);
499 cursor.set(new_cursor);
500 }
501 });
502
503 flag
504 }
505}
506
507impl HasFocus for TextInputState {
508 fn build(&self, builder: &mut FocusBuilder) {
509 builder.leaf_widget(self);
510 }
511
512 fn focus(&self) -> FocusFlag {
513 self.focus.clone()
514 }
515
516 fn area(&self) -> Rect {
517 self.area
518 }
519}
520
521impl TextInputState {
522 pub fn new() -> Self {
523 Self::default()
524 }
525
526 pub fn named(name: &str) -> Self {
527 let mut z = Self::default();
528 z.focus = z.focus_cb(FocusFlag::named(name));
529 z
530 }
531
532 #[inline]
534 pub fn set_invalid(&mut self, invalid: bool) {
535 self.invalid = invalid;
536 }
537
538 #[inline]
540 pub fn invalid(&self) -> bool {
541 self.invalid
542 }
543
544 #[inline]
548 pub fn set_overwrite(&mut self, overwrite: bool) {
549 self.overwrite.set(overwrite);
550 }
551
552 #[inline]
554 pub fn overwrite(&self) -> bool {
555 self.overwrite.get()
556 }
557
558 #[inline]
560 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
561 self.value.set_glyph_ctrl(show_ctrl);
562 }
563
564 pub fn show_ctrl(&self) -> bool {
566 self.value.glyph_ctrl()
567 }
568}
569
570impl TextInputState {
571 #[inline]
574 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
575 match clip {
576 None => self.value.set_clipboard(None),
577 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
578 }
579 }
580
581 #[inline]
584 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
585 self.value.clipboard()
586 }
587
588 #[inline]
590 pub fn copy_to_clip(&mut self) -> bool {
591 let Some(clip) = self.value.clipboard() else {
592 return false;
593 };
594 if self.passwd {
595 return false;
596 }
597
598 _ = clip.set_string(self.selected_text().as_ref());
599 false
600 }
601
602 #[inline]
604 pub fn cut_to_clip(&mut self) -> bool {
605 let Some(clip) = self.value.clipboard() else {
606 return false;
607 };
608 if self.passwd {
609 return false;
610 }
611
612 match clip.set_string(self.selected_text().as_ref()) {
613 Ok(_) => self.delete_range(self.selection()),
614 Err(_) => false,
615 }
616 }
617
618 #[inline]
620 pub fn paste_from_clip(&mut self) -> bool {
621 let Some(clip) = self.value.clipboard() else {
622 return false;
623 };
624
625 if let Ok(text) = clip.get_string() {
626 self.insert_str(text)
627 } else {
628 false
629 }
630 }
631}
632
633impl TextInputState {
634 #[inline]
636 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
637 match undo {
638 None => self.value.set_undo_buffer(None),
639 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
640 }
641 }
642
643 #[inline]
645 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
646 self.value.undo_buffer()
647 }
648
649 #[inline]
651 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
652 self.value.undo_buffer_mut()
653 }
654
655 #[inline]
657 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
658 self.value.recent_replay_log()
659 }
660
661 #[inline]
663 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
664 self.value.replay_log(replay)
665 }
666
667 #[inline]
669 pub fn undo(&mut self) -> bool {
670 self.value.undo()
671 }
672
673 #[inline]
675 pub fn redo(&mut self) -> bool {
676 self.value.redo()
677 }
678}
679
680impl TextInputState {
681 #[inline]
692 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
693 self.value.set_styles(styles);
694 }
695
696 #[inline]
701 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
702 self.value.add_style(range, style);
703 }
704
705 #[inline]
708 pub fn add_range_style(
709 &mut self,
710 range: Range<upos_type>,
711 style: usize,
712 ) -> Result<(), TextError> {
713 let r = self
714 .value
715 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
716 self.value.add_style(r, style);
717 Ok(())
718 }
719
720 #[inline]
722 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
723 self.value.remove_style(range, style);
724 }
725
726 #[inline]
728 pub fn remove_range_style(
729 &mut self,
730 range: Range<upos_type>,
731 style: usize,
732 ) -> Result<(), TextError> {
733 let r = self
734 .value
735 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
736 self.value.remove_style(r, style);
737 Ok(())
738 }
739
740 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
742 self.value.styles_in(range, buf)
743 }
744
745 #[inline]
747 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
748 self.value.styles_at(byte_pos, buf)
749 }
750
751 #[inline]
754 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
755 self.value.styles_at_match(byte_pos, style)
756 }
757
758 #[inline]
760 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
761 self.value.styles()
762 }
763}
764
765impl TextInputState {
766 #[inline]
768 pub fn offset(&self) -> upos_type {
769 self.offset
770 }
771
772 #[inline]
774 pub fn set_offset(&mut self, offset: upos_type) {
775 self.scroll_to_cursor.set(false);
776 self.offset = offset;
777 }
778
779 #[inline]
781 pub fn cursor(&self) -> upos_type {
782 self.value.cursor().x
783 }
784
785 #[inline]
787 pub fn anchor(&self) -> upos_type {
788 self.value.anchor().x
789 }
790
791 #[inline]
794 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
795 self.scroll_cursor_to_visible();
796 self.value
797 .set_cursor(TextPosition::new(cursor, 0), extend_selection)
798 }
799
800 #[inline]
802 pub fn has_selection(&self) -> bool {
803 self.value.has_selection()
804 }
805
806 #[inline]
808 pub fn selection(&self) -> Range<upos_type> {
809 let mut v = self.value.selection();
810 if v.start == TextPosition::new(0, 1) {
811 v.start = TextPosition::new(self.line_width(), 0);
812 }
813 if v.end == TextPosition::new(0, 1) {
814 v.end = TextPosition::new(self.line_width(), 0);
815 }
816 v.start.x..v.end.x
817 }
818
819 #[inline]
822 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
823 self.scroll_cursor_to_visible();
824 self.value
825 .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
826 }
827
828 #[inline]
831 pub fn select_all(&mut self) -> bool {
832 self.scroll_cursor_to_visible();
833 self.value.select_all()
834 }
835
836 #[inline]
838 pub fn selected_text(&self) -> &str {
839 match self.str_slice(self.selection()) {
840 Cow::Borrowed(v) => v,
841 Cow::Owned(_) => {
842 unreachable!()
843 }
844 }
845 }
846}
847
848impl TextInputState {
849 #[inline]
851 pub fn is_empty(&self) -> bool {
852 self.value.is_empty()
853 }
854
855 #[inline]
857 pub fn value<T: for<'a> From<&'a str>>(&self) -> T {
858 self.value.text().as_str().into()
859 }
860
861 #[inline]
863 pub fn text(&self) -> &str {
864 self.value.text().as_str()
865 }
866
867 #[inline]
869 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
870 self.value.str_slice_byte(range).expect("valid_range")
871 }
872
873 #[inline]
875 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
876 self.value.str_slice_byte(range)
877 }
878
879 #[inline]
881 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
882 self.value
883 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
884 .expect("valid_range")
885 }
886
887 #[inline]
889 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
890 self.value
891 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
892 }
893
894 #[inline]
896 pub fn len(&self) -> upos_type {
897 self.value.line_width(0).expect("valid_row")
898 }
899
900 #[inline]
902 pub fn len_bytes(&self) -> usize {
903 self.value.len_bytes()
904 }
905
906 #[inline]
908 pub fn line_width(&self) -> upos_type {
909 self.value.line_width(0).expect("valid_row")
910 }
911
912 #[inline]
914 pub fn text_graphemes(&self, pos: upos_type) -> <TextString as TextStore>::GraphemeIter<'_> {
915 self.value
916 .text_graphemes(TextPosition::new(pos, 0))
917 .expect("valid_pos")
918 }
919
920 #[inline]
922 pub fn try_text_graphemes(
923 &self,
924 pos: upos_type,
925 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
926 self.value.text_graphemes(TextPosition::new(pos, 0))
927 }
928
929 #[inline]
931 pub fn graphemes(
932 &self,
933 range: Range<upos_type>,
934 pos: upos_type,
935 ) -> <TextString as TextStore>::GraphemeIter<'_> {
936 self.value
937 .graphemes(
938 TextRange::new((range.start, 0), (range.end, 0)),
939 TextPosition::new(pos, 0),
940 )
941 .expect("valid_args")
942 }
943
944 #[inline]
946 pub fn try_graphemes(
947 &self,
948 range: Range<upos_type>,
949 pos: upos_type,
950 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
951 self.value.graphemes(
952 TextRange::new((range.start, 0), (range.end, 0)),
953 TextPosition::new(pos, 0),
954 )
955 }
956
957 #[inline]
960 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
961 self.value
962 .byte_at(TextPosition::new(pos, 0))
963 .expect("valid_pos")
964 }
965
966 #[inline]
969 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
970 self.value.byte_at(TextPosition::new(pos, 0))
971 }
972
973 #[inline]
975 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
976 self.value
977 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
978 .expect("valid_range")
979 }
980
981 #[inline]
983 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
984 self.value
985 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
986 }
987
988 #[inline]
991 pub fn byte_pos(&self, byte: usize) -> upos_type {
992 self.value.byte_pos(byte).map(|v| v.x).expect("valid_pos")
993 }
994
995 #[inline]
998 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
999 self.value.byte_pos(byte).map(|v| v.x)
1000 }
1001
1002 #[inline]
1004 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
1005 self.value
1006 .byte_range(bytes)
1007 .map(|v| v.start.x..v.end.x)
1008 .expect("valid_range")
1009 }
1010
1011 #[inline]
1013 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
1014 self.value.byte_range(bytes).map(|v| v.start.x..v.end.x)
1015 }
1016}
1017
1018impl TextInputState {
1019 #[inline]
1021 pub fn clear(&mut self) -> bool {
1022 if self.is_empty() {
1023 false
1024 } else {
1025 self.offset = 0;
1026 self.value.clear();
1027 true
1028 }
1029 }
1030
1031 #[inline]
1035 pub fn set_value<S: Into<String>>(&mut self, s: S) {
1036 self.offset = 0;
1037 self.value.set_text(TextString::new_string(s.into()));
1038 }
1039
1040 #[inline]
1044 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1045 self.offset = 0;
1046 self.value.set_text(TextString::new_string(s.into()));
1047 }
1048
1049 #[inline]
1051 pub fn insert_char(&mut self, c: char) -> bool {
1052 if self.has_selection() {
1053 self.value
1054 .remove_str_range(self.value.selection())
1055 .expect("valid_selection");
1056 }
1057 if c == '\n' {
1058 return false;
1059 } else {
1060 self.value
1061 .insert_char(self.value.cursor(), c)
1062 .expect("valid_cursor");
1063 }
1064 self.scroll_cursor_to_visible();
1065 true
1066 }
1067
1068 #[inline]
1070 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1071 let t = t.as_ref();
1072 if self.has_selection() {
1073 self.value
1074 .remove_str_range(self.value.selection())
1075 .expect("valid_selection");
1076 }
1077 self.value
1078 .insert_str(self.value.cursor(), t)
1079 .expect("valid_cursor");
1080 self.scroll_cursor_to_visible();
1081 true
1082 }
1083
1084 #[inline]
1086 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1087 self.try_delete_range(range).expect("valid_range")
1088 }
1089
1090 #[inline]
1092 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1093 if !range.is_empty() {
1094 self.value
1095 .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1096 self.scroll_cursor_to_visible();
1097 Ok(true)
1098 } else {
1099 Ok(false)
1100 }
1101 }
1102}
1103
1104impl TextInputState {
1105 #[inline]
1107 pub fn delete_next_char(&mut self) -> bool {
1108 if self.has_selection() {
1109 self.delete_range(self.selection())
1110 } else {
1111 let pos = self.value.cursor();
1112 let r = remove_next_char(&mut self.value, pos).expect("valid_cursor");
1113 self.scroll_cursor_to_visible();
1114 r
1115 }
1116 }
1117
1118 #[inline]
1120 pub fn delete_prev_char(&mut self) -> bool {
1121 if self.value.has_selection() {
1122 self.delete_range(self.selection())
1123 } else {
1124 let pos = self.value.cursor();
1125 let r = remove_prev_char(&mut self.value, pos).expect("valid_cursor");
1126 self.scroll_cursor_to_visible();
1127 r
1128 }
1129 }
1130
1131 pub fn next_word_start(&self, pos: upos_type) -> upos_type {
1133 self.try_next_word_start(pos).expect("valid_pos")
1134 }
1135
1136 pub fn try_next_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1138 next_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1139 }
1140
1141 pub fn next_word_end(&self, pos: upos_type) -> upos_type {
1144 self.try_next_word_end(pos).expect("valid_pos")
1145 }
1146
1147 pub fn try_next_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1150 next_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1151 }
1152
1153 pub fn prev_word_start(&self, pos: upos_type) -> upos_type {
1157 self.try_prev_word_start(pos).expect("valid_pos")
1158 }
1159
1160 pub fn try_prev_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1164 prev_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1165 }
1166
1167 pub fn prev_word_end(&self, pos: upos_type) -> upos_type {
1171 self.try_prev_word_end(pos).expect("valid_pos")
1172 }
1173
1174 pub fn try_prev_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1178 prev_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1179 }
1180
1181 pub fn is_word_boundary(&self, pos: upos_type) -> bool {
1183 self.try_is_word_boundary(pos).expect("valid_pos")
1184 }
1185
1186 pub fn try_is_word_boundary(&self, pos: upos_type) -> Result<bool, TextError> {
1188 is_word_boundary(&self.value, TextPosition::new(pos, 0))
1189 }
1190
1191 pub fn word_start(&self, pos: upos_type) -> upos_type {
1193 self.try_word_start(pos).expect("valid_pos")
1194 }
1195
1196 pub fn try_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1198 word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1199 }
1200
1201 pub fn word_end(&self, pos: upos_type) -> upos_type {
1203 self.try_word_end(pos).expect("valid_pos")
1204 }
1205
1206 pub fn try_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1208 word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1209 }
1210
1211 #[inline]
1213 pub fn delete_next_word(&mut self) -> bool {
1214 if self.has_selection() {
1215 self.delete_range(self.selection())
1216 } else {
1217 let cursor = self.cursor();
1218
1219 let start = self.next_word_start(cursor);
1220 if start != cursor {
1221 self.delete_range(cursor..start)
1222 } else {
1223 let end = self.next_word_end(cursor);
1224 self.delete_range(cursor..end)
1225 }
1226 }
1227 }
1228
1229 #[inline]
1231 pub fn delete_prev_word(&mut self) -> bool {
1232 if self.has_selection() {
1233 self.delete_range(self.selection())
1234 } else {
1235 let cursor = self.cursor();
1236
1237 let end = self.prev_word_end(cursor);
1238 if end != cursor {
1239 self.delete_range(end..cursor)
1240 } else {
1241 let start = self.prev_word_start(cursor);
1242 self.delete_range(start..cursor)
1243 }
1244 }
1245 }
1246
1247 #[inline]
1249 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1250 let c = min(self.cursor() + 1, self.len());
1251 self.set_cursor(c, extend_selection)
1252 }
1253
1254 #[inline]
1256 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1257 let c = self.cursor().saturating_sub(1);
1258 self.set_cursor(c, extend_selection)
1259 }
1260
1261 #[inline]
1263 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1264 self.set_cursor(0, extend_selection)
1265 }
1266
1267 #[inline]
1269 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1270 self.set_cursor(self.len(), extend_selection)
1271 }
1272
1273 #[inline]
1274 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1275 let cursor = self.cursor();
1276 let end = self.next_word_end(cursor);
1277 self.set_cursor(end, extend_selection)
1278 }
1279
1280 #[inline]
1281 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1282 let cursor = self.cursor();
1283 let start = self.prev_word_start(cursor);
1284 self.set_cursor(start, extend_selection)
1285 }
1286}
1287
1288impl HasScreenCursor for TextInputState {
1289 #[inline]
1291 fn screen_cursor(&self) -> Option<(u16, u16)> {
1292 if self.is_focused() {
1293 if self.has_selection() {
1294 None
1295 } else {
1296 let cx = self.cursor();
1297 let ox = self.offset();
1298
1299 if cx < ox {
1300 None
1301 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1302 None
1303 } else {
1304 self.col_to_screen(cx)
1305 .map(|sc| (self.inner.x + sc, self.inner.y))
1306 }
1307 }
1308 } else {
1309 None
1310 }
1311 }
1312}
1313
1314impl RelocatableState for TextInputState {
1315 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1316 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1318 self.area = relocate_area(self.area, shift, clip);
1319 self.inner = relocate_area(self.inner, shift, clip);
1320 }
1321}
1322
1323impl TextInputState {
1324 fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1325 let (text_wrap, left_margin, right_margin, word_margin) = (
1326 TextWrap2::Shift,
1327 self.offset() as upos_type,
1328 self.offset() as upos_type + self.rendered.width as upos_type,
1329 self.offset() as upos_type + self.rendered.width as upos_type,
1330 );
1331 self.value
1332 .glyphs2(
1333 self.rendered,
1334 0,
1335 0..1,
1336 0, text_wrap,
1338 false,
1339 left_margin,
1340 right_margin,
1341 word_margin,
1342 )
1343 .expect("valid-row")
1344 }
1345
1346 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1349 let ox = self.offset();
1350
1351 let scx = scx + self.dark_offset.0 as i16;
1352
1353 if scx < 0 {
1354 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1355 } else if scx as u16 >= self.rendered.width {
1356 min(ox + scx as upos_type, self.len())
1357 } else {
1358 let scx = scx as u16;
1359
1360 let line = self.glyphs2();
1361
1362 let mut col = ox;
1363 for g in line {
1364 if g.contains_screen_x(scx) {
1365 break;
1366 }
1367 col = g.pos().x + 1;
1368 }
1369 col
1370 }
1371 }
1372
1373 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1376 let ox = self.offset();
1377
1378 if pos < ox {
1379 return None;
1380 }
1381
1382 let line = self.glyphs2();
1383 let mut screen_x = 0;
1384 for g in line {
1385 if g.pos().x == pos {
1386 break;
1387 }
1388 screen_x = g.screen_pos().0 + g.screen_width();
1389 }
1390
1391 if screen_x >= self.dark_offset.0 {
1392 Some(screen_x - self.dark_offset.0)
1393 } else {
1394 None
1395 }
1396 }
1397
1398 #[inline]
1402 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1403 let scx = cursor;
1404
1405 let cx = self.screen_to_col(scx);
1406
1407 self.set_cursor(cx, extend_selection)
1408 }
1409
1410 pub fn set_screen_cursor_words(&mut self, screen_cursor: i16, extend_selection: bool) -> bool {
1417 let anchor = self.anchor();
1418
1419 let cx = self.screen_to_col(screen_cursor);
1420 let cursor = cx;
1421
1422 let cursor = if cursor < anchor {
1423 self.word_start(cursor)
1424 } else {
1425 self.word_end(cursor)
1426 };
1427
1428 if !self.is_word_boundary(anchor) {
1430 if cursor < anchor {
1431 self.set_cursor(self.word_end(anchor), false);
1432 } else {
1433 self.set_cursor(self.word_start(anchor), false);
1434 }
1435 }
1436
1437 self.set_cursor(cursor, extend_selection)
1438 }
1439
1440 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1442 self.set_offset(self.offset.saturating_sub(delta));
1443 true
1444 }
1445
1446 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1448 self.set_offset(self.offset + delta);
1449 true
1450 }
1451
1452 pub fn scroll_cursor_to_visible(&mut self) {
1454 self.scroll_to_cursor.set(true);
1455 }
1456}
1457
1458impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextInputState {
1459 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1460 fn tc(r: bool) -> TextOutcome {
1462 if r {
1463 TextOutcome::TextChanged
1464 } else {
1465 TextOutcome::Unchanged
1466 }
1467 }
1468 fn overwrite(state: &mut TextInputState) {
1469 if state.overwrite.get() {
1470 state.overwrite.set(false);
1471 state.clear();
1472 }
1473 }
1474 fn clear_overwrite(state: &mut TextInputState) {
1475 state.overwrite.set(false);
1476 }
1477
1478 let mut r = if self.is_focused() {
1502 match event {
1503 ct_event!(key press c)
1504 | ct_event!(key press SHIFT-c)
1505 | ct_event!(key press CONTROL_ALT-c) => {
1506 overwrite(self);
1507 tc(self.insert_char(*c))
1508 }
1509 ct_event!(keycode press Backspace) => {
1510 clear_overwrite(self);
1511 tc(self.delete_prev_char())
1512 }
1513 ct_event!(keycode press Delete) => {
1514 clear_overwrite(self);
1515 tc(self.delete_next_char())
1516 }
1517 ct_event!(keycode press CONTROL-Backspace)
1518 | ct_event!(keycode press ALT-Backspace) => {
1519 clear_overwrite(self);
1520 tc(self.delete_prev_word())
1521 }
1522 ct_event!(keycode press CONTROL-Delete) => {
1523 clear_overwrite(self);
1524 tc(self.delete_next_word())
1525 }
1526 ct_event!(key press CONTROL-'x') => {
1527 clear_overwrite(self);
1528 tc(self.cut_to_clip())
1529 }
1530 ct_event!(key press CONTROL-'v') => {
1531 overwrite(self);
1532 tc(self.paste_from_clip())
1533 }
1534 ct_event!(key press CONTROL-'d') => {
1535 clear_overwrite(self);
1536 tc(self.clear())
1537 }
1538 ct_event!(key press CONTROL-'z') => {
1539 clear_overwrite(self);
1540 tc(self.undo())
1541 }
1542 ct_event!(key press CONTROL_SHIFT-'Z') => {
1543 clear_overwrite(self);
1544 tc(self.redo())
1545 }
1546
1547 ct_event!(key release _)
1548 | ct_event!(key release SHIFT-_)
1549 | ct_event!(key release CONTROL_ALT-_)
1550 | ct_event!(keycode release Tab)
1551 | ct_event!(keycode release Backspace)
1552 | ct_event!(keycode release Delete)
1553 | ct_event!(keycode release CONTROL-Backspace)
1554 | ct_event!(keycode release ALT-Backspace)
1555 | ct_event!(keycode release CONTROL-Delete)
1556 | ct_event!(key release CONTROL-'x')
1557 | ct_event!(key release CONTROL-'v')
1558 | ct_event!(key release CONTROL-'d')
1559 | ct_event!(key release CONTROL-'y')
1560 | ct_event!(key release CONTROL-'z')
1561 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1562
1563 _ => TextOutcome::Continue,
1564 }
1565 } else {
1566 TextOutcome::Continue
1567 };
1568 if r == TextOutcome::Continue {
1569 r = self.handle(event, ReadOnly);
1570 }
1571 r
1572 }
1573}
1574
1575impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextInputState {
1576 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1577 fn clear_overwrite(state: &mut TextInputState) {
1578 state.overwrite.set(false);
1579 }
1580
1581 let mut r = if self.is_focused() {
1582 match event {
1583 ct_event!(keycode press Left) => {
1584 clear_overwrite(self);
1585 self.move_left(false).into()
1586 }
1587 ct_event!(keycode press Right) => {
1588 clear_overwrite(self);
1589 self.move_right(false).into()
1590 }
1591 ct_event!(keycode press CONTROL-Left) => {
1592 clear_overwrite(self);
1593 self.move_to_prev_word(false).into()
1594 }
1595 ct_event!(keycode press CONTROL-Right) => {
1596 clear_overwrite(self);
1597 self.move_to_next_word(false).into()
1598 }
1599 ct_event!(keycode press Home) => {
1600 clear_overwrite(self);
1601 self.move_to_line_start(false).into()
1602 }
1603 ct_event!(keycode press End) => {
1604 clear_overwrite(self);
1605 self.move_to_line_end(false).into()
1606 }
1607 ct_event!(keycode press SHIFT-Left) => {
1608 clear_overwrite(self);
1609 self.move_left(true).into()
1610 }
1611 ct_event!(keycode press SHIFT-Right) => {
1612 clear_overwrite(self);
1613 self.move_right(true).into()
1614 }
1615 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1616 clear_overwrite(self);
1617 self.move_to_prev_word(true).into()
1618 }
1619 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1620 clear_overwrite(self);
1621 self.move_to_next_word(true).into()
1622 }
1623 ct_event!(keycode press SHIFT-Home) => {
1624 clear_overwrite(self);
1625 self.move_to_line_start(true).into()
1626 }
1627 ct_event!(keycode press SHIFT-End) => {
1628 clear_overwrite(self);
1629 self.move_to_line_end(true).into()
1630 }
1631 ct_event!(keycode press ALT-Left) => {
1632 clear_overwrite(self);
1633 self.scroll_left(1).into()
1634 }
1635 ct_event!(keycode press ALT-Right) => {
1636 clear_overwrite(self);
1637 self.scroll_right(1).into()
1638 }
1639 ct_event!(key press CONTROL-'a') => {
1640 clear_overwrite(self);
1641 self.select_all().into()
1642 }
1643 ct_event!(key press CONTROL-'c') => {
1644 clear_overwrite(self);
1645 self.copy_to_clip().into()
1646 }
1647
1648 ct_event!(keycode release Left)
1649 | ct_event!(keycode release Right)
1650 | ct_event!(keycode release CONTROL-Left)
1651 | ct_event!(keycode release CONTROL-Right)
1652 | ct_event!(keycode release Home)
1653 | ct_event!(keycode release End)
1654 | ct_event!(keycode release SHIFT-Left)
1655 | ct_event!(keycode release SHIFT-Right)
1656 | ct_event!(keycode release CONTROL_SHIFT-Left)
1657 | ct_event!(keycode release CONTROL_SHIFT-Right)
1658 | ct_event!(keycode release SHIFT-Home)
1659 | ct_event!(keycode release SHIFT-End)
1660 | ct_event!(key release CONTROL-'a')
1661 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1662
1663 _ => TextOutcome::Continue,
1664 }
1665 } else {
1666 TextOutcome::Continue
1667 };
1668
1669 if r == TextOutcome::Continue {
1670 r = self.handle(event, MouseOnly);
1671 }
1672 r
1673 }
1674}
1675
1676impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextInputState {
1677 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1678 fn clear_overwrite(state: &mut TextInputState) {
1679 state.overwrite.set(false);
1680 }
1681
1682 match event {
1683 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1684 let c = (m.column as i16) - (self.inner.x as i16);
1685 clear_overwrite(self);
1686 self.set_screen_cursor(c, true).into()
1687 }
1688 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1689 let cx = m.column as i16 - self.inner.x as i16;
1690 clear_overwrite(self);
1691 self.set_screen_cursor_words(cx, true).into()
1692 }
1693 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1694 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1695 let start = self.word_start(tx);
1696 let end = self.word_end(tx);
1697 clear_overwrite(self);
1698 self.set_selection(start, end).into()
1699 }
1700 ct_event!(mouse down Left for column,row) => {
1701 if self.gained_focus() {
1702 TextOutcome::Unchanged
1705 } else if self.inner.contains((*column, *row).into()) {
1706 let c = (column - self.inner.x) as i16;
1707 clear_overwrite(self);
1708 self.set_screen_cursor(c, false).into()
1709 } else {
1710 TextOutcome::Continue
1711 }
1712 }
1713 ct_event!(mouse down CONTROL-Left for column,row) => {
1714 if self.inner.contains((*column, *row).into()) {
1715 let cx = (column - self.inner.x) as i16;
1716 clear_overwrite(self);
1717 self.set_screen_cursor(cx, true).into()
1718 } else {
1719 TextOutcome::Continue
1720 }
1721 }
1722 ct_event!(mouse down ALT-Left for column,row) => {
1723 if self.inner.contains((*column, *row).into()) {
1724 let cx = (column - self.inner.x) as i16;
1725 clear_overwrite(self);
1726 self.set_screen_cursor_words(cx, true).into()
1727 } else {
1728 TextOutcome::Continue
1729 }
1730 }
1731 _ => TextOutcome::Continue,
1732 }
1733 }
1734}
1735
1736pub fn handle_events(
1740 state: &mut TextInputState,
1741 focus: bool,
1742 event: &crossterm::event::Event,
1743) -> TextOutcome {
1744 state.focus.set(focus);
1745 state.handle(event, Regular)
1746}
1747
1748pub fn handle_readonly_events(
1752 state: &mut TextInputState,
1753 focus: bool,
1754 event: &crossterm::event::Event,
1755) -> TextOutcome {
1756 state.focus.set(focus);
1757 state.handle(event, ReadOnly)
1758}
1759
1760pub fn handle_mouse_events(
1762 state: &mut TextInputState,
1763 event: &crossterm::event::Event,
1764) -> TextOutcome {
1765 state.handle(event, MouseOnly)
1766}