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::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
251impl<'a> StatefulWidget for &TextInput<'a> {
252 type State = TextInputState;
253
254 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
255 render_ref(self, area, buf, state);
256 }
257}
258
259impl StatefulWidget for TextInput<'_> {
260 type State = TextInputState;
261
262 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
263 render_ref(&self, area, buf, state);
264 }
265}
266
267fn render_ref(widget: &TextInput<'_>, area: Rect, buf: &mut Buffer, state: &mut TextInputState) {
268 state.area = area;
269 state.inner = widget.block.inner_if_some(area);
270 state.rendered = state.inner.as_size();
271 state.passwd = widget.passwd;
272 state.on_focus_gained.set(widget.on_focus_gained);
273 state.on_focus_lost.set(widget.on_focus_lost);
274
275 if state.scroll_to_cursor.get() {
276 let c = state.cursor();
277 let o = state.offset();
278
279 if state.rendered.width > 0 {
280 let mut no = if c < o {
281 c
282 } else if c >= o + state.rendered.width as upos_type {
283 c.saturating_sub(state.rendered.width as upos_type)
284 } else {
285 o
286 };
287 if c == no + state.rendered.width as upos_type {
290 no = no.saturating_add(1);
291 }
292 state.set_offset(no);
293 } else {
294 }
296 }
297
298 let style = widget.style;
299 let focus_style = if let Some(focus_style) = widget.focus_style {
300 focus_style
301 } else {
302 style
303 };
304 let select_style = if let Some(select_style) = widget.select_style {
305 select_style
306 } else {
307 Style::default().black().on_yellow()
308 };
309 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
310 invalid_style
311 } else {
312 Style::default().red()
313 };
314
315 let (style, select_style) = if state.focus.get() {
316 if state.invalid {
317 (
318 style.patch(focus_style).patch(invalid_style),
319 style
320 .patch(focus_style)
321 .patch(select_style)
322 .patch(invalid_style),
323 )
324 } else {
325 (
326 style.patch(focus_style),
327 style.patch(focus_style).patch(select_style),
328 )
329 }
330 } else {
331 if state.invalid {
332 (
333 style.patch(invalid_style),
334 style.patch(select_style).patch(invalid_style),
335 )
336 } else {
337 (style, style.patch(select_style))
338 }
339 };
340
341 if let Some(block) = &widget.block {
343 block.render(area, buf);
344 }
345 buf.set_style(state.inner, style);
346
347 if state.inner.width == 0 || state.inner.height == 0 {
348 return;
350 }
351
352 let ox = state.offset() as u16;
353 let show_range = {
355 let start = min(ox as upos_type, state.len());
356 let end = min(start + state.inner.width as upos_type, state.len());
357 state.bytes_at_range(start..end)
358 };
359 let selection = state.selection();
360 let mut styles = Vec::new();
361
362 if widget.passwd {
363 for g in state.glyphs2() {
365 if g.screen_width() > 0 {
366 let mut style = style;
367 state
368 .value
369 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
370 for style_nr in &styles {
371 if let Some(s) = widget.text_style.get(style_nr) {
372 style = style.patch(*s);
373 }
374 }
375 if selection.contains(&g.pos().x) {
377 style = style.patch(select_style);
378 };
379
380 let screen_pos = g.screen_pos();
382
383 if let Some(cell) =
385 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
386 {
387 cell.set_symbol("*");
388 cell.set_style(style);
389 }
390 for d in 1..g.screen_width() {
392 if let Some(cell) = buf.cell_mut((
393 state.inner.x + screen_pos.0 + d,
394 state.inner.y + screen_pos.1,
395 )) {
396 cell.reset();
397 cell.set_style(style);
398 }
399 }
400 }
401 }
402 } else {
403 for g in state.glyphs2() {
404 if g.screen_width() > 0 {
405 let mut style = style;
406 state
407 .value
408 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
409 for style_nr in &styles {
410 if let Some(s) = widget.text_style.get(style_nr) {
411 style = style.patch(*s);
412 }
413 }
414 if selection.contains(&g.pos().x) {
416 style = style.patch(select_style);
417 };
418
419 let screen_pos = g.screen_pos();
421
422 if let Some(cell) =
424 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
425 {
426 cell.set_symbol(g.glyph());
427 cell.set_style(style);
428 }
429 for d in 1..g.screen_width() {
431 if let Some(cell) = buf.cell_mut((
432 state.inner.x + screen_pos.0 + d,
433 state.inner.y + screen_pos.1,
434 )) {
435 cell.reset();
436 cell.set_style(style);
437 }
438 }
439 }
440 }
441 }
442}
443
444impl Clone for TextInputState {
445 fn clone(&self) -> Self {
446 Self {
447 area: self.area,
448 inner: self.inner,
449 rendered: self.rendered,
450 offset: self.offset,
451 dark_offset: self.dark_offset,
452 scroll_to_cursor: Rc::new(Cell::new(self.scroll_to_cursor.get())),
453 value: self.value.clone(),
454 invalid: self.invalid,
455 passwd: self.passwd,
456 overwrite: Rc::new(Cell::new(self.overwrite.get())),
457 on_focus_gained: Rc::new(Cell::new(self.on_focus_gained.get())),
458 on_focus_lost: Rc::new(Cell::new(self.on_focus_lost.get())),
459 focus: self.focus_cb(FocusFlag::named(self.focus.name())),
460 mouse: Default::default(),
461 non_exhaustive: NonExhaustive,
462 }
463 }
464}
465
466impl Default for TextInputState {
467 fn default() -> Self {
468 let value = TextCore::new(Some(Box::new(UndoVec::new(99))), Some(global_clipboard()));
469
470 let mut z = Self {
471 area: Default::default(),
472 inner: Default::default(),
473 rendered: Default::default(),
474 offset: Default::default(),
475 dark_offset: Default::default(),
476 scroll_to_cursor: Default::default(),
477 value,
478 invalid: Default::default(),
479 passwd: Default::default(),
480 overwrite: Default::default(),
481 on_focus_gained: Default::default(),
482 on_focus_lost: Default::default(),
483 focus: Default::default(),
484 mouse: Default::default(),
485 non_exhaustive: NonExhaustive,
486 };
487 z.focus = z.focus_cb(FocusFlag::default());
488 z
489 }
490}
491
492impl TextInputState {
493 fn focus_cb(&self, flag: FocusFlag) -> FocusFlag {
494 let on_focus_lost = self.on_focus_lost.clone();
495 let cursor = self.value.shared_cursor();
496 let scroll_cursor_to_visible = self.scroll_to_cursor.clone();
497 flag.on_lost(move || match on_focus_lost.get() {
498 TextFocusLost::None => {}
499 TextFocusLost::Position0 => {
500 scroll_cursor_to_visible.set(true);
501 let mut new_cursor = cursor.get();
502 new_cursor.cursor.x = 0;
503 new_cursor.anchor.x = 0;
504 cursor.set(new_cursor);
505 }
506 });
507 let on_focus_gained = self.on_focus_gained.clone();
508 let overwrite = self.overwrite.clone();
509 let cursor = self.value.shared_cursor();
510 let scroll_cursor_to_visible = self.scroll_to_cursor.clone();
511 flag.on_gained(move || match on_focus_gained.get() {
512 TextFocusGained::None => {}
513 TextFocusGained::Overwrite => {
514 overwrite.set(true);
515 }
516 TextFocusGained::SelectAll => {
517 scroll_cursor_to_visible.set(true);
518 let mut new_cursor = cursor.get();
519 new_cursor.anchor = TextPosition::new(0, 0);
520 new_cursor.cursor = TextPosition::new(0, 1);
521 cursor.set(new_cursor);
522 }
523 });
524
525 flag
526 }
527}
528
529impl HasFocus for TextInputState {
530 fn build(&self, builder: &mut FocusBuilder) {
531 builder.leaf_widget(self);
532 }
533
534 fn focus(&self) -> FocusFlag {
535 self.focus.clone()
536 }
537
538 fn area(&self) -> Rect {
539 self.area
540 }
541}
542
543impl TextInputState {
544 pub fn new() -> Self {
545 Self::default()
546 }
547
548 pub fn named(name: &str) -> Self {
549 let mut z = Self::default();
550 z.focus = z.focus_cb(FocusFlag::named(name));
551 z
552 }
553
554 #[inline]
556 pub fn set_invalid(&mut self, invalid: bool) {
557 self.invalid = invalid;
558 }
559
560 #[inline]
562 pub fn invalid(&self) -> bool {
563 self.invalid
564 }
565
566 #[inline]
570 pub fn set_overwrite(&mut self, overwrite: bool) {
571 self.overwrite.set(overwrite);
572 }
573
574 #[inline]
576 pub fn overwrite(&self) -> bool {
577 self.overwrite.get()
578 }
579
580 #[inline]
582 pub fn set_show_ctrl(&mut self, show_ctrl: bool) {
583 self.value.set_glyph_ctrl(show_ctrl);
584 }
585
586 pub fn show_ctrl(&self) -> bool {
588 self.value.glyph_ctrl()
589 }
590}
591
592impl TextInputState {
593 #[inline]
596 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
597 match clip {
598 None => self.value.set_clipboard(None),
599 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
600 }
601 }
602
603 #[inline]
606 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
607 self.value.clipboard()
608 }
609
610 #[inline]
612 pub fn copy_to_clip(&mut self) -> bool {
613 let Some(clip) = self.value.clipboard() else {
614 return false;
615 };
616 if self.passwd {
617 return false;
618 }
619
620 _ = clip.set_string(self.selected_text().as_ref());
621 false
622 }
623
624 #[inline]
626 pub fn cut_to_clip(&mut self) -> bool {
627 let Some(clip) = self.value.clipboard() else {
628 return false;
629 };
630 if self.passwd {
631 return false;
632 }
633
634 match clip.set_string(self.selected_text().as_ref()) {
635 Ok(_) => self.delete_range(self.selection()),
636 Err(_) => false,
637 }
638 }
639
640 #[inline]
642 pub fn paste_from_clip(&mut self) -> bool {
643 let Some(clip) = self.value.clipboard() else {
644 return false;
645 };
646
647 if let Ok(text) = clip.get_string() {
648 self.insert_str(text)
649 } else {
650 false
651 }
652 }
653}
654
655impl TextInputState {
656 #[inline]
658 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
659 match undo {
660 None => self.value.set_undo_buffer(None),
661 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
662 }
663 }
664
665 #[inline]
667 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
668 self.value.undo_buffer()
669 }
670
671 #[inline]
673 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
674 self.value.undo_buffer_mut()
675 }
676
677 #[inline]
679 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
680 self.value.recent_replay_log()
681 }
682
683 #[inline]
685 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
686 self.value.replay_log(replay)
687 }
688
689 #[inline]
691 pub fn undo(&mut self) -> bool {
692 self.value.undo()
693 }
694
695 #[inline]
697 pub fn redo(&mut self) -> bool {
698 self.value.redo()
699 }
700}
701
702impl TextInputState {
703 #[inline]
714 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
715 self.value.set_styles(styles);
716 }
717
718 #[inline]
723 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
724 self.value.add_style(range, style);
725 }
726
727 #[inline]
730 pub fn add_range_style(
731 &mut self,
732 range: Range<upos_type>,
733 style: usize,
734 ) -> Result<(), TextError> {
735 let r = self
736 .value
737 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
738 self.value.add_style(r, style);
739 Ok(())
740 }
741
742 #[inline]
744 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
745 self.value.remove_style(range, style);
746 }
747
748 #[inline]
750 pub fn remove_range_style(
751 &mut self,
752 range: Range<upos_type>,
753 style: usize,
754 ) -> Result<(), TextError> {
755 let r = self
756 .value
757 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
758 self.value.remove_style(r, style);
759 Ok(())
760 }
761
762 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
764 self.value.styles_in(range, buf)
765 }
766
767 #[inline]
769 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
770 self.value.styles_at(byte_pos, buf)
771 }
772
773 #[inline]
776 pub fn styles_at_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
777 self.value.styles_at_match(byte_pos, style)
778 }
779
780 #[inline]
782 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
783 self.value.styles()
784 }
785}
786
787impl TextInputState {
788 #[inline]
790 pub fn offset(&self) -> upos_type {
791 self.offset
792 }
793
794 #[inline]
796 pub fn set_offset(&mut self, offset: upos_type) {
797 self.scroll_to_cursor.set(false);
798 self.offset = offset;
799 }
800
801 #[inline]
803 pub fn cursor(&self) -> upos_type {
804 self.value.cursor().x
805 }
806
807 #[inline]
809 pub fn anchor(&self) -> upos_type {
810 self.value.anchor().x
811 }
812
813 #[inline]
816 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
817 self.scroll_cursor_to_visible();
818 self.value
819 .set_cursor(TextPosition::new(cursor, 0), extend_selection)
820 }
821
822 #[inline]
824 pub fn has_selection(&self) -> bool {
825 self.value.has_selection()
826 }
827
828 #[inline]
830 pub fn selection(&self) -> Range<upos_type> {
831 let mut v = self.value.selection();
832 if v.start == TextPosition::new(0, 1) {
833 v.start = TextPosition::new(self.line_width(), 0);
834 }
835 if v.end == TextPosition::new(0, 1) {
836 v.end = TextPosition::new(self.line_width(), 0);
837 }
838 v.start.x..v.end.x
839 }
840
841 #[inline]
844 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
845 self.scroll_cursor_to_visible();
846 self.value
847 .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
848 }
849
850 #[inline]
853 pub fn select_all(&mut self) -> bool {
854 self.scroll_cursor_to_visible();
855 self.value.select_all()
856 }
857
858 #[inline]
860 pub fn selected_text(&self) -> &str {
861 match self.str_slice(self.selection()) {
862 Cow::Borrowed(v) => v,
863 Cow::Owned(_) => {
864 unreachable!()
865 }
866 }
867 }
868}
869
870impl TextInputState {
871 #[inline]
873 pub fn is_empty(&self) -> bool {
874 self.value.is_empty()
875 }
876
877 #[inline]
879 pub fn value<T: for<'a> From<&'a str>>(&self) -> T {
880 self.value.text().as_str().into()
881 }
882
883 #[inline]
885 pub fn text(&self) -> &str {
886 self.value.text().as_str()
887 }
888
889 #[inline]
891 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
892 self.value.str_slice_byte(range).expect("valid_range")
893 }
894
895 #[inline]
897 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
898 self.value.str_slice_byte(range)
899 }
900
901 #[inline]
903 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
904 self.value
905 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
906 .expect("valid_range")
907 }
908
909 #[inline]
911 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
912 self.value
913 .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
914 }
915
916 #[inline]
918 pub fn len(&self) -> upos_type {
919 self.value.line_width(0).expect("valid_row")
920 }
921
922 #[inline]
924 pub fn len_bytes(&self) -> usize {
925 self.value.len_bytes()
926 }
927
928 #[inline]
930 pub fn line_width(&self) -> upos_type {
931 self.value.line_width(0).expect("valid_row")
932 }
933
934 #[inline]
936 pub fn text_graphemes(&self, pos: upos_type) -> <TextString as TextStore>::GraphemeIter<'_> {
937 self.value
938 .text_graphemes(TextPosition::new(pos, 0))
939 .expect("valid_pos")
940 }
941
942 #[inline]
944 pub fn try_text_graphemes(
945 &self,
946 pos: upos_type,
947 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
948 self.value.text_graphemes(TextPosition::new(pos, 0))
949 }
950
951 #[inline]
953 pub fn graphemes(
954 &self,
955 range: Range<upos_type>,
956 pos: upos_type,
957 ) -> <TextString as TextStore>::GraphemeIter<'_> {
958 self.value
959 .graphemes(
960 TextRange::new((range.start, 0), (range.end, 0)),
961 TextPosition::new(pos, 0),
962 )
963 .expect("valid_args")
964 }
965
966 #[inline]
968 pub fn try_graphemes(
969 &self,
970 range: Range<upos_type>,
971 pos: upos_type,
972 ) -> Result<<TextString as TextStore>::GraphemeIter<'_>, TextError> {
973 self.value.graphemes(
974 TextRange::new((range.start, 0), (range.end, 0)),
975 TextPosition::new(pos, 0),
976 )
977 }
978
979 #[inline]
982 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
983 self.value
984 .byte_at(TextPosition::new(pos, 0))
985 .expect("valid_pos")
986 }
987
988 #[inline]
991 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
992 self.value.byte_at(TextPosition::new(pos, 0))
993 }
994
995 #[inline]
997 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
998 self.value
999 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
1000 .expect("valid_range")
1001 }
1002
1003 #[inline]
1005 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
1006 self.value
1007 .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
1008 }
1009
1010 #[inline]
1013 pub fn byte_pos(&self, byte: usize) -> upos_type {
1014 self.value.byte_pos(byte).map(|v| v.x).expect("valid_pos")
1015 }
1016
1017 #[inline]
1020 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
1021 self.value.byte_pos(byte).map(|v| v.x)
1022 }
1023
1024 #[inline]
1026 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
1027 self.value
1028 .byte_range(bytes)
1029 .map(|v| v.start.x..v.end.x)
1030 .expect("valid_range")
1031 }
1032
1033 #[inline]
1035 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
1036 self.value.byte_range(bytes).map(|v| v.start.x..v.end.x)
1037 }
1038}
1039
1040impl TextInputState {
1041 #[inline]
1043 pub fn clear(&mut self) -> bool {
1044 if self.is_empty() {
1045 false
1046 } else {
1047 self.offset = 0;
1048 self.value.clear();
1049 true
1050 }
1051 }
1052
1053 #[inline]
1057 pub fn set_value<S: Into<String>>(&mut self, s: S) {
1058 self.offset = 0;
1059 self.value.set_text(TextString::new_string(s.into()));
1060 }
1061
1062 #[inline]
1066 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1067 self.offset = 0;
1068 self.value.set_text(TextString::new_string(s.into()));
1069 }
1070
1071 #[inline]
1073 pub fn insert_char(&mut self, c: char) -> bool {
1074 if self.has_selection() {
1075 self.value
1076 .remove_str_range(self.value.selection())
1077 .expect("valid_selection");
1078 }
1079 if c == '\n' {
1080 return false;
1081 } else {
1082 self.value
1083 .insert_char(self.value.cursor(), c)
1084 .expect("valid_cursor");
1085 }
1086 self.scroll_cursor_to_visible();
1087 true
1088 }
1089
1090 #[inline]
1092 pub fn insert_str(&mut self, t: impl AsRef<str>) -> bool {
1093 let t = t.as_ref();
1094 if self.has_selection() {
1095 self.value
1096 .remove_str_range(self.value.selection())
1097 .expect("valid_selection");
1098 }
1099 self.value
1100 .insert_str(self.value.cursor(), t)
1101 .expect("valid_cursor");
1102 self.scroll_cursor_to_visible();
1103 true
1104 }
1105
1106 #[inline]
1108 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1109 self.try_delete_range(range).expect("valid_range")
1110 }
1111
1112 #[inline]
1114 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1115 if !range.is_empty() {
1116 self.value
1117 .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1118 self.scroll_cursor_to_visible();
1119 Ok(true)
1120 } else {
1121 Ok(false)
1122 }
1123 }
1124}
1125
1126impl TextInputState {
1127 #[inline]
1129 pub fn delete_next_char(&mut self) -> bool {
1130 if self.has_selection() {
1131 self.delete_range(self.selection())
1132 } else {
1133 let pos = self.value.cursor();
1134 let r = remove_next_char(&mut self.value, pos).expect("valid_cursor");
1135 self.scroll_cursor_to_visible();
1136 r
1137 }
1138 }
1139
1140 #[inline]
1142 pub fn delete_prev_char(&mut self) -> bool {
1143 if self.value.has_selection() {
1144 self.delete_range(self.selection())
1145 } else {
1146 let pos = self.value.cursor();
1147 let r = remove_prev_char(&mut self.value, pos).expect("valid_cursor");
1148 self.scroll_cursor_to_visible();
1149 r
1150 }
1151 }
1152
1153 pub fn next_word_start(&self, pos: upos_type) -> upos_type {
1155 self.try_next_word_start(pos).expect("valid_pos")
1156 }
1157
1158 pub fn try_next_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1160 next_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1161 }
1162
1163 pub fn next_word_end(&self, pos: upos_type) -> upos_type {
1166 self.try_next_word_end(pos).expect("valid_pos")
1167 }
1168
1169 pub fn try_next_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1172 next_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1173 }
1174
1175 pub fn prev_word_start(&self, pos: upos_type) -> upos_type {
1179 self.try_prev_word_start(pos).expect("valid_pos")
1180 }
1181
1182 pub fn try_prev_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1186 prev_word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1187 }
1188
1189 pub fn prev_word_end(&self, pos: upos_type) -> upos_type {
1193 self.try_prev_word_end(pos).expect("valid_pos")
1194 }
1195
1196 pub fn try_prev_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1200 prev_word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1201 }
1202
1203 pub fn is_word_boundary(&self, pos: upos_type) -> bool {
1205 self.try_is_word_boundary(pos).expect("valid_pos")
1206 }
1207
1208 pub fn try_is_word_boundary(&self, pos: upos_type) -> Result<bool, TextError> {
1210 is_word_boundary(&self.value, TextPosition::new(pos, 0))
1211 }
1212
1213 pub fn word_start(&self, pos: upos_type) -> upos_type {
1215 self.try_word_start(pos).expect("valid_pos")
1216 }
1217
1218 pub fn try_word_start(&self, pos: upos_type) -> Result<upos_type, TextError> {
1220 word_start(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1221 }
1222
1223 pub fn word_end(&self, pos: upos_type) -> upos_type {
1225 self.try_word_end(pos).expect("valid_pos")
1226 }
1227
1228 pub fn try_word_end(&self, pos: upos_type) -> Result<upos_type, TextError> {
1230 word_end(&self.value, TextPosition::new(pos, 0)).map(|v| v.x)
1231 }
1232
1233 #[inline]
1235 pub fn delete_next_word(&mut self) -> bool {
1236 if self.has_selection() {
1237 self.delete_range(self.selection())
1238 } else {
1239 let cursor = self.cursor();
1240
1241 let start = self.next_word_start(cursor);
1242 if start != cursor {
1243 self.delete_range(cursor..start)
1244 } else {
1245 let end = self.next_word_end(cursor);
1246 self.delete_range(cursor..end)
1247 }
1248 }
1249 }
1250
1251 #[inline]
1253 pub fn delete_prev_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 end = self.prev_word_end(cursor);
1260 if end != cursor {
1261 self.delete_range(end..cursor)
1262 } else {
1263 let start = self.prev_word_start(cursor);
1264 self.delete_range(start..cursor)
1265 }
1266 }
1267 }
1268
1269 #[inline]
1271 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1272 let c = min(self.cursor() + 1, self.len());
1273 self.set_cursor(c, extend_selection)
1274 }
1275
1276 #[inline]
1278 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1279 let c = self.cursor().saturating_sub(1);
1280 self.set_cursor(c, extend_selection)
1281 }
1282
1283 #[inline]
1285 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1286 self.set_cursor(0, extend_selection)
1287 }
1288
1289 #[inline]
1291 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1292 self.set_cursor(self.len(), extend_selection)
1293 }
1294
1295 #[inline]
1296 pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
1297 let cursor = self.cursor();
1298 let end = self.next_word_end(cursor);
1299 self.set_cursor(end, extend_selection)
1300 }
1301
1302 #[inline]
1303 pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
1304 let cursor = self.cursor();
1305 let start = self.prev_word_start(cursor);
1306 self.set_cursor(start, extend_selection)
1307 }
1308}
1309
1310impl HasScreenCursor for TextInputState {
1311 #[inline]
1313 fn screen_cursor(&self) -> Option<(u16, u16)> {
1314 if self.is_focused() {
1315 if self.has_selection() {
1316 None
1317 } else {
1318 let cx = self.cursor();
1319 let ox = self.offset();
1320
1321 if cx < ox {
1322 None
1323 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1324 None
1325 } else {
1326 self.col_to_screen(cx)
1327 .map(|sc| (self.inner.x + sc, self.inner.y))
1328 }
1329 }
1330 } else {
1331 None
1332 }
1333 }
1334}
1335
1336impl RelocatableState for TextInputState {
1337 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1338 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1340 self.area = relocate_area(self.area, shift, clip);
1341 self.inner = relocate_area(self.inner, shift, clip);
1342 }
1343}
1344
1345impl TextInputState {
1346 fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1347 let (text_wrap, left_margin, right_margin, word_margin) = (
1348 TextWrap2::Shift,
1349 self.offset() as upos_type,
1350 self.offset() as upos_type + self.rendered.width as upos_type,
1351 self.offset() as upos_type + self.rendered.width as upos_type,
1352 );
1353 self.value
1354 .glyphs2(
1355 self.rendered,
1356 0,
1357 0..1,
1358 0, text_wrap,
1360 false,
1361 left_margin,
1362 right_margin,
1363 word_margin,
1364 )
1365 .expect("valid-row")
1366 }
1367
1368 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1371 let ox = self.offset();
1372
1373 let scx = scx + self.dark_offset.0 as i16;
1374
1375 if scx < 0 {
1376 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1377 } else if scx as u16 >= self.rendered.width {
1378 min(ox + scx as upos_type, self.len())
1379 } else {
1380 let scx = scx as u16;
1381
1382 let line = self.glyphs2();
1383
1384 let mut col = ox;
1385 for g in line {
1386 if g.contains_screen_x(scx) {
1387 break;
1388 }
1389 col = g.pos().x + 1;
1390 }
1391 col
1392 }
1393 }
1394
1395 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1398 let ox = self.offset();
1399
1400 if pos < ox {
1401 return None;
1402 }
1403
1404 let line = self.glyphs2();
1405 let mut screen_x = 0;
1406 for g in line {
1407 if g.pos().x == pos {
1408 break;
1409 }
1410 screen_x = g.screen_pos().0 + g.screen_width();
1411 }
1412
1413 if screen_x >= self.dark_offset.0 {
1414 Some(screen_x - self.dark_offset.0)
1415 } else {
1416 None
1417 }
1418 }
1419
1420 #[inline]
1424 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1425 let scx = cursor;
1426
1427 let cx = self.screen_to_col(scx);
1428
1429 self.set_cursor(cx, extend_selection)
1430 }
1431
1432 pub fn set_screen_cursor_words(&mut self, screen_cursor: i16, extend_selection: bool) -> bool {
1439 let anchor = self.anchor();
1440
1441 let cx = self.screen_to_col(screen_cursor);
1442 let cursor = cx;
1443
1444 let cursor = if cursor < anchor {
1445 self.word_start(cursor)
1446 } else {
1447 self.word_end(cursor)
1448 };
1449
1450 if !self.is_word_boundary(anchor) {
1452 if cursor < anchor {
1453 self.set_cursor(self.word_end(anchor), false);
1454 } else {
1455 self.set_cursor(self.word_start(anchor), false);
1456 }
1457 }
1458
1459 self.set_cursor(cursor, extend_selection)
1460 }
1461
1462 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1464 self.set_offset(self.offset.saturating_sub(delta));
1465 true
1466 }
1467
1468 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1470 self.set_offset(self.offset + delta);
1471 true
1472 }
1473
1474 pub fn scroll_cursor_to_visible(&mut self) {
1476 self.scroll_to_cursor.set(true);
1477 }
1478}
1479
1480impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for TextInputState {
1481 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1482 fn tc(r: bool) -> TextOutcome {
1484 if r {
1485 TextOutcome::TextChanged
1486 } else {
1487 TextOutcome::Unchanged
1488 }
1489 }
1490 fn overwrite(state: &mut TextInputState) {
1491 if state.overwrite.get() {
1492 state.overwrite.set(false);
1493 state.clear();
1494 }
1495 }
1496 fn clear_overwrite(state: &mut TextInputState) {
1497 state.overwrite.set(false);
1498 }
1499
1500 let mut r = if self.is_focused() {
1524 match event {
1525 ct_event!(key press c)
1526 | ct_event!(key press SHIFT-c)
1527 | ct_event!(key press CONTROL_ALT-c) => {
1528 overwrite(self);
1529 tc(self.insert_char(*c))
1530 }
1531 ct_event!(keycode press Backspace) => {
1532 clear_overwrite(self);
1533 tc(self.delete_prev_char())
1534 }
1535 ct_event!(keycode press Delete) => {
1536 clear_overwrite(self);
1537 tc(self.delete_next_char())
1538 }
1539 ct_event!(keycode press CONTROL-Backspace)
1540 | ct_event!(keycode press ALT-Backspace) => {
1541 clear_overwrite(self);
1542 tc(self.delete_prev_word())
1543 }
1544 ct_event!(keycode press CONTROL-Delete) => {
1545 clear_overwrite(self);
1546 tc(self.delete_next_word())
1547 }
1548 ct_event!(key press CONTROL-'x') => {
1549 clear_overwrite(self);
1550 tc(self.cut_to_clip())
1551 }
1552 ct_event!(key press CONTROL-'v') => {
1553 overwrite(self);
1554 tc(self.paste_from_clip())
1555 }
1556 ct_event!(key press CONTROL-'d') => {
1557 clear_overwrite(self);
1558 tc(self.clear())
1559 }
1560 ct_event!(key press CONTROL-'z') => {
1561 clear_overwrite(self);
1562 tc(self.undo())
1563 }
1564 ct_event!(key press CONTROL_SHIFT-'Z') => {
1565 clear_overwrite(self);
1566 tc(self.redo())
1567 }
1568
1569 ct_event!(key release _)
1570 | ct_event!(key release SHIFT-_)
1571 | ct_event!(key release CONTROL_ALT-_)
1572 | ct_event!(keycode release Tab)
1573 | ct_event!(keycode release Backspace)
1574 | ct_event!(keycode release Delete)
1575 | ct_event!(keycode release CONTROL-Backspace)
1576 | ct_event!(keycode release ALT-Backspace)
1577 | ct_event!(keycode release CONTROL-Delete)
1578 | ct_event!(key release CONTROL-'x')
1579 | ct_event!(key release CONTROL-'v')
1580 | ct_event!(key release CONTROL-'d')
1581 | ct_event!(key release CONTROL-'y')
1582 | ct_event!(key release CONTROL-'z')
1583 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1584
1585 _ => TextOutcome::Continue,
1586 }
1587 } else {
1588 TextOutcome::Continue
1589 };
1590 if r == TextOutcome::Continue {
1591 r = self.handle(event, ReadOnly);
1592 }
1593 r
1594 }
1595}
1596
1597impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for TextInputState {
1598 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1599 fn clear_overwrite(state: &mut TextInputState) {
1600 state.overwrite.set(false);
1601 }
1602
1603 let mut r = if self.is_focused() {
1604 match event {
1605 ct_event!(keycode press Left) => {
1606 clear_overwrite(self);
1607 self.move_left(false).into()
1608 }
1609 ct_event!(keycode press Right) => {
1610 clear_overwrite(self);
1611 self.move_right(false).into()
1612 }
1613 ct_event!(keycode press CONTROL-Left) => {
1614 clear_overwrite(self);
1615 self.move_to_prev_word(false).into()
1616 }
1617 ct_event!(keycode press CONTROL-Right) => {
1618 clear_overwrite(self);
1619 self.move_to_next_word(false).into()
1620 }
1621 ct_event!(keycode press Home) => {
1622 clear_overwrite(self);
1623 self.move_to_line_start(false).into()
1624 }
1625 ct_event!(keycode press End) => {
1626 clear_overwrite(self);
1627 self.move_to_line_end(false).into()
1628 }
1629 ct_event!(keycode press SHIFT-Left) => {
1630 clear_overwrite(self);
1631 self.move_left(true).into()
1632 }
1633 ct_event!(keycode press SHIFT-Right) => {
1634 clear_overwrite(self);
1635 self.move_right(true).into()
1636 }
1637 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1638 clear_overwrite(self);
1639 self.move_to_prev_word(true).into()
1640 }
1641 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1642 clear_overwrite(self);
1643 self.move_to_next_word(true).into()
1644 }
1645 ct_event!(keycode press SHIFT-Home) => {
1646 clear_overwrite(self);
1647 self.move_to_line_start(true).into()
1648 }
1649 ct_event!(keycode press SHIFT-End) => {
1650 clear_overwrite(self);
1651 self.move_to_line_end(true).into()
1652 }
1653 ct_event!(keycode press ALT-Left) => {
1654 clear_overwrite(self);
1655 self.scroll_left(1).into()
1656 }
1657 ct_event!(keycode press ALT-Right) => {
1658 clear_overwrite(self);
1659 self.scroll_right(1).into()
1660 }
1661 ct_event!(key press CONTROL-'a') => {
1662 clear_overwrite(self);
1663 self.select_all().into()
1664 }
1665 ct_event!(key press CONTROL-'c') => {
1666 clear_overwrite(self);
1667 self.copy_to_clip().into()
1668 }
1669
1670 ct_event!(keycode release Left)
1671 | ct_event!(keycode release Right)
1672 | ct_event!(keycode release CONTROL-Left)
1673 | ct_event!(keycode release CONTROL-Right)
1674 | ct_event!(keycode release Home)
1675 | ct_event!(keycode release End)
1676 | ct_event!(keycode release SHIFT-Left)
1677 | ct_event!(keycode release SHIFT-Right)
1678 | ct_event!(keycode release CONTROL_SHIFT-Left)
1679 | ct_event!(keycode release CONTROL_SHIFT-Right)
1680 | ct_event!(keycode release SHIFT-Home)
1681 | ct_event!(keycode release SHIFT-End)
1682 | ct_event!(key release CONTROL-'a')
1683 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1684
1685 _ => TextOutcome::Continue,
1686 }
1687 } else {
1688 TextOutcome::Continue
1689 };
1690
1691 if r == TextOutcome::Continue {
1692 r = self.handle(event, MouseOnly);
1693 }
1694 r
1695 }
1696}
1697
1698impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for TextInputState {
1699 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1700 fn clear_overwrite(state: &mut TextInputState) {
1701 state.overwrite.set(false);
1702 }
1703
1704 match event {
1705 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1706 let c = (m.column as i16) - (self.inner.x as i16);
1707 clear_overwrite(self);
1708 self.set_screen_cursor(c, true).into()
1709 }
1710 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1711 let cx = m.column as i16 - self.inner.x as i16;
1712 clear_overwrite(self);
1713 self.set_screen_cursor_words(cx, true).into()
1714 }
1715 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1716 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1717 let start = self.word_start(tx);
1718 let end = self.word_end(tx);
1719 clear_overwrite(self);
1720 self.set_selection(start, end).into()
1721 }
1722 ct_event!(mouse down Left for column,row) => {
1723 if self.gained_focus() {
1724 TextOutcome::Unchanged
1727 } else if self.inner.contains((*column, *row).into()) {
1728 let c = (column - self.inner.x) as i16;
1729 clear_overwrite(self);
1730 self.set_screen_cursor(c, false).into()
1731 } else {
1732 TextOutcome::Continue
1733 }
1734 }
1735 ct_event!(mouse down CONTROL-Left for column,row) => {
1736 if self.inner.contains((*column, *row).into()) {
1737 let cx = (column - self.inner.x) as i16;
1738 clear_overwrite(self);
1739 self.set_screen_cursor(cx, true).into()
1740 } else {
1741 TextOutcome::Continue
1742 }
1743 }
1744 ct_event!(mouse down ALT-Left for column,row) => {
1745 if self.inner.contains((*column, *row).into()) {
1746 let cx = (column - self.inner.x) as i16;
1747 clear_overwrite(self);
1748 self.set_screen_cursor_words(cx, true).into()
1749 } else {
1750 TextOutcome::Continue
1751 }
1752 }
1753 _ => TextOutcome::Continue,
1754 }
1755 }
1756}
1757
1758pub fn handle_events(
1762 state: &mut TextInputState,
1763 focus: bool,
1764 event: &crossterm::event::Event,
1765) -> TextOutcome {
1766 state.focus.set(focus);
1767 state.handle(event, Regular)
1768}
1769
1770pub fn handle_readonly_events(
1774 state: &mut TextInputState,
1775 focus: bool,
1776 event: &crossterm::event::Event,
1777) -> TextOutcome {
1778 state.focus.set(focus);
1779 state.handle(event, ReadOnly)
1780}
1781
1782pub fn handle_mouse_events(
1784 state: &mut TextInputState,
1785 event: &crossterm::event::Event,
1786) -> TextOutcome {
1787 state.handle(event, MouseOnly)
1788}