1use crate::_private::NonExhaustive;
73#[allow(deprecated)]
74use crate::Glyph;
75use crate::clipboard::Clipboard;
76use crate::event::{ReadOnly, TextOutcome};
77use crate::glyph2::Glyph2;
78use crate::text_input::TextInputState;
79use crate::text_mask_core::MaskedCore;
80use crate::undo_buffer::{UndoBuffer, UndoEntry};
81use crate::{
82 Cursor, Grapheme, HasScreenCursor, TextError, TextFocusGained, TextFocusLost, TextStyle,
83 ipos_type, upos_type,
84};
85use crossterm::event::KeyModifiers;
86use format_num_pattern::NumberSymbols;
87use rat_event::util::MouseFlags;
88use rat_event::{HandleEvent, MouseOnly, Regular, ct_event};
89use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
90use rat_reloc::{RelocatableState, relocate_area, relocate_dark_offset};
91use ratatui::buffer::Buffer;
92use ratatui::layout::{Rect, Size};
93use ratatui::prelude::BlockExt;
94use ratatui::style::{Style, Stylize};
95use ratatui::widgets::{Block, StatefulWidget, Widget};
96use std::borrow::Cow;
97use std::cmp::min;
98use std::fmt;
99use std::ops::Range;
100
101#[derive(Debug, Default, Clone)]
107pub struct MaskedInput<'a> {
108 compact: bool,
109 block: Option<Block<'a>>,
110 style: Style,
111 focus_style: Option<Style>,
112 select_style: Option<Style>,
113 invalid_style: Option<Style>,
114 text_style: Vec<Style>,
115 on_focus_gained: TextFocusGained,
116 on_focus_lost: TextFocusLost,
117}
118
119#[derive(Debug)]
121pub struct MaskedInputState {
122 pub area: Rect,
125 pub inner: Rect,
128 pub rendered: Size,
131 pub compact: bool,
134
135 pub offset: upos_type,
138 pub dark_offset: (u16, u16),
141 pub scroll_to_cursor: bool,
143
144 pub value: MaskedCore,
146 pub invalid: bool,
149 pub overwrite: bool,
152 pub on_focus_gained: TextFocusGained,
155 pub on_focus_lost: TextFocusLost,
158
159 pub focus: FocusFlag,
162
163 pub mouse: MouseFlags,
166
167 pub non_exhaustive: NonExhaustive,
169}
170
171impl<'a> MaskedInput<'a> {
172 pub fn new() -> Self {
174 Self::default()
175 }
176
177 #[inline]
180 pub fn compact(mut self, show_compact: bool) -> Self {
181 self.compact = show_compact;
182 self
183 }
184
185 #[inline]
187 pub fn styles_opt(self, styles: Option<TextStyle>) -> Self {
188 if let Some(styles) = styles {
189 self.styles(styles)
190 } else {
191 self
192 }
193 }
194
195 #[inline]
197 pub fn styles(mut self, styles: TextStyle) -> Self {
198 self.style = styles.style;
199 if styles.focus.is_some() {
200 self.focus_style = styles.focus;
201 }
202 if styles.select.is_some() {
203 self.select_style = styles.select;
204 }
205 if styles.invalid.is_some() {
206 self.invalid_style = styles.invalid;
207 }
208 if let Some(of) = styles.on_focus_gained {
209 self.on_focus_gained = of;
210 }
211 if let Some(of) = styles.on_focus_lost {
212 self.on_focus_lost = of;
213 }
214 if let Some(border_style) = styles.border_style {
215 self.block = self.block.map(|v| v.border_style(border_style));
216 }
217 self.block = self.block.map(|v| v.style(self.style));
218 if styles.block.is_some() {
219 self.block = styles.block;
220 }
221 self.block = self.block.map(|v| v.style(self.style));
222 self
223 }
224
225 #[inline]
227 pub fn style(mut self, style: impl Into<Style>) -> Self {
228 self.style = style.into();
229 self.block = self.block.map(|v| v.style(self.style));
230 self
231 }
232
233 #[inline]
235 pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
236 self.focus_style = Some(style.into());
237 self
238 }
239
240 #[inline]
242 pub fn select_style(mut self, style: impl Into<Style>) -> Self {
243 self.select_style = Some(style.into());
244 self
245 }
246
247 #[inline]
250 pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
251 self.invalid_style = Some(style.into());
252 self
253 }
254
255 pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
260 self.text_style = styles.into_iter().collect();
261 self
262 }
263
264 #[inline]
266 pub fn block(mut self, block: Block<'a>) -> Self {
267 self.block = Some(block);
268 self.block = self.block.map(|v| v.style(self.style));
269 self
270 }
271
272 #[inline]
274 pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
275 self.on_focus_gained = of;
276 self
277 }
278
279 #[inline]
281 pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
282 self.on_focus_lost = of;
283 self
284 }
285}
286
287impl<'a> StatefulWidget for &MaskedInput<'a> {
288 type State = MaskedInputState;
289
290 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
291 render_ref(self, area, buf, state);
292 }
293}
294
295impl StatefulWidget for MaskedInput<'_> {
296 type State = MaskedInputState;
297
298 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
299 render_ref(&self, area, buf, state);
300 }
301}
302
303fn render_ref(
304 widget: &MaskedInput<'_>,
305 area: Rect,
306
307 buf: &mut Buffer,
308 state: &mut MaskedInputState,
309) {
310 state.area = area;
311 state.inner = widget.block.inner_if_some(area);
312 state.rendered = state.inner.as_size();
313 state.compact = widget.compact;
314 state.on_focus_gained = widget.on_focus_gained;
315 state.on_focus_lost = widget.on_focus_lost;
316
317 if state.scroll_to_cursor {
318 let c = state.cursor();
319 let o = state.offset();
320 let mut no = if c < o {
321 c
322 } else if c >= o + state.rendered.width as upos_type {
323 c.saturating_sub(state.rendered.width as upos_type)
324 } else {
325 o
326 };
327 if c == no + state.rendered.width as upos_type {
330 no = no.saturating_add(1);
331 }
332 state.set_offset(no);
333 }
334
335 let style = widget.style;
336 let focus_style = if let Some(focus_style) = widget.focus_style {
337 focus_style
338 } else {
339 style
340 };
341 let select_style = if let Some(select_style) = widget.select_style {
342 select_style
343 } else {
344 Style::default().black().on_yellow()
345 };
346 let invalid_style = if let Some(invalid_style) = widget.invalid_style {
347 invalid_style
348 } else {
349 Style::default().red()
350 };
351
352 let (style, select_style) = if state.focus.get() {
353 if state.invalid {
354 (
355 style.patch(focus_style).patch(invalid_style),
356 style
357 .patch(focus_style)
358 .patch(select_style)
359 .patch(invalid_style),
360 )
361 } else {
362 (
363 style.patch(focus_style),
364 style.patch(focus_style).patch(select_style),
365 )
366 }
367 } else {
368 if state.invalid {
369 (
370 style.patch(invalid_style),
371 style.patch(select_style).patch(invalid_style),
372 )
373 } else {
374 (style, style.patch(select_style))
375 }
376 };
377
378 if let Some(block) = &widget.block {
380 block.render(area, buf);
381 } else {
382 buf.set_style(area, style);
383 }
384
385 if state.inner.width == 0 || state.inner.height == 0 {
386 return;
388 }
389
390 let ox = state.offset() as u16;
391 let show_range = {
393 let start = ox as upos_type;
394 let end = min(start + state.inner.width as upos_type, state.len());
395 state.bytes_at_range(start..end)
396 };
397 let selection = state.selection();
398 let mut styles = Vec::new();
399
400 for g in state.glyphs2() {
401 if g.screen_width() > 0 {
402 let mut style = style;
403 styles.clear();
404 state
405 .value
406 .styles_at_page(g.text_bytes().start, show_range.clone(), &mut styles);
407 for style_nr in &styles {
408 if let Some(s) = widget.text_style.get(*style_nr) {
409 style = style.patch(*s);
410 }
411 }
412 if selection.contains(&g.pos().x) {
414 style = style.patch(select_style);
415 };
416
417 let screen_pos = g.screen_pos();
419
420 if let Some(cell) =
422 buf.cell_mut((state.inner.x + screen_pos.0, state.inner.y + screen_pos.1))
423 {
424 cell.set_symbol(g.glyph());
425 cell.set_style(style);
426 }
427 for d in 1..g.screen_width() {
429 if let Some(cell) = buf.cell_mut((
430 state.inner.x + screen_pos.0 + d,
431 state.inner.y + screen_pos.1,
432 )) {
433 cell.reset();
434 cell.set_style(style);
435 }
436 }
437 }
438 }
439}
440
441impl Clone for MaskedInputState {
442 fn clone(&self) -> Self {
443 Self {
444 area: self.area,
445 inner: self.inner,
446 rendered: self.rendered,
447 compact: self.compact,
448 offset: self.offset,
449 dark_offset: self.dark_offset,
450 scroll_to_cursor: self.scroll_to_cursor,
451 value: self.value.clone(),
452 invalid: self.invalid,
453 overwrite: Default::default(),
454 on_focus_gained: Default::default(),
455 on_focus_lost: Default::default(),
456 focus: FocusFlag::named(self.focus.name()),
457 mouse: Default::default(),
458 non_exhaustive: NonExhaustive,
459 }
460 }
461}
462
463impl Default for MaskedInputState {
464 fn default() -> Self {
465 Self {
466 area: Default::default(),
467 inner: Default::default(),
468 rendered: Default::default(),
469 compact: Default::default(),
470 offset: Default::default(),
471 dark_offset: Default::default(),
472 scroll_to_cursor: Default::default(),
473 value: Default::default(),
474 invalid: Default::default(),
475 overwrite: Default::default(),
476 on_focus_gained: Default::default(),
477 on_focus_lost: Default::default(),
478 focus: Default::default(),
479 mouse: Default::default(),
480 non_exhaustive: NonExhaustive,
481 }
482 }
483}
484
485impl HasFocus for MaskedInputState {
486 fn build(&self, builder: &mut FocusBuilder) {
487 builder.leaf_widget(self);
488 }
489
490 fn focus(&self) -> FocusFlag {
491 self.focus.clone()
492 }
493
494 fn area(&self) -> Rect {
495 self.area
496 }
497
498 fn navigable(&self) -> Navigation {
499 let sel = self.selection();
500
501 let has_next = self
502 .value
503 .next_section_range(sel.end)
504 .map(|v| !v.is_empty())
505 .is_some();
506 let has_prev = self
507 .value
508 .prev_section_range(sel.start.saturating_sub(1))
509 .map(|v| !v.is_empty())
510 .is_some();
511
512 if has_next {
513 if has_prev {
514 Navigation::Reach
515 } else {
516 Navigation::ReachLeaveFront
517 }
518 } else {
519 if has_prev {
520 Navigation::ReachLeaveBack
521 } else {
522 Navigation::Regular
523 }
524 }
525 }
526}
527
528impl MaskedInputState {
529 pub fn new() -> Self {
530 Self::default()
531 }
532
533 pub fn named(name: &str) -> Self {
534 Self {
535 focus: FocusFlag::named(name),
536 ..MaskedInputState::default()
537 }
538 }
539
540 #[inline]
542 pub fn with_symbols(mut self, sym: NumberSymbols) -> Self {
543 self.set_num_symbols(sym);
544 self
545 }
546
547 pub fn with_mask<S: AsRef<str>>(mut self, mask: S) -> Result<Self, fmt::Error> {
549 self.value.set_mask(mask.as_ref())?;
550 Ok(self)
551 }
552
553 #[inline]
558 pub fn set_num_symbols(&mut self, sym: NumberSymbols) {
559 self.value.set_num_symbols(sym);
560 }
561
562 #[inline]
593 pub fn set_mask<S: AsRef<str>>(&mut self, s: S) -> Result<(), fmt::Error> {
595 self.value.set_mask(s)
596 }
597
598 #[inline]
600 pub fn mask(&self) -> String {
601 self.value.mask()
602 }
603
604 #[inline]
606 pub fn set_invalid(&mut self, invalid: bool) {
607 self.invalid = invalid;
608 }
609
610 #[inline]
612 pub fn get_invalid(&self) -> bool {
613 self.invalid
614 }
615
616 #[inline]
620 pub fn set_overwrite(&mut self, overwrite: bool) {
621 self.overwrite = overwrite;
622 }
623
624 #[inline]
626 pub fn overwrite(&self) -> bool {
627 self.overwrite
628 }
629}
630
631impl MaskedInputState {
632 #[inline]
635 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
636 match clip {
637 None => self.value.set_clipboard(None),
638 Some(v) => self.value.set_clipboard(Some(Box::new(v))),
639 }
640 }
641
642 #[inline]
645 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
646 self.value.clipboard()
647 }
648
649 #[inline]
651 pub fn copy_to_clip(&mut self) -> bool {
652 let Some(clip) = self.value.clipboard() else {
653 return false;
654 };
655
656 _ = clip.set_string(self.selected_text().as_ref());
657
658 true
659 }
660
661 #[inline]
663 pub fn cut_to_clip(&mut self) -> bool {
664 let Some(clip) = self.value.clipboard() else {
665 return false;
666 };
667
668 match clip.set_string(self.selected_text().as_ref()) {
669 Ok(_) => self.delete_range(self.selection()),
670 Err(_) => true,
671 }
672 }
673
674 #[inline]
676 pub fn paste_from_clip(&mut self) -> bool {
677 let Some(clip) = self.value.clipboard() else {
678 return false;
679 };
680
681 if let Ok(text) = clip.get_string() {
682 for c in text.chars() {
683 self.insert_char(c);
684 }
685 true
686 } else {
687 false
688 }
689 }
690}
691
692impl MaskedInputState {
693 #[inline]
695 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
696 match undo {
697 None => self.value.set_undo_buffer(None),
698 Some(v) => self.value.set_undo_buffer(Some(Box::new(v))),
699 }
700 }
701
702 #[inline]
704 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
705 self.value.undo_buffer()
706 }
707
708 #[inline]
710 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
711 self.value.undo_buffer_mut()
712 }
713
714 #[inline]
716 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
717 self.value.recent_replay_log()
718 }
719
720 #[inline]
722 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
723 self.value.replay_log(replay)
724 }
725
726 #[inline]
728 pub fn undo(&mut self) -> bool {
729 self.value.undo()
730 }
731
732 #[inline]
734 pub fn redo(&mut self) -> bool {
735 self.value.redo()
736 }
737}
738
739impl MaskedInputState {
740 #[inline]
742 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
743 self.value.set_styles(styles);
744 }
745
746 #[inline]
749 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
750 self.value.add_style(range, style);
751 }
752
753 #[inline]
756 pub fn add_range_style(
757 &mut self,
758 range: Range<upos_type>,
759 style: usize,
760 ) -> Result<(), TextError> {
761 let r = self.value.bytes_at_range(range)?;
762 self.value.add_style(r, style);
763 Ok(())
764 }
765
766 #[inline]
768 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
769 self.value.remove_style(range, style);
770 }
771
772 #[inline]
774 pub fn remove_range_style(
775 &mut self,
776 range: Range<upos_type>,
777 style: usize,
778 ) -> Result<(), TextError> {
779 let r = self.value.bytes_at_range(range)?;
780 self.value.remove_style(r, style);
781 Ok(())
782 }
783
784 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
786 self.value.styles_in(range, buf)
787 }
788
789 #[inline]
791 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
792 self.value.styles_at(byte_pos, buf)
793 }
794
795 #[inline]
798 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
799 self.value.style_match(byte_pos, style)
800 }
801
802 #[inline]
804 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
805 self.value.styles()
806 }
807}
808
809impl MaskedInputState {
810 #[inline]
812 pub fn offset(&self) -> upos_type {
813 self.offset
814 }
815
816 #[inline]
818 pub fn set_offset(&mut self, offset: upos_type) {
819 self.scroll_to_cursor = false;
820 self.offset = offset;
821 }
822
823 #[inline]
825 pub fn cursor(&self) -> upos_type {
826 self.value.cursor()
827 }
828
829 #[inline]
832 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
833 self.scroll_cursor_to_visible();
834 self.value.set_cursor(cursor, extend_selection)
835 }
836
837 #[inline]
841 pub fn set_default_cursor(&mut self) {
842 self.scroll_cursor_to_visible();
843 self.value.set_default_cursor();
844 }
845
846 #[inline]
848 pub fn anchor(&self) -> upos_type {
849 self.value.anchor()
850 }
851
852 #[inline]
854 pub fn has_selection(&self) -> bool {
855 self.value.has_selection()
856 }
857
858 #[inline]
860 pub fn selection(&self) -> Range<upos_type> {
861 self.value.selection()
862 }
863
864 #[inline]
867 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
868 self.scroll_cursor_to_visible();
869 self.value.set_selection(anchor, cursor)
870 }
871
872 #[inline]
875 pub fn select_all(&mut self) -> bool {
876 self.scroll_cursor_to_visible();
877 if let Some(section) = self.value.section_range(self.cursor()) {
878 if self.selection() == section {
879 self.value.select_all()
880 } else {
881 self.value.set_selection(section.start, section.end)
882 }
883 } else {
884 self.value.select_all()
885 }
886 }
887
888 #[inline]
890 pub fn selected_text(&self) -> &str {
891 self.value.selected_text()
892 }
893}
894
895impl MaskedInputState {
896 #[inline]
898 pub fn is_empty(&self) -> bool {
899 self.value.is_empty()
900 }
901
902 #[inline]
904 pub fn text(&self) -> &str {
905 self.value.text()
906 }
907
908 #[inline]
910 pub fn str_slice_byte(&self, range: Range<usize>) -> Cow<'_, str> {
911 self.value.str_slice_byte(range).expect("valid_range")
912 }
913
914 #[inline]
916 pub fn try_str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
917 self.value.str_slice_byte(range)
918 }
919
920 #[inline]
922 pub fn str_slice(&self, range: Range<upos_type>) -> Cow<'_, str> {
923 self.value.str_slice(range).expect("valid_range")
924 }
925
926 #[inline]
928 pub fn try_str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
929 self.value.str_slice(range)
930 }
931
932 #[inline]
934 pub fn len(&self) -> upos_type {
935 self.value.line_width()
936 }
937
938 #[inline]
940 pub fn line_width(&self) -> upos_type {
941 self.value.line_width()
942 }
943
944 #[inline]
947 #[allow(deprecated)]
948 #[deprecated(since = "1.1.0", note = "discontinued api")]
949 pub fn glyphs(&self, screen_offset: u16, screen_width: u16) -> impl Iterator<Item = Glyph<'_>> {
950 self.value
951 .glyphs(0..1, screen_offset, screen_width)
952 .expect("valid_row")
953 }
954
955 #[inline]
958 #[allow(deprecated)]
959 #[deprecated(since = "1.1.0", note = "discontinued api")]
960 pub fn condensed_glyphs(
961 &self,
962 screen_offset: u16,
963 screen_width: u16,
964 ) -> impl Iterator<Item = Glyph<'_>> {
965 self.value
966 .condensed_glyphs(0..1, screen_offset, screen_width)
967 .expect("valid_row")
968 }
969
970 #[inline]
972 pub fn text_graphemes(&self, pos: upos_type) -> impl Cursor<Item = Grapheme<'_>> {
973 self.value.text_graphemes(pos).expect("valid_pos")
974 }
975
976 #[inline]
978 pub fn try_text_graphemes(
979 &self,
980 pos: upos_type,
981 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
982 self.value.text_graphemes(pos)
983 }
984
985 #[inline]
987 pub fn graphemes(
988 &self,
989 range: Range<upos_type>,
990 pos: upos_type,
991 ) -> impl Cursor<Item = Grapheme<'_>> {
992 self.value.graphemes(range, pos).expect("valid_args")
993 }
994
995 #[inline]
997 pub fn try_graphemes(
998 &self,
999 range: Range<upos_type>,
1000 pos: upos_type,
1001 ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
1002 self.value.graphemes(range, pos)
1003 }
1004
1005 #[inline]
1008 pub fn byte_at(&self, pos: upos_type) -> Range<usize> {
1009 self.value.byte_at(pos).expect("valid_pos")
1010 }
1011
1012 #[inline]
1015 pub fn try_byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
1016 self.value.byte_at(pos)
1017 }
1018
1019 #[inline]
1021 pub fn bytes_at_range(&self, range: Range<upos_type>) -> Range<usize> {
1022 self.value.bytes_at_range(range).expect("valid_range")
1023 }
1024
1025 #[inline]
1027 pub fn try_bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
1028 self.value.bytes_at_range(range)
1029 }
1030
1031 #[inline]
1034 pub fn byte_pos(&self, byte: usize) -> upos_type {
1035 self.value.byte_pos(byte).expect("valid_pos")
1036 }
1037
1038 #[inline]
1041 pub fn try_byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
1042 self.value.byte_pos(byte)
1043 }
1044
1045 #[inline]
1047 pub fn byte_range(&self, bytes: Range<usize>) -> Range<upos_type> {
1048 self.value.byte_range(bytes).expect("valid_range")
1049 }
1050
1051 #[inline]
1053 pub fn try_byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
1054 self.value.byte_range(bytes)
1055 }
1056}
1057
1058impl MaskedInputState {
1059 #[inline]
1061 pub fn clear(&mut self) -> bool {
1062 if self.is_empty() {
1063 false
1064 } else {
1065 self.offset = 0;
1066 self.value.clear();
1067 true
1068 }
1069 }
1070
1071 #[inline]
1077 pub fn set_text<S: Into<String>>(&mut self, s: S) {
1078 self.offset = 0;
1079 self.value.set_text(s);
1080 self.value.set_default_cursor();
1081 }
1082
1083 #[inline]
1085 pub fn insert_char(&mut self, c: char) -> bool {
1086 self.value.begin_undo_seq();
1087 if self.value.has_selection() {
1088 let sel = self.value.selection();
1089 self.value
1090 .remove_range(sel.clone())
1091 .expect("valid_selection");
1092 self.value.set_cursor(sel.start, false);
1093 }
1094 let c0 = self.value.advance_cursor(c);
1095 let c1 = self.value.insert_char(c);
1096 self.value.end_undo_seq();
1097
1098 self.scroll_cursor_to_visible();
1099 c0 || c1
1100 }
1101
1102 #[inline]
1105 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
1106 self.try_delete_range(range).expect("valid_range")
1107 }
1108
1109 #[inline]
1112 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1113 self.value.begin_undo_seq();
1114 let r = self.value.remove_range(range.clone())?;
1115 if let Some(pos) = self.value.section_cursor(range.start) {
1116 self.value.set_cursor(pos, false);
1117 }
1118 self.value.end_undo_seq();
1119
1120 self.scroll_cursor_to_visible();
1121 Ok(r)
1122 }
1123}
1124
1125impl MaskedInputState {
1126 #[inline]
1128 pub fn delete_next_char(&mut self) -> bool {
1129 if self.has_selection() {
1130 self.delete_range(self.selection())
1131 } else if self.cursor() == self.len() {
1132 false
1133 } else {
1134 self.value.remove_next();
1135 self.scroll_cursor_to_visible();
1136 true
1137 }
1138 }
1139
1140 #[inline]
1142 pub fn delete_prev_char(&mut self) -> bool {
1143 if self.has_selection() {
1144 self.delete_range(self.selection())
1145 } else if self.cursor() == 0 {
1146 false
1147 } else {
1148 self.value.remove_prev();
1149 self.scroll_cursor_to_visible();
1150 true
1151 }
1152 }
1153
1154 #[inline]
1156 pub fn delete_prev_section(&mut self) -> bool {
1157 if self.has_selection() {
1158 self.delete_range(self.selection())
1159 } else {
1160 if let Some(range) = self.value.prev_section_range(self.cursor()) {
1161 self.delete_range(range)
1162 } else {
1163 false
1164 }
1165 }
1166 }
1167
1168 #[inline]
1170 pub fn delete_next_section(&mut self) -> bool {
1171 if self.has_selection() {
1172 self.delete_range(self.selection())
1173 } else {
1174 if let Some(range) = self.value.next_section_range(self.cursor()) {
1175 self.delete_range(range)
1176 } else {
1177 false
1178 }
1179 }
1180 }
1181
1182 #[inline]
1184 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1185 let c = min(self.cursor() + 1, self.len());
1186 self.set_cursor(c, extend_selection)
1187 }
1188
1189 #[inline]
1191 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1192 let c = self.cursor().saturating_sub(1);
1193 self.set_cursor(c, extend_selection)
1194 }
1195
1196 #[inline]
1198 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1199 if let Some(c) = self.value.section_cursor(self.cursor()) {
1200 if c != self.cursor() {
1201 self.set_cursor(c, extend_selection)
1202 } else {
1203 self.set_cursor(0, extend_selection)
1204 }
1205 } else {
1206 self.set_cursor(0, extend_selection)
1207 }
1208 }
1209
1210 #[inline]
1212 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1213 self.set_cursor(self.len(), extend_selection)
1214 }
1215
1216 #[inline]
1218 pub fn move_to_prev_section(&mut self, extend_selection: bool) -> bool {
1219 if let Some(curr) = self.value.section_range(self.cursor()) {
1220 if self.value.cursor() != curr.start {
1221 return self.value.set_cursor(curr.start, extend_selection);
1222 }
1223 }
1224 if let Some(range) = self.value.prev_section_range(self.cursor()) {
1225 self.value.set_cursor(range.start, extend_selection)
1226 } else {
1227 false
1228 }
1229 }
1230
1231 #[inline]
1233 pub fn move_to_next_section(&mut self, extend_selection: bool) -> bool {
1234 if let Some(curr) = self.value.section_range(self.cursor()) {
1235 if self.value.cursor() != curr.end {
1236 return self.value.set_cursor(curr.end, extend_selection);
1237 }
1238 }
1239 if let Some(range) = self.value.next_section_range(self.cursor()) {
1240 self.value.set_cursor(range.end, extend_selection)
1241 } else {
1242 false
1243 }
1244 }
1245
1246 #[inline]
1248 pub fn select_current_section(&mut self) -> bool {
1249 let selection = self.selection();
1250
1251 if let Some(next) = self.value.section_range(selection.start.saturating_sub(1)) {
1252 if !next.is_empty() {
1253 self.set_selection(next.start, next.end)
1254 } else {
1255 false
1256 }
1257 } else {
1258 false
1259 }
1260 }
1261
1262 #[inline]
1264 pub fn select_next_section(&mut self) -> bool {
1265 let selection = self.selection();
1266
1267 if let Some(next) = self.value.next_section_range(selection.start) {
1268 if !next.is_empty() {
1269 self.set_selection(next.start, next.end)
1270 } else {
1271 false
1272 }
1273 } else {
1274 false
1275 }
1276 }
1277
1278 #[inline]
1280 pub fn select_prev_section(&mut self) -> bool {
1281 let selection = self.selection();
1282
1283 if let Some(next) = self
1284 .value
1285 .prev_section_range(selection.start.saturating_sub(1))
1286 {
1287 if !next.is_empty() {
1288 self.set_selection(next.start, next.end)
1289 } else {
1290 false
1291 }
1292 } else {
1293 false
1294 }
1295 }
1296}
1297
1298impl HasScreenCursor for MaskedInputState {
1299 #[inline]
1301 fn screen_cursor(&self) -> Option<(u16, u16)> {
1302 if self.is_focused() {
1303 if self.has_selection() {
1304 None
1305 } else {
1306 let cx = self.cursor();
1307 let ox = self.offset();
1308
1309 if cx < ox {
1310 None
1311 } else if cx > ox + (self.inner.width + self.dark_offset.0) as upos_type {
1312 None
1313 } else {
1314 self.col_to_screen(cx)
1315 .map(|sc| (self.inner.x + sc, self.inner.y))
1316 }
1317 }
1318 } else {
1319 None
1320 }
1321 }
1322}
1323
1324impl RelocatableState for MaskedInputState {
1325 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1326 self.dark_offset = relocate_dark_offset(self.inner, shift, clip);
1328 self.area = relocate_area(self.area, shift, clip);
1329 self.inner = relocate_area(self.inner, shift, clip);
1330 }
1331}
1332
1333impl MaskedInputState {
1334 fn glyphs2(&self) -> impl Iterator<Item = Glyph2<'_>> {
1335 self.value
1336 .glyphs2(
1337 self.offset(),
1338 self.offset() + self.rendered.width as upos_type,
1339 self.compact && !self.is_focused(),
1340 )
1341 .expect("valid-rows")
1342 }
1343
1344 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1347 let ox = self.offset();
1348
1349 let scx = scx + self.dark_offset.0 as i16;
1350
1351 if scx < 0 {
1352 ox.saturating_sub((scx as ipos_type).unsigned_abs())
1353 } else if scx as u16 >= (self.inner.width + self.dark_offset.0) {
1354 min(ox + scx as upos_type, self.len())
1355 } else {
1356 let scx = scx as u16;
1357
1358 let line = self.glyphs2();
1359
1360 let mut col = ox;
1361 for g in line {
1362 if g.contains_screen_x(scx) {
1363 break;
1364 }
1365 col = g.pos().x + 1;
1366 }
1367 col
1368 }
1369 }
1370
1371 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1374 let ox = self.offset();
1375
1376 if pos < ox {
1377 return None;
1378 }
1379
1380 let line = self.glyphs2();
1381 let mut screen_x = 0;
1382 for g in line {
1383 if g.pos().x == pos {
1384 break;
1385 }
1386 screen_x = g.screen_pos().0 + g.screen_width();
1387 }
1388
1389 if screen_x >= self.dark_offset.0 {
1390 Some(screen_x - self.dark_offset.0)
1391 } else {
1392 None
1393 }
1394 }
1395
1396 #[inline]
1400 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1401 let scx = cursor;
1402
1403 let cx = self.screen_to_col(scx);
1404
1405 self.set_cursor(cx, extend_selection)
1406 }
1407
1408 pub fn set_screen_cursor_sections(
1415 &mut self,
1416 screen_cursor: i16,
1417 extend_selection: bool,
1418 ) -> bool {
1419 let anchor = self.anchor();
1420 let cursor = self.screen_to_col(screen_cursor);
1421
1422 let Some(range) = self.value.section_range(cursor) else {
1423 return false;
1424 };
1425
1426 let cursor = if cursor < anchor {
1427 range.start
1428 } else {
1429 range.end
1430 };
1431
1432 if !self.value.is_section_boundary(anchor) {
1434 if let Some(range) = self.value.section_range(anchor) {
1435 if cursor < anchor {
1436 self.set_cursor(range.end, false);
1437 } else {
1438 self.set_cursor(range.start, false);
1439 }
1440 };
1441 }
1442
1443 self.set_cursor(cursor, extend_selection)
1444 }
1445
1446 pub fn scroll_left(&mut self, delta: upos_type) -> bool {
1448 self.set_offset(self.offset.saturating_sub(delta));
1449 true
1450 }
1451
1452 pub fn scroll_right(&mut self, delta: upos_type) -> bool {
1454 self.set_offset(self.offset + delta);
1455 true
1456 }
1457
1458 pub fn scroll_cursor_to_visible(&mut self) {
1460 self.scroll_to_cursor = true;
1461 }
1462}
1463
1464impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for MaskedInputState {
1465 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1466 fn tc(r: bool) -> TextOutcome {
1468 if r {
1469 TextOutcome::TextChanged
1470 } else {
1471 TextOutcome::Unchanged
1472 }
1473 }
1474 fn overwrite(state: &mut MaskedInputState) {
1475 if state.overwrite {
1476 state.overwrite = false;
1477 state.clear();
1478 }
1479 }
1480 fn clear_overwrite(state: &mut MaskedInputState) {
1481 state.overwrite = false;
1482 }
1483
1484 if self.lost_focus() {
1486 match self.on_focus_lost {
1487 TextFocusLost::None => {}
1488 TextFocusLost::Position0 => {
1489 self.set_default_cursor();
1490 self.scroll_cursor_to_visible();
1491 }
1493 }
1494 }
1495 if self.gained_focus() {
1496 match self.on_focus_gained {
1497 TextFocusGained::None => {}
1498 TextFocusGained::Overwrite => {
1499 self.overwrite = true;
1500 }
1501 TextFocusGained::SelectAll => {
1502 self.select_all();
1503 }
1505 }
1506 }
1507
1508 let mut r = if self.is_focused() {
1509 match event {
1510 ct_event!(key press c)
1511 | ct_event!(key press SHIFT-c)
1512 | ct_event!(key press CONTROL_ALT-c) => {
1513 overwrite(self);
1514 tc(self.insert_char(*c))
1515 }
1516 ct_event!(keycode press Backspace) => {
1517 clear_overwrite(self);
1518 tc(self.delete_prev_char())
1519 }
1520 ct_event!(keycode press Delete) => {
1521 clear_overwrite(self);
1522 tc(self.delete_next_char())
1523 }
1524 ct_event!(keycode press CONTROL-Backspace)
1525 | ct_event!(keycode press ALT-Backspace) => {
1526 clear_overwrite(self);
1527 tc(self.delete_prev_section())
1528 }
1529 ct_event!(keycode press CONTROL-Delete) => {
1530 clear_overwrite(self);
1531 tc(self.delete_next_section())
1532 }
1533 ct_event!(key press CONTROL-'x') => {
1534 clear_overwrite(self);
1535 tc(self.cut_to_clip())
1536 }
1537 ct_event!(key press CONTROL-'v') => {
1538 clear_overwrite(self);
1539 tc(self.paste_from_clip())
1540 }
1541 ct_event!(key press CONTROL-'d') => {
1542 clear_overwrite(self);
1543 tc(self.clear())
1544 }
1545 ct_event!(key press CONTROL-'z') => {
1546 clear_overwrite(self);
1547 tc(self.undo())
1548 }
1549 ct_event!(key press CONTROL_SHIFT-'Z') => {
1550 clear_overwrite(self);
1551 tc(self.redo())
1552 }
1553
1554 ct_event!(key release _)
1555 | ct_event!(key release SHIFT-_)
1556 | ct_event!(key release CONTROL_ALT-_)
1557 | ct_event!(keycode release Backspace)
1558 | ct_event!(keycode release Delete)
1559 | ct_event!(keycode release CONTROL-Backspace)
1560 | ct_event!(keycode release ALT-Backspace)
1561 | ct_event!(keycode release CONTROL-Delete)
1562 | ct_event!(key release CONTROL-'x')
1563 | ct_event!(key release CONTROL-'v')
1564 | ct_event!(key release CONTROL-'d')
1565 | ct_event!(key release CONTROL-'z')
1566 | ct_event!(key release CONTROL_SHIFT-'Z') => TextOutcome::Unchanged,
1567
1568 _ => TextOutcome::Continue,
1569 }
1570 } else {
1571 TextOutcome::Continue
1572 };
1573
1574 if r == TextOutcome::Continue {
1575 r = self.handle(event, ReadOnly);
1576 }
1577 r
1578 }
1579}
1580
1581impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for MaskedInputState {
1582 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1583 fn clear_overwrite(state: &mut MaskedInputState) {
1584 state.overwrite = false;
1585 }
1586
1587 let mut r = if self.is_focused() {
1588 match event {
1589 ct_event!(keycode press Left) => {
1590 clear_overwrite(self);
1591 self.move_left(false).into()
1592 }
1593 ct_event!(keycode press Right) => {
1594 clear_overwrite(self);
1595 self.move_right(false).into()
1596 }
1597 ct_event!(keycode press CONTROL-Left) => {
1598 clear_overwrite(self);
1599 self.move_to_prev_section(false).into()
1600 }
1601 ct_event!(keycode press CONTROL-Right) => {
1602 clear_overwrite(self);
1603 self.move_to_next_section(false).into()
1604 }
1605 ct_event!(keycode press Home) => {
1606 clear_overwrite(self);
1607 self.move_to_line_start(false).into()
1608 }
1609 ct_event!(keycode press End) => {
1610 clear_overwrite(self);
1611 self.move_to_line_end(false).into()
1612 }
1613 ct_event!(keycode press SHIFT-Left) => {
1614 clear_overwrite(self);
1615 self.move_left(true).into()
1616 }
1617 ct_event!(keycode press SHIFT-Right) => {
1618 clear_overwrite(self);
1619 self.move_right(true).into()
1620 }
1621 ct_event!(keycode press CONTROL_SHIFT-Left) => {
1622 clear_overwrite(self);
1623 self.move_to_prev_section(true).into()
1624 }
1625 ct_event!(keycode press CONTROL_SHIFT-Right) => {
1626 clear_overwrite(self);
1627 self.move_to_next_section(true).into()
1628 }
1629 ct_event!(keycode press SHIFT-Home) => {
1630 clear_overwrite(self);
1631 self.move_to_line_start(true).into()
1632 }
1633 ct_event!(keycode press SHIFT-End) => {
1634 clear_overwrite(self);
1635 self.move_to_line_end(true).into()
1636 }
1637 ct_event!(keycode press Tab) => {
1638 if !self.focus.gained() {
1640 clear_overwrite(self);
1641 self.select_next_section().into()
1642 } else {
1643 TextOutcome::Unchanged
1644 }
1645 }
1646 ct_event!(keycode press SHIFT-BackTab) => {
1647 if !self.focus.gained() {
1649 clear_overwrite(self);
1650 self.select_prev_section().into()
1651 } else {
1652 TextOutcome::Unchanged
1653 }
1654 }
1655 ct_event!(key press CONTROL-'a') => {
1656 clear_overwrite(self);
1657 self.select_all().into()
1658 }
1659 ct_event!(key press CONTROL-'c') => {
1660 clear_overwrite(self);
1661 self.copy_to_clip().into()
1662 }
1663
1664 ct_event!(keycode release Left)
1665 | ct_event!(keycode release Right)
1666 | ct_event!(keycode release CONTROL-Left)
1667 | ct_event!(keycode release CONTROL-Right)
1668 | ct_event!(keycode release Home)
1669 | ct_event!(keycode release End)
1670 | ct_event!(keycode release SHIFT-Left)
1671 | ct_event!(keycode release SHIFT-Right)
1672 | ct_event!(keycode release CONTROL_SHIFT-Left)
1673 | ct_event!(keycode release CONTROL_SHIFT-Right)
1674 | ct_event!(keycode release SHIFT-Home)
1675 | ct_event!(keycode release SHIFT-End)
1676 | ct_event!(key release CONTROL-'a')
1677 | ct_event!(key release CONTROL-'c') => TextOutcome::Unchanged,
1678
1679 _ => TextOutcome::Continue,
1680 }
1681 } else {
1682 TextOutcome::Continue
1683 };
1684
1685 if r == TextOutcome::Continue {
1686 r = self.handle(event, MouseOnly);
1687 }
1688 r
1689 }
1690}
1691
1692impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for MaskedInputState {
1693 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1694 fn clear_overwrite(state: &mut MaskedInputState) {
1695 state.overwrite = false;
1696 }
1697
1698 match event {
1699 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1700 let c = (m.column as i16) - (self.inner.x as i16);
1701 clear_overwrite(self);
1702 self.set_screen_cursor(c, true).into()
1703 }
1704 ct_event!(mouse any for m) if self.mouse.drag2(self.inner, m, KeyModifiers::ALT) => {
1705 let cx = m.column as i16 - self.inner.x as i16;
1706 clear_overwrite(self);
1707 self.set_screen_cursor_sections(cx, true).into()
1708 }
1709 ct_event!(mouse any for m) if self.mouse.doubleclick(self.inner, m) => {
1710 let tx = self.screen_to_col(m.column as i16 - self.inner.x as i16);
1711 clear_overwrite(self);
1712 if let Some(range) = self.value.section_range(tx) {
1713 self.set_selection(range.start, range.end).into()
1714 } else {
1715 TextOutcome::Unchanged
1716 }
1717 }
1718 ct_event!(mouse down Left for column,row) => {
1719 if self.gained_focus() {
1720 TextOutcome::Unchanged
1723 } else if self.inner.contains((*column, *row).into()) {
1724 let c = (column - self.inner.x) as i16;
1725 clear_overwrite(self);
1726 self.set_screen_cursor(c, false).into()
1727 } else {
1728 TextOutcome::Continue
1729 }
1730 }
1731 ct_event!(mouse down CONTROL-Left for column,row) => {
1732 if self.inner.contains((*column, *row).into()) {
1733 let cx = (column - self.inner.x) as i16;
1734 clear_overwrite(self);
1735 self.set_screen_cursor(cx, true).into()
1736 } else {
1737 TextOutcome::Continue
1738 }
1739 }
1740 ct_event!(mouse down ALT-Left for column,row) => {
1741 if self.inner.contains((*column, *row).into()) {
1742 let cx = (column - self.inner.x) as i16;
1743 clear_overwrite(self);
1744 self.set_screen_cursor_sections(cx, true).into()
1745 } else {
1746 TextOutcome::Continue
1747 }
1748 }
1749 _ => TextOutcome::Continue,
1750 }
1751 }
1752}
1753
1754pub fn handle_events(
1758 state: &mut MaskedInputState,
1759 focus: bool,
1760 event: &crossterm::event::Event,
1761) -> TextOutcome {
1762 state.focus.set(focus);
1763 state.handle(event, Regular)
1764}
1765
1766pub fn handle_readonly_events(
1770 state: &mut TextInputState,
1771 focus: bool,
1772 event: &crossterm::event::Event,
1773) -> TextOutcome {
1774 state.focus.set(focus);
1775 state.handle(event, ReadOnly)
1776}
1777
1778pub fn handle_mouse_events(
1780 state: &mut MaskedInputState,
1781 event: &crossterm::event::Event,
1782) -> TextOutcome {
1783 state.handle(event, MouseOnly)
1784}