rat_widget/
choice.rs

1//!
2//! Choice/Select widget.
3//!
4//! ```rust no_run
5//! use rat_popup::Placement;
6//! use rat_scrolled::Scroll;
7//! use rat_widget::choice::{Choice, ChoiceState};
8//! # use ratatui::prelude::*;
9//! # use ratatui::widgets::Block;
10//! # let mut buf = Buffer::default();
11//! # let mut cstate = ChoiceState::default();
12//! # let mut max_bounds: Rect = Rect::default();
13//!
14//! let (widget, popup) = Choice::new()
15//!         .item(1, "Carrots")
16//!         .item(2, "Potatoes")
17//!         .item(3, "Onions")
18//!         .item(4, "Peas")
19//!         .item(5, "Beans")
20//!         .item(6, "Tomatoes")
21//!         .popup_block(Block::bordered())
22//!         .popup_placement(Placement::AboveOrBelow)
23//!         .popup_boundary(max_bounds)
24//!         .into_widgets();
25//!  widget.render(Rect::new(3,3,15,1), &mut buf, &mut cstate);
26//!
27//!  // ... render other widgets
28//!
29//!  popup.render(Rect::new(3,3,15,1), &mut buf, &mut cstate);
30//!
31//! ```
32//!
33use crate::_private::NonExhaustive;
34use crate::choice::core::ChoiceCore;
35use crate::event::ChoiceOutcome;
36use crate::util::{block_padding, block_size, revert_style};
37use rat_event::util::{MouseFlags, item_at, mouse_trap};
38use rat_event::{ConsumedEvent, HandleEvent, MouseOnly, Popup, ct_event};
39use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
40use rat_popup::event::PopupOutcome;
41use rat_popup::{Placement, PopupCore, PopupCoreState, PopupStyle, fallback_popup_style};
42use rat_reloc::{RelocatableState, relocate_area, relocate_areas};
43use rat_scrolled::event::ScrollOutcome;
44use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState, ScrollStyle};
45use ratatui::buffer::Buffer;
46use ratatui::layout::{Alignment, Rect};
47use ratatui::prelude::BlockExt;
48use ratatui::style::Style;
49use ratatui::text::{Line, Span};
50use ratatui::widgets::{Block, StatefulWidget, Widget};
51use std::cell::{Cell, RefCell};
52use std::cmp::{max, min};
53use std::marker::PhantomData;
54use std::rc::Rc;
55
56/// Enum controling the behaviour of the Choice.
57#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
58pub enum ChoiceFocus {
59    /// Opening the choice popup is a separate action.
60    #[default]
61    SeparateOpen,
62    /// Open the choice popup on focus gained.
63    OpenOnFocusGained,
64}
65
66/// Enum controling the behaviour of the Choice.
67#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
68pub enum ChoiceSelect {
69    /// Change the selection with the mouse-wheel.
70    #[default]
71    MouseScroll,
72    /// Change the selection just by moving.
73    MouseMove,
74    /// Change the selection with a click only
75    MouseClick,
76}
77
78/// Enum controlling the behaviour of the Choice.
79#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
80pub enum ChoiceClose {
81    /// Close the popup with a single click.
82    #[default]
83    SingleClick,
84    /// Close the popup with double-click.
85    DoubleClick,
86}
87
88/// Choice.
89///
90/// Select one of a list. No editable mode for this widget.
91///
92/// This doesn't render itself. [into_widgets](Choice::into_widgets)
93/// creates the base part and the popup part, which are rendered
94/// separately.
95///
96#[derive(Debug, Clone)]
97pub struct Choice<'a, T>
98where
99    T: PartialEq + Clone + Default,
100{
101    values: Rc<RefCell<Vec<T>>>,
102    default_value: Option<T>,
103    items: Rc<RefCell<Vec<Line<'a>>>>,
104
105    style: Style,
106    button_style: Option<Style>,
107    select_style: Option<Style>,
108    focus_style: Option<Style>,
109    block: Option<Block<'a>>,
110    skip_item_render: bool,
111
112    popup_alignment: Alignment,
113    popup_placement: Placement,
114    popup_len: Option<u16>,
115    popup: PopupCore,
116    popup_style: Style,
117    popup_scroll: Option<Scroll<'a>>,
118    popup_block: Option<Block<'a>>,
119
120    behave_focus: ChoiceFocus,
121    behave_select: ChoiceSelect,
122    behave_close: ChoiceClose,
123}
124
125/// Renders the main widget.
126#[derive(Debug)]
127pub struct ChoiceWidget<'a, T>
128where
129    T: PartialEq,
130{
131    values: Rc<RefCell<Vec<T>>>,
132    default_value: Option<T>,
133    items: Rc<RefCell<Vec<Line<'a>>>>,
134
135    style: Style,
136    button_style: Option<Style>,
137    focus_style: Option<Style>,
138    block: Option<Block<'a>>,
139    skip_item_render: bool,
140    len: Option<u16>,
141
142    behave_focus: ChoiceFocus,
143    behave_select: ChoiceSelect,
144    behave_close: ChoiceClose,
145}
146
147/// Renders the popup. This is called after the rest
148/// of the area is rendered and overwrites to display itself.
149#[derive(Debug)]
150pub struct ChoicePopup<'a, T>
151where
152    T: PartialEq,
153{
154    items: Rc<RefCell<Vec<Line<'a>>>>,
155
156    style: Style,
157    select_style: Option<Style>,
158
159    popup_alignment: Alignment,
160    popup_placement: Placement,
161    popup_len: Option<u16>,
162    popup: PopupCore,
163    popup_style: Style,
164    popup_scroll: Option<Scroll<'a>>,
165    popup_block: Option<Block<'a>>,
166
167    _phantom: PhantomData<T>,
168}
169
170/// Combined style.
171#[derive(Debug, Clone)]
172pub struct ChoiceStyle {
173    pub style: Style,
174    pub button: Option<Style>,
175    pub select: Option<Style>,
176    pub focus: Option<Style>,
177    pub block: Option<Block<'static>>,
178
179    pub popup: PopupStyle,
180    pub popup_style: Option<Style>,
181    pub popup_border: Option<Style>,
182    pub popup_scroll: Option<ScrollStyle>,
183    pub popup_block: Option<Block<'static>>,
184    pub popup_len: Option<u16>,
185
186    pub behave_focus: Option<ChoiceFocus>,
187    pub behave_select: Option<ChoiceSelect>,
188    pub behave_close: Option<ChoiceClose>,
189
190    pub non_exhaustive: NonExhaustive,
191}
192
193/// State.
194#[derive(Debug)]
195pub struct ChoiceState<T = usize>
196where
197    T: PartialEq + Clone + Default,
198{
199    /// Total area.
200    /// __read only__. renewed with each render.
201    pub area: Rect,
202    /// First char of each item for navigation.
203    /// __read only__. renewed with each render.
204    pub nav_char: Vec<Vec<char>>,
205    /// Item area in the main widget.
206    /// __read only__. renewed with each render.
207    pub item_area: Rect,
208    /// Button area in the main widget.
209    /// __read only__. renewed with each render.
210    pub button_area: Rect,
211    /// Visible items in the popup.
212    /// __read only__. renewed with each render.
213    pub item_areas: Vec<Rect>,
214    /// Core
215    pub core: ChoiceCore<T>,
216    /// Popup state.
217    pub popup: PopupCoreState,
218    /// Popup scroll state.
219    pub popup_scroll: ScrollState,
220    /// Behaviour for opening the choice popup.
221    pub behave_focus: Rc<Cell<ChoiceFocus>>,
222    /// Behaviour for selecting from the choice popup.
223    /// __read only__ renewed with each render.
224    pub behave_select: ChoiceSelect,
225    /// Behaviour for closing the choice popup.
226    /// __read only__ renewed with each render.
227    pub behave_close: ChoiceClose,
228
229    /// Focus flag.
230    /// __read+write__
231    pub focus: FocusFlag,
232    /// Mouse util.
233    pub mouse: MouseFlags,
234
235    pub non_exhaustive: NonExhaustive,
236}
237
238pub(crate) mod event {
239    use rat_event::{ConsumedEvent, Outcome};
240    use rat_popup::event::PopupOutcome;
241
242    /// Result value for event-handling.
243    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
244    pub enum ChoiceOutcome {
245        /// The given event was not handled at all.
246        Continue,
247        /// The event was handled, no repaint necessary.
248        Unchanged,
249        /// The event was handled, repaint necessary.
250        Changed,
251        /// An item has been selected.
252        Value,
253    }
254
255    impl ConsumedEvent for ChoiceOutcome {
256        fn is_consumed(&self) -> bool {
257            *self != ChoiceOutcome::Continue
258        }
259    }
260
261    impl From<Outcome> for ChoiceOutcome {
262        fn from(value: Outcome) -> Self {
263            match value {
264                Outcome::Continue => ChoiceOutcome::Continue,
265                Outcome::Unchanged => ChoiceOutcome::Unchanged,
266                Outcome::Changed => ChoiceOutcome::Changed,
267            }
268        }
269    }
270
271    impl From<ChoiceOutcome> for Outcome {
272        fn from(value: ChoiceOutcome) -> Self {
273            match value {
274                ChoiceOutcome::Continue => Outcome::Continue,
275                ChoiceOutcome::Unchanged => Outcome::Unchanged,
276                ChoiceOutcome::Changed => Outcome::Changed,
277                ChoiceOutcome::Value => Outcome::Changed,
278            }
279        }
280    }
281
282    impl From<PopupOutcome> for ChoiceOutcome {
283        fn from(value: PopupOutcome) -> Self {
284            match value {
285                PopupOutcome::Continue => ChoiceOutcome::Continue,
286                PopupOutcome::Unchanged => ChoiceOutcome::Unchanged,
287                PopupOutcome::Changed => ChoiceOutcome::Changed,
288                PopupOutcome::Hide => ChoiceOutcome::Changed,
289            }
290        }
291    }
292}
293
294pub mod core {
295    #[derive(Debug, Default, Clone)]
296    pub struct ChoiceCore<T>
297    where
298        T: PartialEq + Clone + Default,
299    {
300        /// Values.
301        /// __read only__. renewed for each render.
302        values: Vec<T>,
303        /// Can return to default with a user interaction.
304        default_value: Option<T>,
305        /// Selected value, or a value set with set_value().
306        /// There may be a value and still no selected index,
307        /// if the values-vec is empty, or if the value is not in
308        /// values.
309        value: T,
310        /// Index of value.
311        selected: Option<usize>,
312    }
313
314    impl<T> ChoiceCore<T>
315    where
316        T: PartialEq + Clone + Default,
317    {
318        pub fn set_values(&mut self, values: Vec<T>) {
319            self.values = values;
320            // ensure integrity
321            if self.values.is_empty() {
322                self.selected = None;
323            } else {
324                self.selected = self.values.iter().position(|v| *v == self.value);
325            }
326        }
327
328        /// List of values.
329        pub fn values(&self) -> &[T] {
330            &self.values
331        }
332
333        /// Set a default-value other than T::default()
334        ///
335        /// The starting value will still be T::default()
336        /// after this. You must call clear() to use this
337        /// default.
338        pub fn set_default_value(&mut self, default_value: Option<T>) {
339            self.default_value = default_value.clone();
340        }
341
342        /// A default value.
343        pub fn default_value(&self) -> &Option<T> {
344            &self.default_value
345        }
346
347        /// Selected item index.
348        ///
349        /// This may be None and there may still be a valid value.
350        /// This can happen if a value is not in the value-list,
351        /// or if the value-list is empty before the first render.
352        ///
353        /// Use of value() is preferred.
354        pub fn selected(&self) -> Option<usize> {
355            self.selected
356        }
357
358        /// Set the selected item by index.
359        ///
360        /// If the select-idx doesn't match the list of values,
361        /// selected will be None. This may happen before the
362        /// first render, while values is still empty.
363        ///
364        /// Use of set_value() is preferred.
365        pub fn set_selected(&mut self, select: usize) -> bool {
366            let old_sel = self.selected;
367            if self.values.is_empty() {
368                self.selected = None;
369            } else {
370                if let Some(value) = self.values.get(select) {
371                    self.value = value.clone();
372                    self.selected = Some(select);
373                } else {
374                    // don't change value
375                    self.selected = None;
376                }
377            }
378            old_sel != self.selected
379        }
380
381        /// Set the value for this Choice.
382        ///
383        /// The value will be retained even if it is not in
384        /// the value-list. This can happen before the first
385        /// render while the value-list is still empty.
386        /// Or because a divergent value is set here.
387        ///
388        /// The starting value will be T::default().
389        pub fn set_value(&mut self, value: T) -> bool {
390            let old_value = self.value.clone();
391
392            self.value = value;
393            self.selected = self.values.iter().position(|v| *v == self.value);
394
395            old_value != self.value
396        }
397
398        /// Return the selected value, or any value set with set_value()
399        pub fn value(&self) -> T {
400            self.value.clone()
401        }
402
403        pub fn is_empty(&self) -> bool {
404            self.values.is_empty()
405        }
406
407        pub fn clear(&mut self) -> bool {
408            let old_selected = self.selected;
409            let old_value = self.value.clone();
410
411            if let Some(default_value) = &self.default_value {
412                self.value = default_value.clone();
413            }
414
415            self.selected = self.values.iter().position(|v| *v == self.value);
416
417            old_selected != self.selected || old_value != self.value
418        }
419    }
420}
421
422impl Default for ChoiceStyle {
423    fn default() -> Self {
424        Self {
425            style: Default::default(),
426            button: Default::default(),
427            select: Default::default(),
428            focus: Default::default(),
429            block: Default::default(),
430            popup: Default::default(),
431            popup_style: Default::default(),
432            popup_border: Default::default(),
433            popup_scroll: Default::default(),
434            popup_block: Default::default(),
435            popup_len: Default::default(),
436            behave_focus: Default::default(),
437            behave_select: Default::default(),
438            behave_close: Default::default(),
439            non_exhaustive: NonExhaustive,
440        }
441    }
442}
443
444impl<T> Default for Choice<'_, T>
445where
446    T: PartialEq + Clone + Default,
447{
448    fn default() -> Self {
449        Self {
450            values: Default::default(),
451            default_value: Default::default(),
452            items: Default::default(),
453            style: Default::default(),
454            button_style: Default::default(),
455            select_style: Default::default(),
456            focus_style: Default::default(),
457            block: Default::default(),
458            popup_len: Default::default(),
459            popup_alignment: Alignment::Left,
460            popup_placement: Placement::BelowOrAbove,
461            popup: Default::default(),
462            popup_style: Default::default(),
463            popup_scroll: Default::default(),
464            popup_block: Default::default(),
465            behave_focus: Default::default(),
466            behave_select: Default::default(),
467            behave_close: Default::default(),
468            skip_item_render: Default::default(),
469        }
470    }
471}
472
473impl<'a> Choice<'a, usize> {
474    /// Add items with auto-generated values.
475    #[inline]
476    pub fn auto_items<V: Into<Line<'a>>>(self, items: impl IntoIterator<Item = V>) -> Self {
477        {
478            let mut values = self.values.borrow_mut();
479            let mut itemz = self.items.borrow_mut();
480
481            values.clear();
482            itemz.clear();
483
484            for (k, v) in items.into_iter().enumerate() {
485                values.push(k);
486                itemz.push(v.into());
487            }
488        }
489
490        self
491    }
492
493    /// Add an item with an auto generated value.
494    pub fn auto_item(self, item: impl Into<Line<'a>>) -> Self {
495        let idx = self.values.borrow().len();
496        self.values.borrow_mut().push(idx);
497        self.items.borrow_mut().push(item.into());
498        self
499    }
500}
501
502impl<'a, T> Choice<'a, T>
503where
504    T: PartialEq + Clone + Default,
505{
506    pub fn new() -> Self {
507        Self::default()
508    }
509
510    /// Button text.
511    #[inline]
512    pub fn items<V: Into<Line<'a>>>(self, items: impl IntoIterator<Item = (T, V)>) -> Self {
513        {
514            let mut values = self.values.borrow_mut();
515            let mut itemz = self.items.borrow_mut();
516
517            values.clear();
518            itemz.clear();
519
520            for (k, v) in items.into_iter() {
521                values.push(k);
522                itemz.push(v.into());
523            }
524        }
525
526        self
527    }
528
529    /// Add an item.
530    pub fn item(self, value: T, item: impl Into<Line<'a>>) -> Self {
531        self.values.borrow_mut().push(value);
532        self.items.borrow_mut().push(item.into());
533        self
534    }
535
536    /// Can return to default with user interaction.
537    pub fn default_value(mut self, default: T) -> Self {
538        self.default_value = Some(default);
539        self
540    }
541
542    /// Combined styles.
543    pub fn styles(mut self, styles: ChoiceStyle) -> Self {
544        self.style = styles.style;
545        self.block = self.block.map(|v| v.style(self.style));
546
547        if styles.button.is_some() {
548            self.button_style = styles.button;
549        }
550        if styles.select.is_some() {
551            self.select_style = styles.select;
552        }
553        if styles.focus.is_some() {
554            self.focus_style = styles.focus;
555        }
556        if styles.block.is_some() {
557            self.block = styles.block;
558        }
559        if let Some(select) = styles.behave_focus {
560            self.behave_focus = select;
561        }
562        if let Some(select) = styles.behave_select {
563            self.behave_select = select;
564        }
565        if let Some(close) = styles.behave_close {
566            self.behave_close = close;
567        }
568        if let Some(alignment) = styles.popup.alignment {
569            self.popup_alignment = alignment;
570        }
571        if let Some(placement) = styles.popup.placement {
572            self.popup_placement = placement;
573        }
574        self.popup = self.popup.styles(styles.popup.clone());
575        if let Some(popup_style) = styles.popup_style {
576            self.popup_style = popup_style;
577        }
578        if let Some(popup_scroll) = styles.popup_scroll {
579            self.popup_scroll = self.popup_scroll.map(|v| v.styles(popup_scroll));
580        }
581        self.popup_block = self.popup_block.map(|v| v.style(self.popup_style));
582        if let Some(border_style) = styles.popup_border {
583            self.popup_block = self.popup_block.map(|v| v.border_style(border_style));
584        }
585        if styles.popup_block.is_some() {
586            self.popup_block = styles.popup_block;
587        }
588        if styles.popup_len.is_some() {
589            self.popup_len = styles.popup_len;
590        }
591
592        self
593    }
594
595    /// Base style.
596    pub fn style(mut self, style: Style) -> Self {
597        self.style = style;
598        self.block = self.block.map(|v| v.style(self.style));
599        self
600    }
601
602    /// Style for the down button.
603    pub fn button_style(mut self, style: Style) -> Self {
604        self.button_style = Some(style);
605        self
606    }
607
608    /// Selection in the list.
609    pub fn select_style(mut self, style: Style) -> Self {
610        self.select_style = Some(style);
611        self
612    }
613
614    /// Focused style.
615    pub fn focus_style(mut self, style: Style) -> Self {
616        self.focus_style = Some(style);
617        self
618    }
619
620    /// Block for the main widget.
621    pub fn block(mut self, block: Block<'a>) -> Self {
622        self.block = Some(block);
623        self.block = self.block.map(|v| v.style(self.style));
624        self
625    }
626
627    /// Skips rendering the selected item.
628    /// This is a flag for Combobox, that wants to do its own rendering instead.
629    pub fn skip_item_render(mut self, skip: bool) -> Self {
630        self.skip_item_render = skip;
631        self
632    }
633
634    /// Alignment of the popup.
635    ///
636    /// __Default__
637    /// Default is Left.
638    pub fn popup_alignment(mut self, alignment: Alignment) -> Self {
639        self.popup_alignment = alignment;
640        self
641    }
642
643    /// Placement of the popup.
644    ///
645    /// __Default__
646    /// Default is BelowOrAbove.
647    pub fn popup_placement(mut self, placement: Placement) -> Self {
648        self.popup_placement = placement;
649        self
650    }
651
652    /// Outer boundary for the popup.
653    pub fn popup_boundary(mut self, boundary: Rect) -> Self {
654        self.popup = self.popup.boundary(boundary);
655        self
656    }
657
658    /// Override the popup length.
659    ///
660    /// __Default__
661    /// Defaults to the number of items or 5.
662    pub fn popup_len(mut self, len: u16) -> Self {
663        self.popup_len = Some(len);
664        self
665    }
666
667    /// Base style for the popup.
668    pub fn popup_style(mut self, style: Style) -> Self {
669        self.popup_style = style;
670        self
671    }
672
673    /// Block for the popup.
674    pub fn popup_block(mut self, block: Block<'a>) -> Self {
675        self.popup_block = Some(block);
676        self
677    }
678
679    /// Scroll for the popup.
680    pub fn popup_scroll(mut self, scroll: Scroll<'a>) -> Self {
681        self.popup_scroll = Some(scroll);
682        self
683    }
684
685    /// Adds an extra offset to the widget area.
686    ///
687    /// This can be used to
688    /// * place the widget under the mouse cursor.
689    /// * align the widget not by the outer bounds but by
690    ///   the text content.
691    pub fn popup_offset(mut self, offset: (i16, i16)) -> Self {
692        self.popup = self.popup.offset(offset);
693        self
694    }
695
696    /// Sets only the x offset.
697    /// See [offset](Self::popup_offset)
698    pub fn popup_x_offset(mut self, offset: i16) -> Self {
699        self.popup = self.popup.x_offset(offset);
700        self
701    }
702
703    /// Sets only the y offset.
704    /// See [offset](Self::popup_offset)
705    pub fn popup_y_offset(mut self, offset: i16) -> Self {
706        self.popup = self.popup.y_offset(offset);
707        self
708    }
709
710    /// Sets the behaviour for selecting from the list.
711    pub fn behave_focus(mut self, focus: ChoiceFocus) -> Self {
712        self.behave_focus = focus;
713        self
714    }
715
716    /// Sets the behaviour for selecting from the list.
717    pub fn behave_select(mut self, select: ChoiceSelect) -> Self {
718        self.behave_select = select;
719        self
720    }
721
722    /// Sets the behaviour for closing the list.
723    pub fn behave_close(mut self, close: ChoiceClose) -> Self {
724        self.behave_close = close;
725        self
726    }
727
728    /// Inherent width.
729    pub fn width(&self) -> u16 {
730        let w = self
731            .items
732            .borrow()
733            .iter()
734            .map(|v| v.width())
735            .max()
736            .unwrap_or_default();
737
738        w as u16 + block_size(&self.block).width
739    }
740
741    /// Inherent height.
742    pub fn height(&self) -> u16 {
743        1 + block_size(&self.block).height
744    }
745
746    /// Choice itself doesn't render.
747    ///
748    /// This builds the widgets from the parameters set for Choice.
749    pub fn into_widgets(self) -> (ChoiceWidget<'a, T>, ChoicePopup<'a, T>) {
750        (
751            ChoiceWidget {
752                values: self.values,
753                default_value: self.default_value,
754                items: self.items.clone(),
755                style: self.style,
756                button_style: self.button_style,
757                focus_style: self.focus_style,
758                block: self.block,
759                skip_item_render: self.skip_item_render,
760                len: self.popup_len,
761                behave_focus: self.behave_focus,
762                behave_select: self.behave_select,
763                behave_close: self.behave_close,
764            },
765            ChoicePopup {
766                items: self.items.clone(),
767                style: self.style,
768                select_style: self.select_style,
769                popup: self.popup,
770                popup_style: self.popup_style,
771                popup_scroll: self.popup_scroll,
772                popup_block: self.popup_block,
773                popup_alignment: self.popup_alignment,
774                popup_placement: self.popup_placement,
775                popup_len: self.popup_len,
776                _phantom: Default::default(),
777            },
778        )
779    }
780}
781
782impl<'a, T> ChoiceWidget<'a, T>
783where
784    T: PartialEq + Clone + Default,
785{
786    /// Inherent width.
787    pub fn width(&self) -> u16 {
788        let w = self
789            .items
790            .borrow()
791            .iter()
792            .map(|v| v.width())
793            .max()
794            .unwrap_or_default();
795
796        w as u16 + block_size(&self.block).width
797    }
798
799    /// Inherent height.
800    pub fn height(&self) -> u16 {
801        1 + block_size(&self.block).height
802    }
803}
804
805impl<'a, T> StatefulWidget for &ChoiceWidget<'a, T>
806where
807    T: PartialEq + Clone + Default,
808{
809    type State = ChoiceState<T>;
810
811    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
812        state.core.set_values(self.values.borrow().clone());
813        if let Some(default_value) = self.default_value.clone() {
814            state.core.set_default_value(Some(default_value));
815        }
816
817        render_choice(self, area, buf, state);
818    }
819}
820
821impl<T> StatefulWidget for ChoiceWidget<'_, T>
822where
823    T: PartialEq + Clone + Default,
824{
825    type State = ChoiceState<T>;
826
827    fn render(mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
828        state.core.set_values(self.values.take());
829        if let Some(default_value) = self.default_value.take() {
830            state.core.set_default_value(Some(default_value));
831        }
832
833        render_choice(&self, area, buf, state);
834    }
835}
836
837fn render_choice<T: PartialEq + Clone + Default>(
838    widget: &ChoiceWidget<'_, T>,
839    area: Rect,
840    buf: &mut Buffer,
841    state: &mut ChoiceState<T>,
842) {
843    state.area = area;
844    state.behave_focus.set(widget.behave_focus);
845    state.behave_select = widget.behave_select;
846    state.behave_close = widget.behave_close;
847
848    if !state.popup.is_active() {
849        let len = widget
850            .len
851            .unwrap_or_else(|| min(5, widget.items.borrow().len()) as u16);
852        state.popup_scroll.max_offset = widget.items.borrow().len().saturating_sub(len as usize);
853        state.popup_scroll.page_len = len as usize;
854        if let Some(selected) = state.core.selected() {
855            state.popup_scroll.scroll_to_pos(selected);
856        }
857    }
858
859    state.nav_char.clear();
860    state.nav_char.extend(widget.items.borrow().iter().map(|v| {
861        v.spans
862            .first()
863            .and_then(|v| v.content.as_ref().chars().next())
864            .map_or(Vec::default(), |c| c.to_lowercase().collect::<Vec<_>>())
865    }));
866
867    let inner = widget.block.inner_if_some(area);
868
869    state.item_area = Rect::new(
870        inner.x,
871        inner.y,
872        inner.width.saturating_sub(3),
873        inner.height,
874    );
875    state.button_area = Rect::new(
876        inner.right().saturating_sub(min(3, inner.width)),
877        inner.y,
878        3,
879        inner.height,
880    );
881
882    let style = widget.style;
883    let focus_style = widget.focus_style.unwrap_or(revert_style(widget.style));
884
885    if state.is_focused() {
886        if let Some(block) = &widget.block {
887            block.render(area, buf);
888        } else {
889            buf.set_style(inner, style);
890        }
891        buf.set_style(inner, focus_style);
892    } else {
893        if let Some(block) = &widget.block {
894            block.render(area, buf);
895        } else {
896            buf.set_style(inner, style);
897        }
898        if let Some(button_style) = widget.button_style {
899            buf.set_style(state.button_area, button_style);
900        }
901    }
902
903    if !widget.skip_item_render {
904        if let Some(selected) = state.core.selected() {
905            if let Some(item) = widget.items.borrow().get(selected) {
906                item.render(state.item_area, buf);
907            }
908        }
909    }
910
911    let dy = if (state.button_area.height & 1) == 1 {
912        state.button_area.height / 2
913    } else {
914        state.button_area.height.saturating_sub(1) / 2
915    };
916    let bc = if state.is_popup_active() {
917        " â—† "
918    } else {
919        " â–¼ "
920    };
921    Span::from(bc).render(
922        Rect::new(state.button_area.x, state.button_area.y + dy, 3, 1),
923        buf,
924    );
925}
926
927impl<T> ChoicePopup<'_, T>
928where
929    T: PartialEq + Clone + Default,
930{
931    /// Calculate the layout for the popup before rendering.
932    /// Area is the area of the ChoiceWidget not the ChoicePopup.
933    pub fn layout(&self, area: Rect, buf: &mut Buffer, state: &mut ChoiceState<T>) -> Rect {
934        if state.popup.is_active() {
935            let len = min(
936                self.popup_len.unwrap_or(5),
937                self.items.borrow().len() as u16,
938            );
939            let padding = block_padding(&self.popup_block);
940            let popup_len = len + padding.top + padding.bottom;
941            let pop_area = Rect::new(0, 0, area.width, popup_len);
942
943            self.popup
944                .ref_constraint(
945                    self.popup_placement
946                        .into_constraint(self.popup_alignment, area),
947                )
948                .layout(pop_area, buf)
949        } else {
950            Rect::default()
951        }
952    }
953}
954
955impl<T> StatefulWidget for &ChoicePopup<'_, T>
956where
957    T: PartialEq + Clone + Default,
958{
959    type State = ChoiceState<T>;
960
961    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
962        render_popup(self, area, buf, state);
963    }
964}
965
966impl<T> StatefulWidget for ChoicePopup<'_, T>
967where
968    T: PartialEq + Clone + Default,
969{
970    type State = ChoiceState<T>;
971
972    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
973        render_popup(&self, area, buf, state);
974    }
975}
976
977fn render_popup<T: PartialEq + Clone + Default>(
978    widget: &ChoicePopup<'_, T>,
979    area: Rect,
980    buf: &mut Buffer,
981    state: &mut ChoiceState<T>,
982) {
983    if state.popup.is_active() {
984        let popup_style = widget.popup_style;
985        let select_style = widget.select_style.unwrap_or(revert_style(widget.style));
986
987        {
988            let len = min(
989                widget.popup_len.unwrap_or(5),
990                widget.items.borrow().len() as u16,
991            );
992            let padding = block_padding(&widget.popup_block);
993            let popup_len = len + padding.top + padding.bottom;
994            let pop_area = Rect::new(0, 0, area.width, popup_len);
995
996            widget
997                .popup
998                .ref_constraint(
999                    widget
1000                        .popup_placement
1001                        .into_constraint(widget.popup_alignment, area),
1002                )
1003                .render(pop_area, buf, &mut state.popup);
1004        }
1005
1006        let sa = ScrollArea::new()
1007            .style(fallback_popup_style(widget.popup_style))
1008            .block(widget.popup_block.as_ref())
1009            .v_scroll(widget.popup_scroll.as_ref());
1010
1011        let inner = sa.inner(state.popup.area, None, Some(&state.popup_scroll));
1012
1013        sa.render(
1014            state.popup.area,
1015            buf,
1016            &mut ScrollAreaState::new().v_scroll(&mut state.popup_scroll),
1017        );
1018
1019        state.popup_scroll.max_offset = widget
1020            .items
1021            .borrow()
1022            .len()
1023            .saturating_sub(inner.height as usize);
1024        state.popup_scroll.page_len = inner.height as usize;
1025
1026        state.item_areas.clear();
1027        let mut row = inner.y;
1028        let mut idx = state.popup_scroll.offset;
1029        loop {
1030            if row >= inner.bottom() {
1031                break;
1032            }
1033
1034            let item_area = Rect::new(inner.x, row, inner.width, 1);
1035            state.item_areas.push(item_area);
1036
1037            if let Some(item) = widget.items.borrow().get(idx) {
1038                let style = if state.core.selected() == Some(idx) {
1039                    popup_style.patch(select_style)
1040                } else {
1041                    popup_style
1042                };
1043
1044                buf.set_style(item_area, style);
1045                item.render(item_area, buf);
1046            } else {
1047                // noop?
1048            }
1049
1050            row += 1;
1051            idx += 1;
1052        }
1053    } else {
1054        state.popup.clear_areas();
1055    }
1056}
1057
1058impl<T> Clone for ChoiceState<T>
1059where
1060    T: PartialEq + Clone + Default,
1061{
1062    fn clone(&self) -> Self {
1063        let popup = self.popup.clone();
1064        let behave_focus = Rc::new(Cell::new(self.behave_focus.get()));
1065        let focus = focus_cb(
1066            popup.active.clone(),
1067            behave_focus.clone(),
1068            Default::default(),
1069        );
1070
1071        Self {
1072            area: self.area,
1073            nav_char: self.nav_char.clone(),
1074            item_area: self.item_area,
1075            button_area: self.button_area,
1076            item_areas: self.item_areas.clone(),
1077            core: self.core.clone(),
1078            popup,
1079            popup_scroll: self.popup_scroll.clone(),
1080            behave_focus,
1081            behave_select: self.behave_select,
1082            behave_close: self.behave_close,
1083            focus,
1084            mouse: Default::default(),
1085            non_exhaustive: NonExhaustive,
1086        }
1087    }
1088}
1089
1090impl<T> Default for ChoiceState<T>
1091where
1092    T: PartialEq + Clone + Default,
1093{
1094    fn default() -> Self {
1095        let popup = PopupCoreState::default();
1096        let behave_focus = Rc::new(Cell::new(ChoiceFocus::default()));
1097        let focus = focus_cb(
1098            popup.active.clone(),
1099            behave_focus.clone(),
1100            Default::default(),
1101        );
1102
1103        Self {
1104            area: Default::default(),
1105            nav_char: Default::default(),
1106            item_area: Default::default(),
1107            button_area: Default::default(),
1108            item_areas: Default::default(),
1109            core: Default::default(),
1110            popup,
1111            popup_scroll: Default::default(),
1112            behave_focus,
1113            behave_select: Default::default(),
1114            behave_close: Default::default(),
1115            focus,
1116            mouse: Default::default(),
1117            non_exhaustive: NonExhaustive,
1118        }
1119    }
1120}
1121
1122fn focus_cb(
1123    active: Rc<Cell<bool>>,
1124    behave_focus: Rc<Cell<ChoiceFocus>>,
1125    flag: FocusFlag,
1126) -> FocusFlag {
1127    let active_clone = active.clone();
1128    flag.on_lost(move || {
1129        if active_clone.get() {
1130            active_clone.set(false);
1131        }
1132    });
1133    flag.on_gained(move || {
1134        if !active.get() {
1135            if behave_focus.get() == ChoiceFocus::OpenOnFocusGained {
1136                active.set(true);
1137            }
1138        }
1139    });
1140    flag
1141}
1142
1143impl<T> HasFocus for ChoiceState<T>
1144where
1145    T: PartialEq + Clone + Default,
1146{
1147    fn build(&self, builder: &mut FocusBuilder) {
1148        builder.widget_with_flags(self.focus(), self.area(), 0, self.navigable());
1149        builder.widget_with_flags(self.focus(), self.popup.area, 1, Navigation::Mouse);
1150    }
1151
1152    fn focus(&self) -> FocusFlag {
1153        self.focus.clone()
1154    }
1155
1156    fn area(&self) -> Rect {
1157        self.area
1158    }
1159}
1160
1161impl<T> RelocatableState for ChoiceState<T>
1162where
1163    T: PartialEq + Clone + Default,
1164{
1165    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1166        self.area = relocate_area(self.area, shift, clip);
1167        self.item_area = relocate_area(self.item_area, shift, clip);
1168        self.button_area = relocate_area(self.button_area, shift, clip);
1169        relocate_areas(&mut self.item_areas, shift, clip);
1170        self.popup.relocate(shift, clip);
1171    }
1172}
1173
1174impl<T> ChoiceState<T>
1175where
1176    T: PartialEq + Clone + Default,
1177{
1178    pub fn new() -> Self {
1179        Self::default()
1180    }
1181
1182    pub fn named(name: &str) -> Self {
1183        let mut z = Self::default();
1184        z.focus = focus_cb(
1185            z.popup.active.clone(),
1186            z.behave_focus.clone(),
1187            FocusFlag::named(name),
1188        );
1189        z
1190    }
1191
1192    /// Popup is active?
1193    pub fn is_popup_active(&self) -> bool {
1194        self.popup.is_active()
1195    }
1196
1197    /// Flip the popup state.
1198    pub fn flip_popup_active(&mut self) {
1199        self.popup.flip_active();
1200    }
1201
1202    /// Show the popup.
1203    pub fn set_popup_active(&mut self, active: bool) -> bool {
1204        self.popup.set_active(active)
1205    }
1206
1207    /// Set a default-value other than T::default()
1208    ///
1209    /// The starting value will still be T::default()
1210    /// after this. You must call clear() to use this
1211    /// default.
1212    ///
1213    /// This default will be overridden by a default set
1214    /// on the widget.
1215    pub fn set_default_value(&mut self, default_value: Option<T>) {
1216        self.core.set_default_value(default_value);
1217    }
1218
1219    /// A default value.
1220    pub fn default_value(&self) -> &Option<T> {
1221        self.core.default_value()
1222    }
1223
1224    /// Select the given value.
1225    ///
1226    /// If the value doesn't exist in the list or the list is
1227    /// empty the value will still be set, but selected will be
1228    /// None. The list will be empty before the first render, but
1229    /// the first thing render will do is set the list of values.
1230    /// This will adjust the selected index if possible.
1231    /// It's still ok to set a value here that can not be represented.
1232    /// As long as there is no user interaction, the same value
1233    /// will be returned by value().
1234    pub fn set_value(&mut self, value: T) -> bool {
1235        self.core.set_value(value)
1236    }
1237
1238    /// Get the selected value.
1239    pub fn value(&self) -> T {
1240        self.core.value()
1241    }
1242
1243    /// Select the default value or T::default.
1244    pub fn clear(&mut self) -> bool {
1245        self.core.clear()
1246    }
1247
1248    /// Select the value at index. This will set the value
1249    /// to the given index in the value-list. If the index is
1250    /// out of bounds or the value-list is empty it will
1251    /// set selected to None and leave the value as is.
1252    /// The list is empty before the first render so this
1253    /// may not work as expected.
1254    ///
1255    /// The selected index is a best effort artefact, the main
1256    /// thing is the value itself.
1257    ///
1258    /// Use of set_value() is preferred.
1259    pub fn select(&mut self, select: usize) -> bool {
1260        self.core.set_selected(select)
1261    }
1262
1263    /// Returns the selected index or None if the
1264    /// value is not in the list or the list is empty.
1265    ///
1266    /// You can still get the value set with set_value() though.
1267    pub fn selected(&self) -> Option<usize> {
1268        self.core.selected()
1269    }
1270
1271    /// Any items?
1272    pub fn is_empty(&self) -> bool {
1273        self.core.values().is_empty()
1274    }
1275
1276    /// Number of items.
1277    pub fn len(&self) -> usize {
1278        self.core.values().len()
1279    }
1280
1281    /// Scroll offset for the item list.
1282    pub fn clear_offset(&mut self) {
1283        self.popup_scroll.set_offset(0);
1284    }
1285
1286    /// Scroll offset for the item list.
1287    pub fn set_offset(&mut self, offset: usize) -> bool {
1288        self.popup_scroll.set_offset(offset)
1289    }
1290
1291    /// Scroll offset for the item list.
1292    pub fn offset(&self) -> usize {
1293        self.popup_scroll.offset()
1294    }
1295
1296    /// Scroll offset for the item list.
1297    pub fn max_offset(&self) -> usize {
1298        self.popup_scroll.max_offset()
1299    }
1300
1301    /// Page length for the item list.
1302    pub fn page_len(&self) -> usize {
1303        self.popup_scroll.page_len()
1304    }
1305
1306    /// Scroll unit for the item list.
1307    pub fn scroll_by(&self) -> usize {
1308        self.popup_scroll.scroll_by()
1309    }
1310
1311    /// Scroll the item list to the selected value.
1312    pub fn scroll_to_selected(&mut self) -> bool {
1313        if let Some(selected) = self.core.selected() {
1314            self.popup_scroll.scroll_to_pos(selected)
1315        } else {
1316            false
1317        }
1318    }
1319}
1320
1321impl<T> ChoiceState<T>
1322where
1323    T: PartialEq + Clone + Default,
1324{
1325    /// Select by first character.
1326    pub fn select_by_char(&mut self, c: char) -> bool {
1327        if self.nav_char.is_empty() {
1328            return false;
1329        }
1330        let c = c.to_lowercase().collect::<Vec<_>>();
1331
1332        let (mut idx, end_loop) = if let Some(idx) = self.core.selected() {
1333            (idx + 1, idx)
1334        } else {
1335            if self.nav_char[0] == c {
1336                self.core.set_selected(0);
1337                return true;
1338            } else {
1339                (1, 0)
1340            }
1341        };
1342        loop {
1343            if idx >= self.nav_char.len() {
1344                idx = 0;
1345            }
1346            if idx == end_loop {
1347                break;
1348            }
1349
1350            if self.nav_char[idx] == c {
1351                self.core.set_selected(idx);
1352                return true;
1353            }
1354
1355            idx += 1;
1356        }
1357        false
1358    }
1359
1360    /// Select by first character. Reverse direction
1361    pub fn reverse_select_by_char(&mut self, c: char) -> bool {
1362        if self.nav_char.is_empty() {
1363            return false;
1364        }
1365        let c = c.to_lowercase().collect::<Vec<_>>();
1366
1367        let (mut idx, end_loop) = if let Some(idx) = self.core.selected() {
1368            if idx == 0 {
1369                (self.nav_char.len() - 1, 0)
1370            } else {
1371                (idx - 1, idx)
1372            }
1373        } else {
1374            if self.nav_char.last() == Some(&c) {
1375                self.core.set_selected(self.nav_char.len() - 1);
1376                return true;
1377            } else {
1378                (self.nav_char.len() - 1, 0)
1379            }
1380        };
1381        loop {
1382            if self.nav_char[idx] == c {
1383                self.core.set_selected(idx);
1384                return true;
1385            }
1386
1387            if idx == end_loop {
1388                break;
1389            }
1390
1391            if idx == 0 {
1392                idx = self.nav_char.len() - 1;
1393            } else {
1394                idx -= 1;
1395            }
1396        }
1397        false
1398    }
1399
1400    /// Select at position
1401    pub fn move_to(&mut self, n: usize) -> ChoiceOutcome {
1402        let old_selected = self.selected();
1403        let r1 = self.popup.set_active(true);
1404        let r2 = self.select(n);
1405        let r3 = self.scroll_to_selected();
1406        if old_selected != self.selected() {
1407            ChoiceOutcome::Value
1408        } else if r1 || r2 || r3 {
1409            ChoiceOutcome::Changed
1410        } else {
1411            ChoiceOutcome::Continue
1412        }
1413    }
1414
1415    /// Select next entry.
1416    pub fn move_down(&mut self, n: usize) -> ChoiceOutcome {
1417        if self.core.is_empty() {
1418            return ChoiceOutcome::Continue;
1419        }
1420
1421        let old_selected = self.selected();
1422        let r1 = self.popup.set_active(true);
1423        let idx = if let Some(idx) = self.core.selected() {
1424            idx + n
1425        } else {
1426            n.saturating_sub(1)
1427        };
1428        let idx = idx.clamp(0, self.len() - 1);
1429        let r2 = self.core.set_selected(idx);
1430        let r3 = self.scroll_to_selected();
1431
1432        if old_selected != self.selected() {
1433            ChoiceOutcome::Value
1434        } else if r1 || r2 || r3 {
1435            ChoiceOutcome::Changed
1436        } else {
1437            ChoiceOutcome::Continue
1438        }
1439    }
1440
1441    /// Select prev entry.
1442    pub fn move_up(&mut self, n: usize) -> ChoiceOutcome {
1443        if self.core.is_empty() {
1444            return ChoiceOutcome::Continue;
1445        }
1446
1447        let old_selected = self.selected();
1448        let r1 = self.popup.set_active(true);
1449        let idx = if let Some(idx) = self.core.selected() {
1450            idx.saturating_sub(n)
1451        } else {
1452            0
1453        };
1454        let idx = idx.clamp(0, self.len() - 1);
1455        let r2 = self.core.set_selected(idx);
1456        let r3 = self.scroll_to_selected();
1457
1458        if old_selected != self.selected() {
1459            ChoiceOutcome::Value
1460        } else if r1 || r2 || r3 {
1461            ChoiceOutcome::Changed
1462        } else {
1463            ChoiceOutcome::Continue
1464        }
1465    }
1466}
1467
1468impl<T: PartialEq + Clone + Default> HandleEvent<crossterm::event::Event, Popup, ChoiceOutcome>
1469    for ChoiceState<T>
1470{
1471    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Popup) -> ChoiceOutcome {
1472        let r = if self.is_focused() {
1473            match event {
1474                ct_event!(key press ' ') | ct_event!(keycode press Enter) => {
1475                    self.flip_popup_active();
1476                    ChoiceOutcome::Changed
1477                }
1478                ct_event!(keycode press Esc) => {
1479                    if self.set_popup_active(false) {
1480                        ChoiceOutcome::Changed
1481                    } else {
1482                        ChoiceOutcome::Continue
1483                    }
1484                }
1485                ct_event!(key press c) => {
1486                    if self.select_by_char(*c) {
1487                        self.scroll_to_selected();
1488                        ChoiceOutcome::Value
1489                    } else {
1490                        ChoiceOutcome::Continue
1491                    }
1492                }
1493                ct_event!(key press SHIFT-c) => {
1494                    if self.reverse_select_by_char(*c) {
1495                        self.scroll_to_selected();
1496                        ChoiceOutcome::Value
1497                    } else {
1498                        ChoiceOutcome::Continue
1499                    }
1500                }
1501                ct_event!(keycode press Delete) | ct_event!(keycode press Backspace) => {
1502                    if self.clear() {
1503                        ChoiceOutcome::Value
1504                    } else {
1505                        ChoiceOutcome::Continue
1506                    }
1507                }
1508                ct_event!(keycode press Down) => self.move_down(1),
1509                ct_event!(keycode press Up) => self.move_up(1),
1510                ct_event!(keycode press PageUp) => self.move_up(self.page_len()),
1511                ct_event!(keycode press PageDown) => self.move_down(self.page_len()),
1512                ct_event!(keycode press Home) => self.move_to(0),
1513                ct_event!(keycode press End) => self.move_to(self.len().saturating_sub(1)),
1514                _ => ChoiceOutcome::Continue,
1515            }
1516        } else {
1517            ChoiceOutcome::Continue
1518        };
1519
1520        if !r.is_consumed() {
1521            self.handle(event, MouseOnly)
1522        } else {
1523            r
1524        }
1525    }
1526}
1527
1528impl<T: PartialEq + Clone + Default> HandleEvent<crossterm::event::Event, MouseOnly, ChoiceOutcome>
1529    for ChoiceState<T>
1530{
1531    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> ChoiceOutcome {
1532        let r0 = handle_mouse(self, event);
1533        let r1 = handle_select(self, event);
1534        let r2 = handle_close(self, event);
1535        let mut r = max(r0, max(r1, r2));
1536
1537        r = r.or_else(|| mouse_trap(event, self.popup.area).into());
1538
1539        r
1540    }
1541}
1542
1543fn handle_mouse<T: PartialEq + Clone + Default>(
1544    state: &mut ChoiceState<T>,
1545    event: &crossterm::event::Event,
1546) -> ChoiceOutcome {
1547    match event {
1548        ct_event!(mouse down Left for x,y)
1549            if state.item_area.contains((*x, *y).into())
1550                || state.button_area.contains((*x, *y).into()) =>
1551        {
1552            if !state.gained_focus() {
1553                state.flip_popup_active();
1554                ChoiceOutcome::Changed
1555            } else {
1556                // hide is down by self.popup.handle() as this click
1557                // is outside the popup area!!
1558                ChoiceOutcome::Continue
1559            }
1560        }
1561        ct_event!(mouse down Left for x,y)
1562        | ct_event!(mouse down Right for x,y)
1563        | ct_event!(mouse down Middle for x,y)
1564            if !state.item_area.contains((*x, *y).into())
1565                && !state.button_area.contains((*x, *y).into()) =>
1566        {
1567            match state.popup.handle(event, Popup) {
1568                PopupOutcome::Hide => {
1569                    state.set_popup_active(false);
1570                    ChoiceOutcome::Changed
1571                }
1572                r => r.into(),
1573            }
1574        }
1575        _ => ChoiceOutcome::Continue,
1576    }
1577}
1578
1579fn handle_select<T: PartialEq + Clone + Default>(
1580    state: &mut ChoiceState<T>,
1581    event: &crossterm::event::Event,
1582) -> ChoiceOutcome {
1583    match state.behave_select {
1584        ChoiceSelect::MouseScroll => {
1585            let mut sas = ScrollAreaState::new()
1586                .area(state.popup.area)
1587                .v_scroll(&mut state.popup_scroll);
1588            let mut r = match sas.handle(event, MouseOnly) {
1589                ScrollOutcome::Up(n) => state.move_up(n),
1590                ScrollOutcome::Down(n) => state.move_down(n),
1591                ScrollOutcome::VPos(n) => state.move_to(n),
1592                _ => ChoiceOutcome::Continue,
1593            };
1594
1595            r = r.or_else(|| match event {
1596                ct_event!(mouse down Left for x,y)
1597                    if state.popup.area.contains((*x, *y).into()) =>
1598                {
1599                    if let Some(n) = item_at(&state.item_areas, *x, *y) {
1600                        state.move_to(state.offset() + n)
1601                    } else {
1602                        ChoiceOutcome::Unchanged
1603                    }
1604                }
1605                ct_event!(mouse drag Left for x,y)
1606                    if state.popup.area.contains((*x, *y).into()) =>
1607                {
1608                    if let Some(n) = item_at(&state.item_areas, *x, *y) {
1609                        state.move_to(state.offset() + n)
1610                    } else {
1611                        ChoiceOutcome::Unchanged
1612                    }
1613                }
1614                _ => ChoiceOutcome::Continue,
1615            });
1616            r
1617        }
1618        ChoiceSelect::MouseMove => {
1619            // effect: move the content below the mouse and keep visible selection.
1620            let mut r = if let Some(selected) = state.core.selected() {
1621                let rel_sel = selected.saturating_sub(state.offset());
1622                let mut sas = ScrollAreaState::new()
1623                    .area(state.popup.area)
1624                    .v_scroll(&mut state.popup_scroll);
1625                match sas.handle(event, MouseOnly) {
1626                    ScrollOutcome::Up(n) => {
1627                        state.popup_scroll.scroll_up(n);
1628                        if state.select(state.offset() + rel_sel) {
1629                            ChoiceOutcome::Value
1630                        } else {
1631                            ChoiceOutcome::Unchanged
1632                        }
1633                    }
1634                    ScrollOutcome::Down(n) => {
1635                        state.popup_scroll.scroll_down(n);
1636                        if state.select(state.offset() + rel_sel) {
1637                            ChoiceOutcome::Value
1638                        } else {
1639                            ChoiceOutcome::Unchanged
1640                        }
1641                    }
1642                    ScrollOutcome::VPos(n) => {
1643                        if state.popup_scroll.set_offset(n) {
1644                            ChoiceOutcome::Value
1645                        } else {
1646                            ChoiceOutcome::Unchanged
1647                        }
1648                    }
1649                    _ => ChoiceOutcome::Continue,
1650                }
1651            } else {
1652                ChoiceOutcome::Continue
1653            };
1654
1655            r = r.or_else(|| match event {
1656                ct_event!(mouse moved for x,y) if state.popup.area.contains((*x, *y).into()) => {
1657                    if let Some(n) = item_at(&state.item_areas, *x, *y) {
1658                        state.move_to(state.offset() + n)
1659                    } else {
1660                        ChoiceOutcome::Unchanged
1661                    }
1662                }
1663                _ => ChoiceOutcome::Continue,
1664            });
1665            r
1666        }
1667        ChoiceSelect::MouseClick => {
1668            // effect: move the content below the mouse and keep visible selection.
1669            let mut sas = ScrollAreaState::new()
1670                .area(state.popup.area)
1671                .v_scroll(&mut state.popup_scroll);
1672            let mut r = match sas.handle(event, MouseOnly) {
1673                ScrollOutcome::Up(n) => {
1674                    if state.popup_scroll.scroll_up(n) {
1675                        ChoiceOutcome::Changed
1676                    } else {
1677                        ChoiceOutcome::Unchanged
1678                    }
1679                }
1680                ScrollOutcome::Down(n) => {
1681                    if state.popup_scroll.scroll_down(n) {
1682                        ChoiceOutcome::Changed
1683                    } else {
1684                        ChoiceOutcome::Unchanged
1685                    }
1686                }
1687                ScrollOutcome::VPos(n) => {
1688                    if state.popup_scroll.set_offset(n) {
1689                        ChoiceOutcome::Changed
1690                    } else {
1691                        ChoiceOutcome::Unchanged
1692                    }
1693                }
1694                _ => ChoiceOutcome::Continue,
1695            };
1696
1697            r = r.or_else(|| match event {
1698                ct_event!(mouse down Left for x,y)
1699                    if state.popup.area.contains((*x, *y).into()) =>
1700                {
1701                    if let Some(n) = item_at(&state.item_areas, *x, *y) {
1702                        state.move_to(state.offset() + n)
1703                    } else {
1704                        ChoiceOutcome::Unchanged
1705                    }
1706                }
1707                ct_event!(mouse drag Left for x,y)
1708                    if state.popup.area.contains((*x, *y).into()) =>
1709                {
1710                    if let Some(n) = item_at(&state.item_areas, *x, *y) {
1711                        state.move_to(state.offset() + n)
1712                    } else {
1713                        ChoiceOutcome::Unchanged
1714                    }
1715                }
1716                _ => ChoiceOutcome::Continue,
1717            });
1718            r
1719        }
1720    }
1721}
1722
1723fn handle_close<T: PartialEq + Clone + Default>(
1724    state: &mut ChoiceState<T>,
1725    event: &crossterm::event::Event,
1726) -> ChoiceOutcome {
1727    match state.behave_close {
1728        ChoiceClose::SingleClick => match event {
1729            ct_event!(mouse down Left for x,y) if state.popup.area.contains((*x, *y).into()) => {
1730                if let Some(n) = item_at(&state.item_areas, *x, *y) {
1731                    let r = state.move_to(state.offset() + n);
1732                    let s = if state.set_popup_active(false) {
1733                        ChoiceOutcome::Changed
1734                    } else {
1735                        ChoiceOutcome::Unchanged
1736                    };
1737                    max(r, s)
1738                } else {
1739                    ChoiceOutcome::Unchanged
1740                }
1741            }
1742            _ => ChoiceOutcome::Continue,
1743        },
1744        ChoiceClose::DoubleClick => match event {
1745            ct_event!(mouse any for m) if state.mouse.doubleclick(state.popup.area, m) => {
1746                if let Some(n) = item_at(&state.item_areas, m.column, m.row) {
1747                    let r = state.move_to(state.offset() + n);
1748                    let s = if state.set_popup_active(false) {
1749                        ChoiceOutcome::Changed
1750                    } else {
1751                        ChoiceOutcome::Unchanged
1752                    };
1753                    max(r, s)
1754                } else {
1755                    ChoiceOutcome::Unchanged
1756                }
1757            }
1758            _ => ChoiceOutcome::Continue,
1759        },
1760    }
1761}
1762
1763/// Handle events for the popup.
1764/// Call before other handlers to deal with intersections
1765/// with other widgets.
1766pub fn handle_events<T: PartialEq + Clone + Default>(
1767    state: &mut ChoiceState<T>,
1768    focus: bool,
1769    event: &crossterm::event::Event,
1770) -> ChoiceOutcome {
1771    state.focus.set(focus);
1772    HandleEvent::handle(state, event, Popup)
1773}
1774
1775/// Handle only mouse-events.
1776pub fn handle_mouse_events<T: PartialEq + Clone + Default>(
1777    state: &mut ChoiceState<T>,
1778    event: &crossterm::event::Event,
1779) -> ChoiceOutcome {
1780    HandleEvent::handle(state, event, MouseOnly)
1781}