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