1use crate::_private::NonExhaustive;
35use crate::choice::core::ChoiceCore;
36use crate::event::ChoiceOutcome;
37use crate::text::HasScreenCursor;
38use crate::util::{block_padding, block_size, revert_style};
39use rat_event::util::{MouseFlags, item_at, mouse_trap};
40use rat_event::{ConsumedEvent, HandleEvent, MouseOnly, Popup, ct_event};
41use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
42use rat_popup::event::PopupOutcome;
43use rat_popup::{Placement, PopupCore, PopupCoreState, PopupStyle, fallback_popup_style};
44use rat_reloc::RelocatableState;
45use rat_scrolled::event::ScrollOutcome;
46use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState, ScrollStyle};
47use ratatui::buffer::Buffer;
48use ratatui::layout::{Alignment, Rect};
49use ratatui::prelude::BlockExt;
50use ratatui::style::Style;
51use ratatui::text::{Line, Span};
52use ratatui::widgets::{Block, StatefulWidget, Widget};
53use std::cell::{Cell, RefCell};
54use std::cmp::{max, min};
55use std::marker::PhantomData;
56use std::rc::Rc;
57
58#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
60pub enum ChoiceFocus {
61 #[default]
63 SeparateOpen,
64 OpenOnFocusGained,
66}
67
68#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
70pub enum ChoiceSelect {
71 #[default]
73 MouseScroll,
74 MouseMove,
76 MouseClick,
78}
79
80#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
82pub enum ChoiceClose {
83 #[default]
85 SingleClick,
86 DoubleClick,
88}
89
90#[derive(Debug, Clone)]
99pub struct Choice<'a, T = usize>
100where
101 T: PartialEq + Clone + Default,
102{
103 values: Rc<RefCell<Vec<T>>>,
104 default_value: Option<T>,
105 items: Rc<RefCell<Vec<Line<'a>>>>,
106
107 style: Style,
108 button_style: Option<Style>,
109 select_style: Option<Style>,
110 select_marker: Option<char>,
111 focus_style: Option<Style>,
112 block: Option<Block<'a>>,
113 skip_item_render: bool,
114
115 popup_alignment: Alignment,
116 popup_placement: Placement,
117 popup_len: Option<u16>,
118 popup: PopupCore,
119 popup_style: Style,
120 popup_scroll: Option<Scroll<'a>>,
121 popup_block: Option<Block<'a>>,
122
123 behave_focus: ChoiceFocus,
124 behave_select: ChoiceSelect,
125 behave_close: ChoiceClose,
126}
127
128#[derive(Debug)]
130pub struct ChoiceWidget<'a, T>
131where
132 T: PartialEq,
133{
134 values: Rc<RefCell<Vec<T>>>,
135 default_value: Option<T>,
136 items: Rc<RefCell<Vec<Line<'a>>>>,
137
138 style: Style,
139 button_style: Option<Style>,
140 focus_style: Option<Style>,
141 block: Option<Block<'a>>,
142 skip_item_render: bool,
143 len: Option<u16>,
144
145 behave_focus: ChoiceFocus,
146 behave_select: ChoiceSelect,
147 behave_close: ChoiceClose,
148}
149
150#[derive(Debug)]
153pub struct ChoicePopup<'a, T>
154where
155 T: PartialEq,
156{
157 items: Rc<RefCell<Vec<Line<'a>>>>,
158
159 style: Style,
160 select_style: Option<Style>,
161 select_marker: Option<char>,
162
163 popup_alignment: Alignment,
164 popup_placement: Placement,
165 popup_len: Option<u16>,
166 popup: PopupCore,
167 popup_style: Style,
168 popup_scroll: Option<Scroll<'a>>,
169 popup_block: Option<Block<'a>>,
170
171 _phantom: PhantomData<T>,
172}
173
174#[derive(Debug, Clone)]
176pub struct ChoiceStyle {
177 pub style: Style,
178 pub button: Option<Style>,
179 pub select: Option<Style>,
180 pub select_marker: Option<char>,
181 pub focus: Option<Style>,
182 pub block: Option<Block<'static>>,
183 pub border_style: Option<Style>,
184 pub title_style: Option<Style>,
185
186 pub popup: PopupStyle,
187 pub popup_style: Option<Style>,
188 pub popup_border: Option<Style>,
189 pub popup_scroll: Option<ScrollStyle>,
190 pub popup_block: Option<Block<'static>>,
191 pub popup_len: Option<u16>,
192
193 pub behave_focus: Option<ChoiceFocus>,
194 pub behave_select: Option<ChoiceSelect>,
195 pub behave_close: Option<ChoiceClose>,
196
197 pub non_exhaustive: NonExhaustive,
198}
199
200#[derive(Debug)]
202pub struct ChoiceState<T = usize>
203where
204 T: PartialEq + Clone + Default,
205{
206 pub area: Rect,
209 pub inner: Rect,
212 pub nav_char: Vec<Vec<char>>,
215 pub item_area: Rect,
218 pub button_area: Rect,
221 pub item_areas: Vec<Rect>,
224 pub core: ChoiceCore<T>,
226 pub popup: PopupCoreState,
228 pub popup_scroll: ScrollState,
230 pub behave_focus: Rc<Cell<ChoiceFocus>>,
232 pub behave_select: ChoiceSelect,
235 pub behave_close: ChoiceClose,
238
239 pub focus: FocusFlag,
242 pub mouse: MouseFlags,
244
245 pub non_exhaustive: NonExhaustive,
246}
247
248pub(crate) mod event {
249 use rat_event::{ConsumedEvent, Outcome};
250 use rat_popup::event::PopupOutcome;
251
252 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
254 pub enum ChoiceOutcome {
255 Continue,
257 Unchanged,
259 Changed,
261 Value,
263 }
264
265 impl ConsumedEvent for ChoiceOutcome {
266 fn is_consumed(&self) -> bool {
267 *self != ChoiceOutcome::Continue
268 }
269 }
270
271 impl From<Outcome> for ChoiceOutcome {
272 fn from(value: Outcome) -> Self {
273 match value {
274 Outcome::Continue => ChoiceOutcome::Continue,
275 Outcome::Unchanged => ChoiceOutcome::Unchanged,
276 Outcome::Changed => ChoiceOutcome::Changed,
277 }
278 }
279 }
280
281 impl From<ChoiceOutcome> for Outcome {
282 fn from(value: ChoiceOutcome) -> Self {
283 match value {
284 ChoiceOutcome::Continue => Outcome::Continue,
285 ChoiceOutcome::Unchanged => Outcome::Unchanged,
286 ChoiceOutcome::Changed => Outcome::Changed,
287 ChoiceOutcome::Value => Outcome::Changed,
288 }
289 }
290 }
291
292 impl From<PopupOutcome> for ChoiceOutcome {
293 fn from(value: PopupOutcome) -> Self {
294 match value {
295 PopupOutcome::Continue => ChoiceOutcome::Continue,
296 PopupOutcome::Unchanged => ChoiceOutcome::Unchanged,
297 PopupOutcome::Changed => ChoiceOutcome::Changed,
298 PopupOutcome::Hide => ChoiceOutcome::Changed,
299 }
300 }
301 }
302}
303
304pub mod core {
305 #[derive(Debug, Default, Clone)]
306 pub struct ChoiceCore<T>
307 where
308 T: PartialEq + Clone + Default,
309 {
310 values: Vec<T>,
313 default_value: Option<T>,
315 value: T,
320 selected: Option<usize>,
322 }
323
324 impl<T> ChoiceCore<T>
325 where
326 T: PartialEq + Clone + Default,
327 {
328 pub fn set_values(&mut self, values: Vec<T>) {
329 self.values = values;
330 if self.values.is_empty() {
332 self.selected = None;
333 } else {
334 self.selected = self.values.iter().position(|v| *v == self.value);
335 }
336 }
337
338 pub fn values(&self) -> &[T] {
340 &self.values
341 }
342
343 pub fn set_default_value(&mut self, default_value: Option<T>) {
349 self.default_value = default_value.clone();
350 }
351
352 pub fn default_value(&self) -> &Option<T> {
354 &self.default_value
355 }
356
357 pub fn selected(&self) -> Option<usize> {
365 self.selected
366 }
367
368 pub fn set_selected(&mut self, select: usize) -> bool {
376 let old_sel = self.selected;
377 if self.values.is_empty() {
378 self.selected = None;
379 } else {
380 if let Some(value) = self.values.get(select) {
381 self.value = value.clone();
382 self.selected = Some(select);
383 } else {
384 self.selected = None;
386 }
387 }
388 old_sel != self.selected
389 }
390
391 pub fn set_value(&mut self, value: T) -> bool {
400 let old_value = self.value.clone();
401
402 self.value = value;
403 self.selected = self.values.iter().position(|v| *v == self.value);
404
405 old_value != self.value
406 }
407
408 pub fn value(&self) -> T {
410 self.value.clone()
411 }
412
413 pub fn is_empty(&self) -> bool {
414 self.values.is_empty()
415 }
416
417 pub fn clear(&mut self) -> bool {
418 let old_selected = self.selected;
419 let old_value = self.value.clone();
420
421 if let Some(default_value) = &self.default_value {
422 self.value = default_value.clone();
423 }
424
425 self.selected = self.values.iter().position(|v| *v == self.value);
426
427 old_selected != self.selected || old_value != self.value
428 }
429 }
430}
431
432impl Default for ChoiceStyle {
433 fn default() -> Self {
434 Self {
435 style: Default::default(),
436 button: Default::default(),
437 select: Default::default(),
438 select_marker: Default::default(),
439 focus: Default::default(),
440 block: Default::default(),
441 border_style: Default::default(),
442 title_style: Default::default(),
443 popup: Default::default(),
444 popup_style: Default::default(),
445 popup_border: Default::default(),
446 popup_scroll: Default::default(),
447 popup_block: Default::default(),
448 popup_len: Default::default(),
449 behave_focus: Default::default(),
450 behave_select: Default::default(),
451 behave_close: Default::default(),
452 non_exhaustive: NonExhaustive,
453 }
454 }
455}
456
457impl<T> Default for Choice<'_, T>
458where
459 T: PartialEq + Clone + Default,
460{
461 fn default() -> Self {
462 Self {
463 values: Default::default(),
464 default_value: Default::default(),
465 items: Default::default(),
466 style: Default::default(),
467 button_style: Default::default(),
468 select_style: Default::default(),
469 select_marker: Default::default(),
470 focus_style: Default::default(),
471 block: Default::default(),
472 popup_len: Default::default(),
473 popup_alignment: Alignment::Left,
474 popup_placement: Placement::BelowOrAbove,
475 popup: Default::default(),
476 popup_style: Default::default(),
477 popup_scroll: Default::default(),
478 popup_block: Default::default(),
479 behave_focus: Default::default(),
480 behave_select: Default::default(),
481 behave_close: Default::default(),
482 skip_item_render: Default::default(),
483 }
484 }
485}
486
487impl<'a> Choice<'a, usize> {
488 #[inline]
490 pub fn auto_items<V: Into<Line<'a>>>(self, items: impl IntoIterator<Item = V>) -> Self {
491 {
492 let mut values = self.values.borrow_mut();
493 let mut itemz = self.items.borrow_mut();
494
495 values.clear();
496 itemz.clear();
497
498 for (k, v) in items.into_iter().enumerate() {
499 values.push(k);
500 itemz.push(v.into());
501 }
502 }
503
504 self
505 }
506
507 pub fn auto_item(self, item: impl Into<Line<'a>>) -> Self {
509 let idx = self.values.borrow().len();
510 self.values.borrow_mut().push(idx);
511 self.items.borrow_mut().push(item.into());
512 self
513 }
514}
515
516impl<'a, T> Choice<'a, T>
517where
518 T: PartialEq + Clone + Default,
519{
520 pub fn new() -> Self {
521 Self::default()
522 }
523
524 #[inline]
526 pub fn items<V: Into<Line<'a>>>(self, items: impl IntoIterator<Item = (T, V)>) -> Self {
527 {
528 let mut values = self.values.borrow_mut();
529 let mut itemz = self.items.borrow_mut();
530
531 values.clear();
532 itemz.clear();
533
534 for (k, v) in items.into_iter() {
535 values.push(k);
536 itemz.push(v.into());
537 }
538 }
539
540 self
541 }
542
543 pub fn item(self, value: T, item: impl Into<Line<'a>>) -> Self {
545 self.values.borrow_mut().push(value);
546 self.items.borrow_mut().push(item.into());
547 self
548 }
549
550 pub fn default_value(mut self, default: T) -> Self {
552 self.default_value = Some(default);
553 self
554 }
555
556 pub fn styles(mut self, styles: ChoiceStyle) -> Self {
558 self.style = styles.style;
559 if styles.block.is_some() {
560 self.block = styles.block;
561 }
562 if let Some(border_style) = styles.border_style {
563 self.block = self.block.map(|v| v.border_style(border_style));
564 }
565 if let Some(title_style) = styles.title_style {
566 self.block = self.block.map(|v| v.title_style(title_style));
567 }
568 self.block = self.block.map(|v| v.style(self.style));
569 if styles.button.is_some() {
570 self.button_style = styles.button;
571 }
572 if styles.select.is_some() {
573 self.select_style = styles.select;
574 }
575 if let Some(marker) = styles.select_marker {
576 self.select_marker = Some(marker);
577 }
578 if styles.focus.is_some() {
579 self.focus_style = styles.focus;
580 }
581 if let Some(select) = styles.behave_focus {
582 self.behave_focus = select;
583 }
584 if let Some(select) = styles.behave_select {
585 self.behave_select = select;
586 }
587 if let Some(close) = styles.behave_close {
588 self.behave_close = close;
589 }
590 if let Some(alignment) = styles.popup.alignment {
591 self.popup_alignment = alignment;
592 }
593 if let Some(placement) = styles.popup.placement {
594 self.popup_placement = placement;
595 }
596 self.popup = self.popup.styles(styles.popup.clone());
597 if let Some(popup_style) = styles.popup_style {
598 self.popup_style = popup_style;
599 }
600 if let Some(popup_scroll) = styles.popup_scroll {
601 self.popup_scroll = self.popup_scroll.map(|v| v.styles(popup_scroll));
602 }
603 self.popup_block = self.popup_block.map(|v| v.style(self.popup_style));
604 if let Some(border_style) = styles.popup_border {
605 self.popup_block = self.popup_block.map(|v| v.border_style(border_style));
606 }
607 if styles.popup_block.is_some() {
608 self.popup_block = styles.popup_block;
609 }
610 if styles.popup_len.is_some() {
611 self.popup_len = styles.popup_len;
612 }
613
614 self
615 }
616
617 pub fn style(mut self, style: Style) -> Self {
619 self.style = style;
620 self.block = self.block.map(|v| v.style(self.style));
621 self
622 }
623
624 pub fn button_style(mut self, style: Style) -> Self {
626 self.button_style = Some(style);
627 self
628 }
629
630 pub fn select_style(mut self, style: Style) -> Self {
632 self.select_style = Some(style);
633 self
634 }
635
636 pub fn select_marker(mut self, marker: char) -> Self {
638 self.select_marker = Some(marker);
639 self
640 }
641
642 pub fn focus_style(mut self, style: Style) -> Self {
644 self.focus_style = Some(style);
645 self
646 }
647
648 pub fn block(mut self, block: Block<'a>) -> Self {
650 self.block = Some(block);
651 self.block = self.block.map(|v| v.style(self.style));
652 self
653 }
654
655 pub fn skip_item_render(mut self, skip: bool) -> Self {
658 self.skip_item_render = skip;
659 self
660 }
661
662 pub fn popup_alignment(mut self, alignment: Alignment) -> Self {
667 self.popup_alignment = alignment;
668 self
669 }
670
671 pub fn popup_placement(mut self, placement: Placement) -> Self {
676 self.popup_placement = placement;
677 self
678 }
679
680 pub fn popup_boundary(mut self, boundary: Rect) -> Self {
682 self.popup = self.popup.boundary(boundary);
683 self
684 }
685
686 pub fn popup_len(mut self, len: u16) -> Self {
691 self.popup_len = Some(len);
692 self
693 }
694
695 pub fn popup_style(mut self, style: Style) -> Self {
697 self.popup_style = style;
698 self
699 }
700
701 pub fn popup_block(mut self, block: Block<'a>) -> Self {
703 self.popup_block = Some(block);
704 self
705 }
706
707 pub fn popup_scroll(mut self, scroll: Scroll<'a>) -> Self {
709 self.popup_scroll = Some(scroll);
710 self
711 }
712
713 pub fn popup_offset(mut self, offset: (i16, i16)) -> Self {
720 self.popup = self.popup.offset(offset);
721 self
722 }
723
724 pub fn popup_x_offset(mut self, offset: i16) -> Self {
727 self.popup = self.popup.x_offset(offset);
728 self
729 }
730
731 pub fn popup_y_offset(mut self, offset: i16) -> Self {
734 self.popup = self.popup.y_offset(offset);
735 self
736 }
737
738 pub fn behave_focus(mut self, focus: ChoiceFocus) -> Self {
740 self.behave_focus = focus;
741 self
742 }
743
744 pub fn behave_select(mut self, select: ChoiceSelect) -> Self {
746 self.behave_select = select;
747 self
748 }
749
750 pub fn behave_close(mut self, close: ChoiceClose) -> Self {
752 self.behave_close = close;
753 self
754 }
755
756 pub fn width(&self) -> u16 {
758 let w = self
759 .items
760 .borrow()
761 .iter()
762 .map(|v| v.width())
763 .max()
764 .unwrap_or_default();
765
766 w as u16 + block_size(&self.block).width
767 }
768
769 pub fn height(&self) -> u16 {
771 1 + block_size(&self.block).height
772 }
773
774 pub fn into_widgets(self) -> (ChoiceWidget<'a, T>, ChoicePopup<'a, T>) {
778 (
779 ChoiceWidget {
780 values: self.values,
781 default_value: self.default_value,
782 items: self.items.clone(),
783 style: self.style,
784 button_style: self.button_style,
785 focus_style: self.focus_style,
786 block: self.block,
787 skip_item_render: self.skip_item_render,
788 len: self.popup_len,
789 behave_focus: self.behave_focus,
790 behave_select: self.behave_select,
791 behave_close: self.behave_close,
792 },
793 ChoicePopup {
794 items: self.items.clone(),
795 style: self.style,
796 select_style: self.select_style,
797 select_marker: self.select_marker,
798 popup: self.popup,
799 popup_style: self.popup_style,
800 popup_scroll: self.popup_scroll,
801 popup_block: self.popup_block,
802 popup_alignment: self.popup_alignment,
803 popup_placement: self.popup_placement,
804 popup_len: self.popup_len,
805 _phantom: Default::default(),
806 },
807 )
808 }
809}
810
811impl<'a, T> ChoiceWidget<'a, T>
812where
813 T: PartialEq + Clone + Default,
814{
815 pub fn width(&self) -> u16 {
817 let w = self
818 .items
819 .borrow()
820 .iter()
821 .map(|v| v.width())
822 .max()
823 .unwrap_or_default();
824
825 w as u16 + block_size(&self.block).width
826 }
827
828 pub fn height(&self) -> u16 {
830 1 + block_size(&self.block).height
831 }
832}
833
834impl<'a, T> StatefulWidget for &ChoiceWidget<'a, T>
835where
836 T: PartialEq + Clone + Default,
837{
838 type State = ChoiceState<T>;
839
840 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
841 state.core.set_values(self.values.borrow().clone());
842 if let Some(default_value) = self.default_value.clone() {
843 state.core.set_default_value(Some(default_value));
844 }
845
846 render_choice(self, area, buf, state);
847 }
848}
849
850impl<T> StatefulWidget for ChoiceWidget<'_, T>
851where
852 T: PartialEq + Clone + Default,
853{
854 type State = ChoiceState<T>;
855
856 fn render(mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
857 state.core.set_values(self.values.take());
858 if let Some(default_value) = self.default_value.take() {
859 state.core.set_default_value(Some(default_value));
860 }
861
862 render_choice(&self, area, buf, state);
863 }
864}
865
866fn render_choice<T: PartialEq + Clone + Default>(
867 widget: &ChoiceWidget<'_, T>,
868 area: Rect,
869 buf: &mut Buffer,
870 state: &mut ChoiceState<T>,
871) {
872 state.area = area;
873 state.behave_focus.set(widget.behave_focus);
874 state.behave_select = widget.behave_select;
875 state.behave_close = widget.behave_close;
876
877 if !state.popup.is_active() {
878 let len = widget
879 .len
880 .unwrap_or_else(|| min(5, widget.items.borrow().len()) as u16);
881 state.popup_scroll.max_offset = widget.items.borrow().len().saturating_sub(len as usize);
882 state.popup_scroll.page_len = len as usize;
883 if let Some(selected) = state.core.selected() {
884 state.popup_scroll.scroll_to_pos(selected);
885 }
886 }
887
888 state.nav_char.clear();
889 state.nav_char.extend(widget.items.borrow().iter().map(|v| {
890 v.spans
891 .first()
892 .and_then(|v| v.content.as_ref().chars().next())
893 .map_or(Vec::default(), |c| c.to_lowercase().collect::<Vec<_>>())
894 }));
895
896 state.inner = widget.block.inner_if_some(area);
897
898 state.item_area = Rect::new(
899 state.inner.x,
900 state.inner.y,
901 state.inner.width.saturating_sub(3),
902 state.inner.height,
903 );
904 state.button_area = Rect::new(
905 state
906 .inner
907 .right()
908 .saturating_sub(min(3, state.inner.width)),
909 state.inner.y,
910 3,
911 state.inner.height,
912 );
913
914 let style = widget.style;
915 let focus_style = widget.focus_style.unwrap_or(revert_style(widget.style));
916
917 if state.is_focused() {
918 if let Some(block) = &widget.block {
919 block.render(area, buf);
920 } else {
921 buf.set_style(state.inner, style);
922 }
923 buf.set_style(state.inner, focus_style);
924 } else {
925 if let Some(block) = &widget.block {
926 block.render(area, buf);
927 } else {
928 buf.set_style(state.inner, style);
929 }
930 if let Some(button_style) = widget.button_style {
931 buf.set_style(state.button_area, button_style);
932 }
933 }
934
935 if !widget.skip_item_render {
936 if let Some(selected) = state.core.selected() {
937 if let Some(item) = widget.items.borrow().get(selected) {
938 item.render(state.item_area, buf);
939 }
940 }
941 }
942
943 let dy = if (state.button_area.height & 1) == 1 {
944 state.button_area.height / 2
945 } else {
946 state.button_area.height.saturating_sub(1) / 2
947 };
948 let bc = if state.is_popup_active() {
949 " â—† "
950 } else {
951 " â–¼ "
952 };
953 Span::from(bc).render(
954 Rect::new(state.button_area.x, state.button_area.y + dy, 3, 1),
955 buf,
956 );
957}
958
959impl<T> ChoicePopup<'_, T>
960where
961 T: PartialEq + Clone + Default,
962{
963 pub fn layout(&self, area: Rect, buf: &mut Buffer, state: &mut ChoiceState<T>) -> Rect {
966 if state.popup.is_active() {
967 let len = min(
968 self.popup_len.unwrap_or(5),
969 self.items.borrow().len() as u16,
970 );
971 let padding = block_padding(&self.popup_block);
972 let popup_len = len + padding.top + padding.bottom;
973 let pop_area = Rect::new(0, 0, area.width, popup_len);
974
975 self.popup
976 .ref_constraint(
977 self.popup_placement
978 .into_constraint(self.popup_alignment, area),
979 )
980 .layout(pop_area, buf)
981 } else {
982 Rect::default()
983 }
984 }
985}
986
987impl<T> StatefulWidget for &ChoicePopup<'_, T>
988where
989 T: PartialEq + Clone + Default,
990{
991 type State = ChoiceState<T>;
992
993 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
994 render_popup(self, area, buf, state);
995 }
996}
997
998impl<T> StatefulWidget for ChoicePopup<'_, T>
999where
1000 T: PartialEq + Clone + Default,
1001{
1002 type State = ChoiceState<T>;
1003
1004 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
1005 render_popup(&self, area, buf, state);
1006 }
1007}
1008
1009fn render_popup<T: PartialEq + Clone + Default>(
1010 widget: &ChoicePopup<'_, T>,
1011 area: Rect,
1012 buf: &mut Buffer,
1013 state: &mut ChoiceState<T>,
1014) {
1015 if state.popup.is_active() {
1016 let popup_style = widget.popup_style;
1017 let select_style = widget.select_style.unwrap_or(revert_style(widget.style));
1018
1019 {
1020 let len = min(
1021 widget.popup_len.unwrap_or(5),
1022 widget.items.borrow().len() as u16,
1023 );
1024 let padding = block_padding(&widget.popup_block);
1025 let popup_len = len + padding.top + padding.bottom;
1026 let pop_area = Rect::new(0, 0, area.width, popup_len);
1027
1028 widget
1029 .popup
1030 .ref_constraint(
1031 widget
1032 .popup_placement
1033 .into_constraint(widget.popup_alignment, area),
1034 )
1035 .render(pop_area, buf, &mut state.popup);
1036 }
1037
1038 let sa = ScrollArea::new()
1039 .style(fallback_popup_style(widget.popup_style))
1040 .block(widget.popup_block.as_ref())
1041 .v_scroll(widget.popup_scroll.as_ref());
1042
1043 let inner = sa.inner(state.popup.area, None, Some(&state.popup_scroll));
1044
1045 sa.render(
1046 state.popup.area,
1047 buf,
1048 &mut ScrollAreaState::new().v_scroll(&mut state.popup_scroll),
1049 );
1050
1051 state.popup_scroll.max_offset = widget
1052 .items
1053 .borrow()
1054 .len()
1055 .saturating_sub(inner.height as usize);
1056 state.popup_scroll.page_len = inner.height as usize;
1057
1058 state.item_areas.clear();
1059 let mut row = inner.y;
1060 let mut idx = state.popup_scroll.offset;
1061
1062 let hidden_marker;
1063 let (marker_len, marker, no_marker) = {
1064 if let Some(marker) = widget.select_marker {
1065 hidden_marker = marker.to_string();
1066 if unicode_display_width::is_double_width(marker) {
1067 (2, Some(hidden_marker.as_str()), Some(" "))
1068 } else {
1069 (1, Some(hidden_marker.as_str()), Some(" "))
1070 }
1071 } else {
1072 (0, None, None)
1073 }
1074 };
1075 loop {
1076 if row >= inner.bottom() {
1077 break;
1078 }
1079
1080 let item_area = Rect::new(inner.x, row, inner.width, 1);
1081 state.item_areas.push(item_area);
1082
1083 let (style, marker) = if state.core.selected() == Some(idx) {
1084 (popup_style.patch(select_style), marker)
1085 } else {
1086 (popup_style, no_marker)
1087 };
1088 buf.set_style(item_area, style);
1089
1090 if let Some(item) = widget.items.borrow().get(idx) {
1091 if let Some(marker) = &marker {
1092 marker.render(
1093 Rect::new(item_area.x, item_area.y, marker_len, item_area.height),
1094 buf,
1095 );
1096 item.render(
1097 Rect::new(
1098 item_area.x + marker_len,
1099 item_area.y,
1100 item_area.width.saturating_sub(marker_len),
1101 item_area.height,
1102 ),
1103 buf,
1104 );
1105 } else {
1106 item.render(item_area, buf);
1107 }
1108 } else {
1109 }
1111
1112 row += 1;
1113 idx += 1;
1114 }
1115 } else {
1116 state.popup.clear_areas();
1117 }
1118}
1119
1120impl<T> Clone for ChoiceState<T>
1121where
1122 T: PartialEq + Clone + Default,
1123{
1124 fn clone(&self) -> Self {
1125 let popup = self.popup.clone();
1126 let behave_focus = Rc::new(Cell::new(self.behave_focus.get()));
1127 let focus = focus_cb(
1128 popup.active.clone(),
1129 behave_focus.clone(),
1130 self.focus.new_instance(),
1131 );
1132
1133 Self {
1134 area: self.area,
1135 inner: self.inner,
1136 nav_char: self.nav_char.clone(),
1137 item_area: self.item_area,
1138 button_area: self.button_area,
1139 item_areas: self.item_areas.clone(),
1140 core: self.core.clone(),
1141 popup,
1142 popup_scroll: self.popup_scroll.clone(),
1143 behave_focus,
1144 behave_select: self.behave_select,
1145 behave_close: self.behave_close,
1146 focus,
1147 mouse: Default::default(),
1148 non_exhaustive: NonExhaustive,
1149 }
1150 }
1151}
1152
1153impl<T> Default for ChoiceState<T>
1154where
1155 T: PartialEq + Clone + Default,
1156{
1157 fn default() -> Self {
1158 let popup = PopupCoreState::default();
1159 let behave_focus = Rc::new(Cell::new(ChoiceFocus::default()));
1160 let focus = focus_cb(
1161 popup.active.clone(),
1162 behave_focus.clone(),
1163 Default::default(),
1164 );
1165
1166 Self {
1167 area: Default::default(),
1168 inner: Default::default(),
1169 nav_char: Default::default(),
1170 item_area: Default::default(),
1171 button_area: Default::default(),
1172 item_areas: Default::default(),
1173 core: Default::default(),
1174 popup,
1175 popup_scroll: Default::default(),
1176 behave_focus,
1177 behave_select: Default::default(),
1178 behave_close: Default::default(),
1179 focus,
1180 mouse: Default::default(),
1181 non_exhaustive: NonExhaustive,
1182 }
1183 }
1184}
1185
1186fn focus_cb(
1187 active: Rc<Cell<bool>>,
1188 behave_focus: Rc<Cell<ChoiceFocus>>,
1189 flag: FocusFlag,
1190) -> FocusFlag {
1191 let active_clone = active.clone();
1192 flag.on_lost(move || {
1193 if active_clone.get() {
1194 active_clone.set(false);
1195 }
1196 });
1197 flag.on_gained(move || {
1198 if !active.get() {
1199 if behave_focus.get() == ChoiceFocus::OpenOnFocusGained {
1200 active.set(true);
1201 }
1202 }
1203 });
1204 flag
1205}
1206
1207impl<T> HasFocus for ChoiceState<T>
1208where
1209 T: PartialEq + Clone + Default,
1210{
1211 fn build(&self, builder: &mut FocusBuilder) {
1212 builder.widget_with_flags(self.focus(), self.area(), 0, self.navigable());
1213 builder.widget_with_flags(self.focus(), self.popup.area, 1, Navigation::Mouse);
1214 }
1215
1216 fn focus(&self) -> FocusFlag {
1217 self.focus.clone()
1218 }
1219
1220 fn area(&self) -> Rect {
1221 self.area
1222 }
1223}
1224
1225impl<T> HasScreenCursor for ChoiceState<T>
1226where
1227 T: PartialEq + Clone + Default,
1228{
1229 fn screen_cursor(&self) -> Option<(u16, u16)> {
1230 None
1231 }
1232}
1233
1234impl<T> RelocatableState for ChoiceState<T>
1235where
1236 T: PartialEq + Clone + Default,
1237{
1238 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1239 self.area.relocate(shift, clip);
1240 self.inner.relocate(shift, clip);
1241 self.item_area.relocate(shift, clip);
1242 self.button_area.relocate(shift, clip);
1243 }
1244
1245 fn relocate_popup(&mut self, shift: (i16, i16), clip: Rect) {
1246 self.item_areas.relocate(shift, clip);
1247 self.popup.relocate_popup(shift, clip);
1248 self.popup_scroll.relocate(shift, clip);
1249 }
1250}
1251
1252impl<T> ChoiceState<T>
1253where
1254 T: PartialEq + Clone + Default,
1255{
1256 pub fn new() -> Self {
1257 Self::default()
1258 }
1259
1260 pub fn named(name: &str) -> Self {
1261 let mut z = Self::default();
1262 z.focus = z.focus.with_name(name);
1263 z
1264 }
1265
1266 pub fn is_popup_active(&self) -> bool {
1268 self.popup.is_active()
1269 }
1270
1271 pub fn flip_popup_active(&mut self) {
1273 self.popup.flip_active();
1274 }
1275
1276 pub fn set_popup_active(&mut self, active: bool) -> bool {
1278 self.popup.set_active(active)
1279 }
1280
1281 pub fn set_default_value(&mut self, default_value: Option<T>) {
1290 self.core.set_default_value(default_value);
1291 }
1292
1293 pub fn default_value(&self) -> &Option<T> {
1295 self.core.default_value()
1296 }
1297
1298 pub fn set_value(&mut self, value: T) -> bool {
1309 self.core.set_value(value)
1310 }
1311
1312 pub fn value(&self) -> T {
1314 self.core.value()
1315 }
1316
1317 pub fn clear(&mut self) -> bool {
1319 self.core.clear()
1320 }
1321
1322 pub fn select(&mut self, select: usize) -> bool {
1334 self.core.set_selected(select)
1335 }
1336
1337 pub fn selected(&self) -> Option<usize> {
1342 self.core.selected()
1343 }
1344
1345 pub fn is_empty(&self) -> bool {
1347 self.core.values().is_empty()
1348 }
1349
1350 pub fn len(&self) -> usize {
1352 self.core.values().len()
1353 }
1354
1355 pub fn clear_offset(&mut self) {
1357 self.popup_scroll.set_offset(0);
1358 }
1359
1360 pub fn set_offset(&mut self, offset: usize) -> bool {
1362 self.popup_scroll.set_offset(offset)
1363 }
1364
1365 pub fn offset(&self) -> usize {
1367 self.popup_scroll.offset()
1368 }
1369
1370 pub fn max_offset(&self) -> usize {
1372 self.popup_scroll.max_offset()
1373 }
1374
1375 pub fn page_len(&self) -> usize {
1377 self.popup_scroll.page_len()
1378 }
1379
1380 pub fn scroll_by(&self) -> usize {
1382 self.popup_scroll.scroll_by()
1383 }
1384
1385 pub fn scroll_to_selected(&mut self) -> bool {
1387 if let Some(selected) = self.core.selected() {
1388 self.popup_scroll.scroll_to_pos(selected)
1389 } else {
1390 false
1391 }
1392 }
1393}
1394
1395impl<T> ChoiceState<T>
1396where
1397 T: PartialEq + Clone + Default,
1398{
1399 pub fn select_by_char(&mut self, c: char) -> bool {
1401 if self.nav_char.is_empty() {
1402 return false;
1403 }
1404 let c = c.to_lowercase().collect::<Vec<_>>();
1405
1406 let (mut idx, end_loop) = if let Some(idx) = self.core.selected() {
1407 (idx + 1, idx)
1408 } else {
1409 if self.nav_char[0] == c {
1410 self.core.set_selected(0);
1411 return true;
1412 } else {
1413 (1, 0)
1414 }
1415 };
1416 loop {
1417 if idx >= self.nav_char.len() {
1418 idx = 0;
1419 }
1420 if idx == end_loop {
1421 break;
1422 }
1423
1424 if self.nav_char[idx] == c {
1425 self.core.set_selected(idx);
1426 return true;
1427 }
1428
1429 idx += 1;
1430 }
1431 false
1432 }
1433
1434 pub fn reverse_select_by_char(&mut self, c: char) -> bool {
1436 if self.nav_char.is_empty() {
1437 return false;
1438 }
1439 let c = c.to_lowercase().collect::<Vec<_>>();
1440
1441 let (mut idx, end_loop) = if let Some(idx) = self.core.selected() {
1442 if idx == 0 {
1443 (self.nav_char.len() - 1, 0)
1444 } else {
1445 (idx - 1, idx)
1446 }
1447 } else {
1448 if self.nav_char.last() == Some(&c) {
1449 self.core.set_selected(self.nav_char.len() - 1);
1450 return true;
1451 } else {
1452 (self.nav_char.len() - 1, 0)
1453 }
1454 };
1455 loop {
1456 if self.nav_char[idx] == c {
1457 self.core.set_selected(idx);
1458 return true;
1459 }
1460
1461 if idx == end_loop {
1462 break;
1463 }
1464
1465 if idx == 0 {
1466 idx = self.nav_char.len() - 1;
1467 } else {
1468 idx -= 1;
1469 }
1470 }
1471 false
1472 }
1473
1474 pub fn move_to(&mut self, n: usize) -> ChoiceOutcome {
1476 let old_selected = self.selected();
1477 let r1 = self.popup.set_active(true);
1478 let r2 = self.select(n);
1479 let r3 = self.scroll_to_selected();
1480 if old_selected != self.selected() {
1481 ChoiceOutcome::Value
1482 } else if r1 || r2 || r3 {
1483 ChoiceOutcome::Changed
1484 } else {
1485 ChoiceOutcome::Continue
1486 }
1487 }
1488
1489 pub fn move_down(&mut self, n: usize) -> ChoiceOutcome {
1491 if self.core.is_empty() {
1492 return ChoiceOutcome::Continue;
1493 }
1494
1495 let old_selected = self.selected();
1496 let r1 = self.popup.set_active(true);
1497 let idx = if let Some(idx) = self.core.selected() {
1498 idx + n
1499 } else {
1500 n.saturating_sub(1)
1501 };
1502 let idx = idx.clamp(0, self.len() - 1);
1503 let r2 = self.core.set_selected(idx);
1504 let r3 = self.scroll_to_selected();
1505
1506 if old_selected != self.selected() {
1507 ChoiceOutcome::Value
1508 } else if r1 || r2 || r3 {
1509 ChoiceOutcome::Changed
1510 } else {
1511 ChoiceOutcome::Continue
1512 }
1513 }
1514
1515 pub fn move_up(&mut self, n: usize) -> ChoiceOutcome {
1517 if self.core.is_empty() {
1518 return ChoiceOutcome::Continue;
1519 }
1520
1521 let old_selected = self.selected();
1522 let r1 = self.popup.set_active(true);
1523 let idx = if let Some(idx) = self.core.selected() {
1524 idx.saturating_sub(n)
1525 } else {
1526 0
1527 };
1528 let idx = idx.clamp(0, self.len() - 1);
1529 let r2 = self.core.set_selected(idx);
1530 let r3 = self.scroll_to_selected();
1531
1532 if old_selected != self.selected() {
1533 ChoiceOutcome::Value
1534 } else if r1 || r2 || r3 {
1535 ChoiceOutcome::Changed
1536 } else {
1537 ChoiceOutcome::Continue
1538 }
1539 }
1540}
1541
1542impl<T: PartialEq + Clone + Default> HandleEvent<crossterm::event::Event, Popup, ChoiceOutcome>
1543 for ChoiceState<T>
1544{
1545 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Popup) -> ChoiceOutcome {
1546 let r = if self.is_focused() {
1547 match event {
1548 ct_event!(key press ' ') | ct_event!(keycode press Enter) => {
1549 self.flip_popup_active();
1550 ChoiceOutcome::Changed
1551 }
1552 ct_event!(keycode press Esc) => {
1553 if self.set_popup_active(false) {
1554 ChoiceOutcome::Changed
1555 } else {
1556 ChoiceOutcome::Continue
1557 }
1558 }
1559 ct_event!(key press c) => {
1560 if self.select_by_char(*c) {
1561 self.scroll_to_selected();
1562 ChoiceOutcome::Value
1563 } else {
1564 ChoiceOutcome::Continue
1565 }
1566 }
1567 ct_event!(key press SHIFT-c) => {
1568 if self.reverse_select_by_char(*c) {
1569 self.scroll_to_selected();
1570 ChoiceOutcome::Value
1571 } else {
1572 ChoiceOutcome::Continue
1573 }
1574 }
1575 ct_event!(keycode press Delete) | ct_event!(keycode press Backspace) => {
1576 if self.clear() {
1577 ChoiceOutcome::Value
1578 } else {
1579 ChoiceOutcome::Continue
1580 }
1581 }
1582 ct_event!(keycode press Down) => self.move_down(1),
1583 ct_event!(keycode press Up) => self.move_up(1),
1584 ct_event!(keycode press PageUp) => self.move_up(self.page_len()),
1585 ct_event!(keycode press PageDown) => self.move_down(self.page_len()),
1586 ct_event!(keycode press Home) => self.move_to(0),
1587 ct_event!(keycode press End) => self.move_to(self.len().saturating_sub(1)),
1588 _ => ChoiceOutcome::Continue,
1589 }
1590 } else {
1591 ChoiceOutcome::Continue
1592 };
1593
1594 if !r.is_consumed() {
1595 self.handle(event, MouseOnly)
1596 } else {
1597 r
1598 }
1599 }
1600}
1601
1602impl<T: PartialEq + Clone + Default> HandleEvent<crossterm::event::Event, MouseOnly, ChoiceOutcome>
1603 for ChoiceState<T>
1604{
1605 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> ChoiceOutcome {
1606 let r0 = handle_mouse(self, event);
1607 let r1 = handle_select(self, event);
1608 let r2 = handle_close(self, event);
1609 let mut r = max(r0, max(r1, r2));
1610
1611 r = r.or_else(|| mouse_trap(event, self.popup.area).into());
1612
1613 r
1614 }
1615}
1616
1617fn handle_mouse<T: PartialEq + Clone + Default>(
1618 state: &mut ChoiceState<T>,
1619 event: &crossterm::event::Event,
1620) -> ChoiceOutcome {
1621 match event {
1622 ct_event!(mouse down Left for x,y)
1623 if state.item_area.contains((*x, *y).into())
1624 || state.button_area.contains((*x, *y).into()) =>
1625 {
1626 if !state.gained_focus() {
1627 state.flip_popup_active();
1628 ChoiceOutcome::Changed
1629 } else {
1630 ChoiceOutcome::Continue
1633 }
1634 }
1635 ct_event!(mouse down Left for x,y)
1636 | ct_event!(mouse down Right for x,y)
1637 | ct_event!(mouse down Middle for x,y)
1638 if !state.item_area.contains((*x, *y).into())
1639 && !state.button_area.contains((*x, *y).into()) =>
1640 {
1641 match state.popup.handle(event, Popup) {
1642 PopupOutcome::Hide => {
1643 state.set_popup_active(false);
1644 ChoiceOutcome::Changed
1645 }
1646 r => r.into(),
1647 }
1648 }
1649 _ => ChoiceOutcome::Continue,
1650 }
1651}
1652
1653fn handle_select<T: PartialEq + Clone + Default>(
1654 state: &mut ChoiceState<T>,
1655 event: &crossterm::event::Event,
1656) -> ChoiceOutcome {
1657 match state.behave_select {
1658 ChoiceSelect::MouseScroll => {
1659 let mut sas = ScrollAreaState::new()
1660 .area(state.popup.area)
1661 .v_scroll(&mut state.popup_scroll);
1662 let mut r = match sas.handle(event, MouseOnly) {
1663 ScrollOutcome::Up(n) => state.move_up(n),
1664 ScrollOutcome::Down(n) => state.move_down(n),
1665 ScrollOutcome::VPos(n) => state.move_to(n),
1666 _ => ChoiceOutcome::Continue,
1667 };
1668
1669 r = r.or_else(|| match event {
1670 ct_event!(mouse down Left for x,y)
1671 if state.popup.area.contains((*x, *y).into()) =>
1672 {
1673 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1674 state.move_to(state.offset() + n)
1675 } else {
1676 ChoiceOutcome::Unchanged
1677 }
1678 }
1679 ct_event!(mouse drag Left for x,y)
1680 if state.popup.area.contains((*x, *y).into()) =>
1681 {
1682 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1683 state.move_to(state.offset() + n)
1684 } else {
1685 ChoiceOutcome::Unchanged
1686 }
1687 }
1688 _ => ChoiceOutcome::Continue,
1689 });
1690 r
1691 }
1692 ChoiceSelect::MouseMove => {
1693 let mut r = if let Some(selected) = state.core.selected() {
1695 let rel_sel = selected.saturating_sub(state.offset());
1696 let mut sas = ScrollAreaState::new()
1697 .area(state.popup.area)
1698 .v_scroll(&mut state.popup_scroll);
1699 match sas.handle(event, MouseOnly) {
1700 ScrollOutcome::Up(n) => {
1701 state.popup_scroll.scroll_up(n);
1702 if state.select(state.offset() + rel_sel) {
1703 ChoiceOutcome::Value
1704 } else {
1705 ChoiceOutcome::Unchanged
1706 }
1707 }
1708 ScrollOutcome::Down(n) => {
1709 state.popup_scroll.scroll_down(n);
1710 if state.select(state.offset() + rel_sel) {
1711 ChoiceOutcome::Value
1712 } else {
1713 ChoiceOutcome::Unchanged
1714 }
1715 }
1716 ScrollOutcome::VPos(n) => {
1717 if state.popup_scroll.set_offset(n) {
1718 ChoiceOutcome::Value
1719 } else {
1720 ChoiceOutcome::Unchanged
1721 }
1722 }
1723 _ => ChoiceOutcome::Continue,
1724 }
1725 } else {
1726 ChoiceOutcome::Continue
1727 };
1728
1729 r = r.or_else(|| match event {
1730 ct_event!(mouse moved for x,y) if state.popup.area.contains((*x, *y).into()) => {
1731 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1732 state.move_to(state.offset() + n)
1733 } else {
1734 ChoiceOutcome::Unchanged
1735 }
1736 }
1737 _ => ChoiceOutcome::Continue,
1738 });
1739 r
1740 }
1741 ChoiceSelect::MouseClick => {
1742 let mut sas = ScrollAreaState::new()
1744 .area(state.popup.area)
1745 .v_scroll(&mut state.popup_scroll);
1746 let mut r = match sas.handle(event, MouseOnly) {
1747 ScrollOutcome::Up(n) => {
1748 if state.popup_scroll.scroll_up(n) {
1749 ChoiceOutcome::Changed
1750 } else {
1751 ChoiceOutcome::Unchanged
1752 }
1753 }
1754 ScrollOutcome::Down(n) => {
1755 if state.popup_scroll.scroll_down(n) {
1756 ChoiceOutcome::Changed
1757 } else {
1758 ChoiceOutcome::Unchanged
1759 }
1760 }
1761 ScrollOutcome::VPos(n) => {
1762 if state.popup_scroll.set_offset(n) {
1763 ChoiceOutcome::Changed
1764 } else {
1765 ChoiceOutcome::Unchanged
1766 }
1767 }
1768 _ => ChoiceOutcome::Continue,
1769 };
1770
1771 r = r.or_else(|| match event {
1772 ct_event!(mouse down Left for x,y)
1773 if state.popup.area.contains((*x, *y).into()) =>
1774 {
1775 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1776 state.move_to(state.offset() + n)
1777 } else {
1778 ChoiceOutcome::Unchanged
1779 }
1780 }
1781 ct_event!(mouse drag Left for x,y)
1782 if state.popup.area.contains((*x, *y).into()) =>
1783 {
1784 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1785 state.move_to(state.offset() + n)
1786 } else {
1787 ChoiceOutcome::Unchanged
1788 }
1789 }
1790 _ => ChoiceOutcome::Continue,
1791 });
1792 r
1793 }
1794 }
1795}
1796
1797fn handle_close<T: PartialEq + Clone + Default>(
1798 state: &mut ChoiceState<T>,
1799 event: &crossterm::event::Event,
1800) -> ChoiceOutcome {
1801 match state.behave_close {
1802 ChoiceClose::SingleClick => match event {
1803 ct_event!(mouse down Left for x,y) if state.popup.area.contains((*x, *y).into()) => {
1804 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1805 let r = state.move_to(state.offset() + n);
1806 let s = if state.set_popup_active(false) {
1807 ChoiceOutcome::Changed
1808 } else {
1809 ChoiceOutcome::Unchanged
1810 };
1811 max(r, s)
1812 } else {
1813 ChoiceOutcome::Unchanged
1814 }
1815 }
1816 _ => ChoiceOutcome::Continue,
1817 },
1818 ChoiceClose::DoubleClick => match event {
1819 ct_event!(mouse any for m) if state.mouse.doubleclick(state.popup.area, m) => {
1820 if let Some(n) = item_at(&state.item_areas, m.column, m.row) {
1821 let r = state.move_to(state.offset() + n);
1822 let s = if state.set_popup_active(false) {
1823 ChoiceOutcome::Changed
1824 } else {
1825 ChoiceOutcome::Unchanged
1826 };
1827 max(r, s)
1828 } else {
1829 ChoiceOutcome::Unchanged
1830 }
1831 }
1832 _ => ChoiceOutcome::Continue,
1833 },
1834 }
1835}
1836
1837pub fn handle_events<T: PartialEq + Clone + Default>(
1841 state: &mut ChoiceState<T>,
1842 focus: bool,
1843 event: &crossterm::event::Event,
1844) -> ChoiceOutcome {
1845 state.focus.set(focus);
1846 HandleEvent::handle(state, event, Popup)
1847}
1848
1849pub fn handle_mouse_events<T: PartialEq + Clone + Default>(
1851 state: &mut ChoiceState<T>,
1852 event: &crossterm::event::Event,
1853) -> ChoiceOutcome {
1854 HandleEvent::handle(state, event, MouseOnly)
1855}