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