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, 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, buf, state);
1006 }
1007}
1008
1009fn render_popup<T: PartialEq + Clone + Default>(
1010 widget: &ChoicePopup<'_, T>,
1011 buf: &mut Buffer,
1012 state: &mut ChoiceState<T>,
1013) {
1014 if state.popup.is_active() {
1015 let popup_style = widget.popup_style;
1016 let select_style = widget.select_style.unwrap_or(revert_style(widget.style));
1017
1018 {
1019 let area = state.area;
1020
1021 let len = min(
1022 widget.popup_len.unwrap_or(5),
1023 widget.items.borrow().len() as u16,
1024 );
1025 let padding = block_padding(&widget.popup_block);
1026 let popup_len = len + padding.top + padding.bottom;
1027 let pop_area = Rect::new(0, 0, area.width, popup_len);
1028
1029 widget
1030 .popup
1031 .ref_constraint(
1032 widget
1033 .popup_placement
1034 .into_constraint(widget.popup_alignment, area),
1035 )
1036 .render(pop_area, buf, &mut state.popup);
1037 }
1038
1039 let sa = ScrollArea::new()
1040 .style(fallback_popup_style(widget.popup_style))
1041 .block(widget.popup_block.as_ref())
1042 .v_scroll(widget.popup_scroll.as_ref());
1043
1044 let inner = sa.inner(state.popup.area, None, Some(&state.popup_scroll));
1045
1046 sa.render(
1047 state.popup.area,
1048 buf,
1049 &mut ScrollAreaState::new().v_scroll(&mut state.popup_scroll),
1050 );
1051
1052 state.popup_scroll.max_offset = widget
1053 .items
1054 .borrow()
1055 .len()
1056 .saturating_sub(inner.height as usize);
1057 state.popup_scroll.page_len = inner.height as usize;
1058
1059 state.item_areas.clear();
1060 let mut row = inner.y;
1061 let mut idx = state.popup_scroll.offset;
1062
1063 let hidden_marker;
1064 let (marker_len, marker, no_marker) = {
1065 if let Some(marker) = widget.select_marker {
1066 hidden_marker = marker.to_string();
1067 if unicode_display_width::is_double_width(marker) {
1068 (2, Some(hidden_marker.as_str()), Some(" "))
1069 } else {
1070 (1, Some(hidden_marker.as_str()), Some(" "))
1071 }
1072 } else {
1073 (0, None, None)
1074 }
1075 };
1076 loop {
1077 if row >= inner.bottom() {
1078 break;
1079 }
1080
1081 let item_area = Rect::new(inner.x, row, inner.width, 1);
1082 state.item_areas.push(item_area);
1083
1084 let (style, marker) = if state.core.selected() == Some(idx) {
1085 (popup_style.patch(select_style), marker)
1086 } else {
1087 (popup_style, no_marker)
1088 };
1089 buf.set_style(item_area, style);
1090
1091 if let Some(item) = widget.items.borrow().get(idx) {
1092 if let Some(marker) = &marker {
1093 marker.render(
1094 Rect::new(item_area.x, item_area.y, marker_len, item_area.height),
1095 buf,
1096 );
1097 item.render(
1098 Rect::new(
1099 item_area.x + marker_len,
1100 item_area.y,
1101 item_area.width.saturating_sub(marker_len),
1102 item_area.height,
1103 ),
1104 buf,
1105 );
1106 } else {
1107 item.render(item_area, buf);
1108 }
1109 } else {
1110 }
1112
1113 row += 1;
1114 idx += 1;
1115 }
1116 } else {
1117 state.popup.clear_areas();
1118 }
1119}
1120
1121impl<T> Clone for ChoiceState<T>
1122where
1123 T: PartialEq + Clone + Default,
1124{
1125 fn clone(&self) -> Self {
1126 let popup = self.popup.clone();
1127 let behave_focus = Rc::new(Cell::new(self.behave_focus.get()));
1128 let focus = focus_cb(
1129 popup.active.clone(),
1130 behave_focus.clone(),
1131 self.focus.new_instance(),
1132 );
1133
1134 Self {
1135 area: self.area,
1136 inner: self.inner,
1137 nav_char: self.nav_char.clone(),
1138 item_area: self.item_area,
1139 button_area: self.button_area,
1140 item_areas: self.item_areas.clone(),
1141 core: self.core.clone(),
1142 popup,
1143 popup_scroll: self.popup_scroll.clone(),
1144 behave_focus,
1145 behave_select: self.behave_select,
1146 behave_close: self.behave_close,
1147 focus,
1148 mouse: Default::default(),
1149 non_exhaustive: NonExhaustive,
1150 }
1151 }
1152}
1153
1154impl<T> Default for ChoiceState<T>
1155where
1156 T: PartialEq + Clone + Default,
1157{
1158 fn default() -> Self {
1159 let popup = PopupCoreState::default();
1160 let behave_focus = Rc::new(Cell::new(ChoiceFocus::default()));
1161 let focus = focus_cb(
1162 popup.active.clone(),
1163 behave_focus.clone(),
1164 Default::default(),
1165 );
1166
1167 Self {
1168 area: Default::default(),
1169 inner: Default::default(),
1170 nav_char: Default::default(),
1171 item_area: Default::default(),
1172 button_area: Default::default(),
1173 item_areas: Default::default(),
1174 core: Default::default(),
1175 popup,
1176 popup_scroll: Default::default(),
1177 behave_focus,
1178 behave_select: Default::default(),
1179 behave_close: Default::default(),
1180 focus,
1181 mouse: Default::default(),
1182 non_exhaustive: NonExhaustive,
1183 }
1184 }
1185}
1186
1187fn focus_cb(
1188 active: Rc<Cell<bool>>,
1189 behave_focus: Rc<Cell<ChoiceFocus>>,
1190 flag: FocusFlag,
1191) -> FocusFlag {
1192 let active_clone = active.clone();
1193 flag.on_lost(move || {
1194 if active_clone.get() {
1195 active_clone.set(false);
1196 }
1197 });
1198 flag.on_gained(move || {
1199 if !active.get() {
1200 if behave_focus.get() == ChoiceFocus::OpenOnFocusGained {
1201 active.set(true);
1202 }
1203 }
1204 });
1205 flag
1206}
1207
1208impl<T> HasFocus for ChoiceState<T>
1209where
1210 T: PartialEq + Clone + Default,
1211{
1212 fn build(&self, builder: &mut FocusBuilder) {
1213 builder.widget_with_flags(self.focus(), self.area(), 0, self.navigable());
1214 builder.widget_with_flags(self.focus(), self.popup.area, 1, Navigation::Mouse);
1215 }
1216
1217 fn focus(&self) -> FocusFlag {
1218 self.focus.clone()
1219 }
1220
1221 fn area(&self) -> Rect {
1222 self.area
1223 }
1224}
1225
1226impl<T> HasScreenCursor for ChoiceState<T>
1227where
1228 T: PartialEq + Clone + Default,
1229{
1230 fn screen_cursor(&self) -> Option<(u16, u16)> {
1231 None
1232 }
1233}
1234
1235impl<T> RelocatableState for ChoiceState<T>
1236where
1237 T: PartialEq + Clone + Default,
1238{
1239 fn relocate(&mut self, _shift: (i16, i16), _clip: Rect) {
1240 }
1242
1243 fn relocate_popup(&mut self, shift: (i16, i16), clip: Rect) {
1244 self.area.relocate(shift, clip);
1245 self.inner.relocate(shift, clip);
1246 self.item_area.relocate(shift, clip);
1247 self.button_area.relocate(shift, clip);
1248 self.item_areas.relocate(shift, clip);
1249 self.popup.relocate_popup(shift, clip);
1250 self.popup_scroll.relocate(shift, clip);
1251 }
1252}
1253
1254impl<T> ChoiceState<T>
1255where
1256 T: PartialEq + Clone + Default,
1257{
1258 pub fn new() -> Self {
1259 Self::default()
1260 }
1261
1262 pub fn named(name: &str) -> Self {
1263 let mut z = Self::default();
1264 z.focus = z.focus.with_name(name);
1265 z
1266 }
1267
1268 pub fn is_popup_active(&self) -> bool {
1270 self.popup.is_active()
1271 }
1272
1273 pub fn flip_popup_active(&mut self) {
1275 self.popup.flip_active();
1276 }
1277
1278 pub fn set_popup_active(&mut self, active: bool) -> bool {
1280 self.popup.set_active(active)
1281 }
1282
1283 pub fn set_default_value(&mut self, default_value: Option<T>) {
1292 self.core.set_default_value(default_value);
1293 }
1294
1295 pub fn default_value(&self) -> &Option<T> {
1297 self.core.default_value()
1298 }
1299
1300 pub fn set_value(&mut self, value: T) -> bool {
1311 self.core.set_value(value)
1312 }
1313
1314 pub fn value(&self) -> T {
1316 self.core.value()
1317 }
1318
1319 pub fn clear(&mut self) -> bool {
1321 self.core.clear()
1322 }
1323
1324 pub fn select(&mut self, select: usize) -> bool {
1336 self.core.set_selected(select)
1337 }
1338
1339 pub fn selected(&self) -> Option<usize> {
1344 self.core.selected()
1345 }
1346
1347 pub fn is_empty(&self) -> bool {
1349 self.core.values().is_empty()
1350 }
1351
1352 pub fn len(&self) -> usize {
1354 self.core.values().len()
1355 }
1356
1357 pub fn clear_offset(&mut self) {
1359 self.popup_scroll.set_offset(0);
1360 }
1361
1362 pub fn set_offset(&mut self, offset: usize) -> bool {
1364 self.popup_scroll.set_offset(offset)
1365 }
1366
1367 pub fn offset(&self) -> usize {
1369 self.popup_scroll.offset()
1370 }
1371
1372 pub fn max_offset(&self) -> usize {
1374 self.popup_scroll.max_offset()
1375 }
1376
1377 pub fn page_len(&self) -> usize {
1379 self.popup_scroll.page_len()
1380 }
1381
1382 pub fn scroll_by(&self) -> usize {
1384 self.popup_scroll.scroll_by()
1385 }
1386
1387 pub fn scroll_to_selected(&mut self) -> bool {
1389 if let Some(selected) = self.core.selected() {
1390 self.popup_scroll.scroll_to_pos(selected)
1391 } else {
1392 false
1393 }
1394 }
1395}
1396
1397impl<T> ChoiceState<T>
1398where
1399 T: PartialEq + Clone + Default,
1400{
1401 pub fn select_by_char(&mut self, c: char) -> bool {
1403 if self.nav_char.is_empty() {
1404 return false;
1405 }
1406 let c = c.to_lowercase().collect::<Vec<_>>();
1407
1408 let (mut idx, end_loop) = if let Some(idx) = self.core.selected() {
1409 (idx + 1, idx)
1410 } else {
1411 if self.nav_char[0] == c {
1412 self.core.set_selected(0);
1413 return true;
1414 } else {
1415 (1, 0)
1416 }
1417 };
1418 loop {
1419 if idx >= self.nav_char.len() {
1420 idx = 0;
1421 }
1422 if idx == end_loop {
1423 break;
1424 }
1425
1426 if self.nav_char[idx] == c {
1427 self.core.set_selected(idx);
1428 return true;
1429 }
1430
1431 idx += 1;
1432 }
1433 false
1434 }
1435
1436 pub fn reverse_select_by_char(&mut self, c: char) -> bool {
1438 if self.nav_char.is_empty() {
1439 return false;
1440 }
1441 let c = c.to_lowercase().collect::<Vec<_>>();
1442
1443 let (mut idx, end_loop) = if let Some(idx) = self.core.selected() {
1444 if idx == 0 {
1445 (self.nav_char.len() - 1, 0)
1446 } else {
1447 (idx - 1, idx)
1448 }
1449 } else {
1450 if self.nav_char.last() == Some(&c) {
1451 self.core.set_selected(self.nav_char.len() - 1);
1452 return true;
1453 } else {
1454 (self.nav_char.len() - 1, 0)
1455 }
1456 };
1457 loop {
1458 if self.nav_char[idx] == c {
1459 self.core.set_selected(idx);
1460 return true;
1461 }
1462
1463 if idx == end_loop {
1464 break;
1465 }
1466
1467 if idx == 0 {
1468 idx = self.nav_char.len() - 1;
1469 } else {
1470 idx -= 1;
1471 }
1472 }
1473 false
1474 }
1475
1476 pub fn move_to(&mut self, n: usize) -> ChoiceOutcome {
1478 let old_selected = self.selected();
1479 let r1 = self.popup.set_active(true);
1480 let r2 = self.select(n);
1481 let r3 = self.scroll_to_selected();
1482 if old_selected != self.selected() {
1483 ChoiceOutcome::Value
1484 } else if r1 || r2 || r3 {
1485 ChoiceOutcome::Changed
1486 } else {
1487 ChoiceOutcome::Continue
1488 }
1489 }
1490
1491 pub fn move_down(&mut self, n: usize) -> ChoiceOutcome {
1493 if self.core.is_empty() {
1494 return ChoiceOutcome::Continue;
1495 }
1496
1497 let old_selected = self.selected();
1498 let r1 = self.popup.set_active(true);
1499 let idx = if let Some(idx) = self.core.selected() {
1500 idx + n
1501 } else {
1502 n.saturating_sub(1)
1503 };
1504 let idx = idx.clamp(0, self.len() - 1);
1505 let r2 = self.core.set_selected(idx);
1506 let r3 = self.scroll_to_selected();
1507
1508 if old_selected != self.selected() {
1509 ChoiceOutcome::Value
1510 } else if r1 || r2 || r3 {
1511 ChoiceOutcome::Changed
1512 } else {
1513 ChoiceOutcome::Continue
1514 }
1515 }
1516
1517 pub fn move_up(&mut self, n: usize) -> ChoiceOutcome {
1519 if self.core.is_empty() {
1520 return ChoiceOutcome::Continue;
1521 }
1522
1523 let old_selected = self.selected();
1524 let r1 = self.popup.set_active(true);
1525 let idx = if let Some(idx) = self.core.selected() {
1526 idx.saturating_sub(n)
1527 } else {
1528 0
1529 };
1530 let idx = idx.clamp(0, self.len() - 1);
1531 let r2 = self.core.set_selected(idx);
1532 let r3 = self.scroll_to_selected();
1533
1534 if old_selected != self.selected() {
1535 ChoiceOutcome::Value
1536 } else if r1 || r2 || r3 {
1537 ChoiceOutcome::Changed
1538 } else {
1539 ChoiceOutcome::Continue
1540 }
1541 }
1542}
1543
1544impl<T: PartialEq + Clone + Default> HandleEvent<crossterm::event::Event, Popup, ChoiceOutcome>
1545 for ChoiceState<T>
1546{
1547 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Popup) -> ChoiceOutcome {
1548 let r = if self.is_focused() {
1549 match event {
1550 ct_event!(key press ' ') | ct_event!(keycode press Enter) => {
1551 self.flip_popup_active();
1552 ChoiceOutcome::Changed
1553 }
1554 ct_event!(keycode press Esc) => {
1555 if self.set_popup_active(false) {
1556 ChoiceOutcome::Changed
1557 } else {
1558 ChoiceOutcome::Continue
1559 }
1560 }
1561 ct_event!(key press c) => {
1562 if self.select_by_char(*c) {
1563 self.scroll_to_selected();
1564 ChoiceOutcome::Value
1565 } else {
1566 ChoiceOutcome::Continue
1567 }
1568 }
1569 ct_event!(key press SHIFT-c) => {
1570 if self.reverse_select_by_char(*c) {
1571 self.scroll_to_selected();
1572 ChoiceOutcome::Value
1573 } else {
1574 ChoiceOutcome::Continue
1575 }
1576 }
1577 ct_event!(keycode press Delete) | ct_event!(keycode press Backspace) => {
1578 if self.clear() {
1579 ChoiceOutcome::Value
1580 } else {
1581 ChoiceOutcome::Continue
1582 }
1583 }
1584 ct_event!(keycode press Down) => self.move_down(1),
1585 ct_event!(keycode press Up) => self.move_up(1),
1586 ct_event!(keycode press PageUp) => self.move_up(self.page_len()),
1587 ct_event!(keycode press PageDown) => self.move_down(self.page_len()),
1588 ct_event!(keycode press Home) => self.move_to(0),
1589 ct_event!(keycode press End) => self.move_to(self.len().saturating_sub(1)),
1590 _ => ChoiceOutcome::Continue,
1591 }
1592 } else {
1593 ChoiceOutcome::Continue
1594 };
1595
1596 if !r.is_consumed() {
1597 self.handle(event, MouseOnly)
1598 } else {
1599 r
1600 }
1601 }
1602}
1603
1604impl<T: PartialEq + Clone + Default> HandleEvent<crossterm::event::Event, MouseOnly, ChoiceOutcome>
1605 for ChoiceState<T>
1606{
1607 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> ChoiceOutcome {
1608 let r0 = handle_mouse(self, event);
1609 let r1 = handle_select(self, event);
1610 let r2 = handle_close(self, event);
1611 let mut r = max(r0, max(r1, r2));
1612
1613 r = r.or_else(|| mouse_trap(event, self.popup.area).into());
1614
1615 r
1616 }
1617}
1618
1619fn handle_mouse<T: PartialEq + Clone + Default>(
1620 state: &mut ChoiceState<T>,
1621 event: &crossterm::event::Event,
1622) -> ChoiceOutcome {
1623 match event {
1624 ct_event!(mouse down Left for x,y)
1625 if state.item_area.contains((*x, *y).into())
1626 || state.button_area.contains((*x, *y).into()) =>
1627 {
1628 if !state.gained_focus() {
1629 state.flip_popup_active();
1630 ChoiceOutcome::Changed
1631 } else {
1632 ChoiceOutcome::Continue
1635 }
1636 }
1637 ct_event!(mouse down Left for x,y)
1638 | ct_event!(mouse down Right for x,y)
1639 | ct_event!(mouse down Middle for x,y)
1640 if !state.item_area.contains((*x, *y).into())
1641 && !state.button_area.contains((*x, *y).into()) =>
1642 {
1643 match state.popup.handle(event, Popup) {
1644 PopupOutcome::Hide => {
1645 state.set_popup_active(false);
1646 ChoiceOutcome::Changed
1647 }
1648 r => r.into(),
1649 }
1650 }
1651 _ => ChoiceOutcome::Continue,
1652 }
1653}
1654
1655fn handle_select<T: PartialEq + Clone + Default>(
1656 state: &mut ChoiceState<T>,
1657 event: &crossterm::event::Event,
1658) -> ChoiceOutcome {
1659 match state.behave_select {
1660 ChoiceSelect::MouseScroll => {
1661 let mut sas = ScrollAreaState::new()
1662 .area(state.popup.area)
1663 .v_scroll(&mut state.popup_scroll);
1664 let mut r = match sas.handle(event, MouseOnly) {
1665 ScrollOutcome::Up(n) => state.move_up(n),
1666 ScrollOutcome::Down(n) => state.move_down(n),
1667 ScrollOutcome::VPos(n) => state.move_to(n),
1668 _ => ChoiceOutcome::Continue,
1669 };
1670
1671 r = r.or_else(|| match event {
1672 ct_event!(mouse down Left for x,y)
1673 if state.popup.area.contains((*x, *y).into()) =>
1674 {
1675 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1676 state.move_to(state.offset() + n)
1677 } else {
1678 ChoiceOutcome::Unchanged
1679 }
1680 }
1681 ct_event!(mouse drag Left for x,y)
1682 if state.popup.area.contains((*x, *y).into()) =>
1683 {
1684 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1685 state.move_to(state.offset() + n)
1686 } else {
1687 ChoiceOutcome::Unchanged
1688 }
1689 }
1690 _ => ChoiceOutcome::Continue,
1691 });
1692 r
1693 }
1694 ChoiceSelect::MouseMove => {
1695 let mut r = if let Some(selected) = state.core.selected() {
1697 let rel_sel = selected.saturating_sub(state.offset());
1698 let mut sas = ScrollAreaState::new()
1699 .area(state.popup.area)
1700 .v_scroll(&mut state.popup_scroll);
1701 match sas.handle(event, MouseOnly) {
1702 ScrollOutcome::Up(n) => {
1703 state.popup_scroll.scroll_up(n);
1704 if state.select(state.offset() + rel_sel) {
1705 ChoiceOutcome::Value
1706 } else {
1707 ChoiceOutcome::Unchanged
1708 }
1709 }
1710 ScrollOutcome::Down(n) => {
1711 state.popup_scroll.scroll_down(n);
1712 if state.select(state.offset() + rel_sel) {
1713 ChoiceOutcome::Value
1714 } else {
1715 ChoiceOutcome::Unchanged
1716 }
1717 }
1718 ScrollOutcome::VPos(n) => {
1719 if state.popup_scroll.set_offset(n) {
1720 ChoiceOutcome::Value
1721 } else {
1722 ChoiceOutcome::Unchanged
1723 }
1724 }
1725 _ => ChoiceOutcome::Continue,
1726 }
1727 } else {
1728 ChoiceOutcome::Continue
1729 };
1730
1731 r = r.or_else(|| match event {
1732 ct_event!(mouse moved for x,y) if state.popup.area.contains((*x, *y).into()) => {
1733 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1734 state.move_to(state.offset() + n)
1735 } else {
1736 ChoiceOutcome::Unchanged
1737 }
1738 }
1739 _ => ChoiceOutcome::Continue,
1740 });
1741 r
1742 }
1743 ChoiceSelect::MouseClick => {
1744 let mut sas = ScrollAreaState::new()
1746 .area(state.popup.area)
1747 .v_scroll(&mut state.popup_scroll);
1748 let mut r = match sas.handle(event, MouseOnly) {
1749 ScrollOutcome::Up(n) => {
1750 if state.popup_scroll.scroll_up(n) {
1751 ChoiceOutcome::Changed
1752 } else {
1753 ChoiceOutcome::Unchanged
1754 }
1755 }
1756 ScrollOutcome::Down(n) => {
1757 if state.popup_scroll.scroll_down(n) {
1758 ChoiceOutcome::Changed
1759 } else {
1760 ChoiceOutcome::Unchanged
1761 }
1762 }
1763 ScrollOutcome::VPos(n) => {
1764 if state.popup_scroll.set_offset(n) {
1765 ChoiceOutcome::Changed
1766 } else {
1767 ChoiceOutcome::Unchanged
1768 }
1769 }
1770 _ => ChoiceOutcome::Continue,
1771 };
1772
1773 r = r.or_else(|| match event {
1774 ct_event!(mouse down Left for x,y)
1775 if state.popup.area.contains((*x, *y).into()) =>
1776 {
1777 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1778 state.move_to(state.offset() + n)
1779 } else {
1780 ChoiceOutcome::Unchanged
1781 }
1782 }
1783 ct_event!(mouse drag Left for x,y)
1784 if state.popup.area.contains((*x, *y).into()) =>
1785 {
1786 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1787 state.move_to(state.offset() + n)
1788 } else {
1789 ChoiceOutcome::Unchanged
1790 }
1791 }
1792 _ => ChoiceOutcome::Continue,
1793 });
1794 r
1795 }
1796 }
1797}
1798
1799fn handle_close<T: PartialEq + Clone + Default>(
1800 state: &mut ChoiceState<T>,
1801 event: &crossterm::event::Event,
1802) -> ChoiceOutcome {
1803 match state.behave_close {
1804 ChoiceClose::SingleClick => match event {
1805 ct_event!(mouse down Left for x,y) if state.popup.area.contains((*x, *y).into()) => {
1806 if let Some(n) = item_at(&state.item_areas, *x, *y) {
1807 let r = state.move_to(state.offset() + n);
1808 let s = if state.set_popup_active(false) {
1809 ChoiceOutcome::Changed
1810 } else {
1811 ChoiceOutcome::Unchanged
1812 };
1813 max(r, s)
1814 } else {
1815 ChoiceOutcome::Unchanged
1816 }
1817 }
1818 _ => ChoiceOutcome::Continue,
1819 },
1820 ChoiceClose::DoubleClick => match event {
1821 ct_event!(mouse any for m) if state.mouse.doubleclick(state.popup.area, m) => {
1822 if let Some(n) = item_at(&state.item_areas, m.column, m.row) {
1823 let r = state.move_to(state.offset() + n);
1824 let s = if state.set_popup_active(false) {
1825 ChoiceOutcome::Changed
1826 } else {
1827 ChoiceOutcome::Unchanged
1828 };
1829 max(r, s)
1830 } else {
1831 ChoiceOutcome::Unchanged
1832 }
1833 }
1834 _ => ChoiceOutcome::Continue,
1835 },
1836 }
1837}
1838
1839pub fn handle_events<T: PartialEq + Clone + Default>(
1843 state: &mut ChoiceState<T>,
1844 focus: bool,
1845 event: &crossterm::event::Event,
1846) -> ChoiceOutcome {
1847 state.focus.set(focus);
1848 HandleEvent::handle(state, event, Popup)
1849}
1850
1851pub fn handle_mouse_events<T: PartialEq + Clone + Default>(
1853 state: &mut ChoiceState<T>,
1854 event: &crossterm::event::Event,
1855) -> ChoiceOutcome {
1856 HandleEvent::handle(state, event, MouseOnly)
1857}