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