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        if styles.button.is_some() {
546            self.button_style = styles.button;
547        }
548        if styles.select.is_some() {
549            self.select_style = styles.select;
550        }
551        if styles.focus.is_some() {
552            self.focus_style = styles.focus;
553        }
554        if styles.block.is_some() {
555            self.block = styles.block;
556        }
557        if let Some(select) = styles.behave_focus {
558            self.behave_focus = select;
559        }
560        if let Some(select) = styles.behave_select {
561            self.behave_select = select;
562        }
563        if let Some(close) = styles.behave_close {
564            self.behave_close = close;
565        }
566        self.block = self.block.map(|v| v.style(self.style));
567        if let Some(alignment) = styles.popup.alignment {
568            self.popup_alignment = alignment;
569        }
570        if let Some(placement) = styles.popup.placement {
571            self.popup_placement = placement;
572        }
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
582        self.popup_block = self.popup_block.map(|v| v.style(self.popup_style));
583        if let Some(border_style) = styles.popup_border {
584            self.popup_block = self.popup_block.map(|v| v.border_style(border_style));
585        }
586        if styles.popup_block.is_some() {
587            self.popup_block = styles.popup_block;
588        }
589
590        if styles.popup_len.is_some() {
591            self.popup_len = styles.popup_len;
592        }
593
594        self
595    }
596
597    /// Base style.
598    pub fn style(mut self, style: Style) -> Self {
599        self.style = style;
600        self.block = self.block.map(|v| v.style(self.style));
601        self
602    }
603
604    /// Style for the down button.
605    pub fn button_style(mut self, style: Style) -> Self {
606        self.button_style = Some(style);
607        self
608    }
609
610    /// Selection in the list.
611    pub fn select_style(mut self, style: Style) -> Self {
612        self.select_style = Some(style);
613        self
614    }
615
616    /// Focused style.
617    pub fn focus_style(mut self, style: Style) -> Self {
618        self.focus_style = Some(style);
619        self
620    }
621
622    /// Block for the main widget.
623    pub fn block(mut self, block: Block<'a>) -> Self {
624        self.block = Some(block);
625        self.block = self.block.map(|v| v.style(self.style));
626        self
627    }
628
629    /// Skips rendering the selected item.
630    /// This is a flag for Combobox, that wants to do its own rendering instead.
631    pub fn skip_item_render(mut self, skip: bool) -> Self {
632        self.skip_item_render = skip;
633        self
634    }
635
636    /// Alignment of the popup.
637    ///
638    /// __Default__
639    /// Default is Left.
640    pub fn popup_alignment(mut self, alignment: Alignment) -> Self {
641        self.popup_alignment = alignment;
642        self
643    }
644
645    /// Placement of the popup.
646    ///
647    /// __Default__
648    /// Default is BelowOrAbove.
649    pub fn popup_placement(mut self, placement: Placement) -> Self {
650        self.popup_placement = placement;
651        self
652    }
653
654    /// Outer boundary for the popup.
655    pub fn popup_boundary(mut self, boundary: Rect) -> Self {
656        self.popup = self.popup.boundary(boundary);
657        self
658    }
659
660    /// Override the popup length.
661    ///
662    /// __Default__
663    /// Defaults to the number of items or 5.
664    pub fn popup_len(mut self, len: u16) -> Self {
665        self.popup_len = Some(len);
666        self
667    }
668
669    /// Base style for the popup.
670    pub fn popup_style(mut self, style: Style) -> Self {
671        self.popup_style = style;
672        self
673    }
674
675    /// Block for the popup.
676    pub fn popup_block(mut self, block: Block<'a>) -> Self {
677        self.popup_block = Some(block);
678        self
679    }
680
681    /// Scroll for the popup.
682    pub fn popup_scroll(mut self, scroll: Scroll<'a>) -> Self {
683        self.popup_scroll = Some(scroll);
684        self
685    }
686
687    /// Adds an extra offset to the widget area.
688    ///
689    /// This can be used to
690    /// * place the widget under the mouse cursor.
691    /// * align the widget not by the outer bounds but by
692    ///   the text content.
693    pub fn popup_offset(mut self, offset: (i16, i16)) -> Self {
694        self.popup = self.popup.offset(offset);
695        self
696    }
697
698    /// Sets only the x offset.
699    /// See [offset](Self::popup_offset)
700    pub fn popup_x_offset(mut self, offset: i16) -> Self {
701        self.popup = self.popup.x_offset(offset);
702        self
703    }
704
705    /// Sets only the y offset.
706    /// See [offset](Self::popup_offset)
707    pub fn popup_y_offset(mut self, offset: i16) -> Self {
708        self.popup = self.popup.y_offset(offset);
709        self
710    }
711
712    /// Sets the behaviour for selecting from the list.
713    pub fn behave_focus(mut self, focus: ChoiceFocus) -> Self {
714        self.behave_focus = focus;
715        self
716    }
717
718    /// Sets the behaviour for selecting from the list.
719    pub fn behave_select(mut self, select: ChoiceSelect) -> Self {
720        self.behave_select = select;
721        self
722    }
723
724    /// Sets the behaviour for closing the list.
725    pub fn behave_close(mut self, close: ChoiceClose) -> Self {
726        self.behave_close = close;
727        self
728    }
729
730    /// Inherent width.
731    pub fn width(&self) -> u16 {
732        let w = self
733            .items
734            .borrow()
735            .iter()
736            .map(|v| v.width())
737            .max()
738            .unwrap_or_default();
739
740        w as u16 + block_size(&self.block).width
741    }
742
743    /// Inherent height.
744    pub fn height(&self) -> u16 {
745        1 + block_size(&self.block).height
746    }
747
748    /// Choice itself doesn't render.
749    ///
750    /// This builds the widgets from the parameters set for Choice.
751    pub fn into_widgets(self) -> (ChoiceWidget<'a, T>, ChoicePopup<'a, T>) {
752        (
753            ChoiceWidget {
754                values: self.values,
755                default_value: self.default_value,
756                items: self.items.clone(),
757                style: self.style,
758                button_style: self.button_style,
759                focus_style: self.focus_style,
760                block: self.block,
761                skip_item_render: self.skip_item_render,
762                len: self.popup_len,
763                behave_focus: self.behave_focus,
764                behave_select: self.behave_select,
765                behave_close: self.behave_close,
766            },
767            ChoicePopup {
768                items: self.items.clone(),
769                style: self.style,
770                select_style: self.select_style,
771                popup: self.popup,
772                popup_style: self.popup_style,
773                popup_scroll: self.popup_scroll,
774                popup_block: self.popup_block,
775                popup_alignment: self.popup_alignment,
776                popup_placement: self.popup_placement,
777                popup_len: self.popup_len,
778                _phantom: Default::default(),
779            },
780        )
781    }
782}
783
784impl<'a, T> ChoiceWidget<'a, T>
785where
786    T: PartialEq + Clone + Default,
787{
788    /// Inherent width.
789    pub fn width(&self) -> u16 {
790        let w = self
791            .items
792            .borrow()
793            .iter()
794            .map(|v| v.width())
795            .max()
796            .unwrap_or_default();
797
798        w as u16 + block_size(&self.block).width
799    }
800
801    /// Inherent height.
802    pub fn height(&self) -> u16 {
803        1 + block_size(&self.block).height
804    }
805}
806
807impl<'a, T> StatefulWidget for &ChoiceWidget<'a, T>
808where
809    T: PartialEq + Clone + Default,
810{
811    type State = ChoiceState<T>;
812
813    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
814        state.core.set_values(self.values.borrow().clone());
815        if let Some(default_value) = self.default_value.clone() {
816            state.core.set_default_value(Some(default_value));
817        }
818
819        render_choice(self, area, buf, state);
820    }
821}
822
823impl<T> StatefulWidget for ChoiceWidget<'_, T>
824where
825    T: PartialEq + Clone + Default,
826{
827    type State = ChoiceState<T>;
828
829    fn render(mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
830        state.core.set_values(self.values.take());
831        if let Some(default_value) = self.default_value.take() {
832            state.core.set_default_value(Some(default_value));
833        }
834
835        render_choice(&self, area, buf, state);
836    }
837}
838
839fn render_choice<T: PartialEq + Clone + Default>(
840    widget: &ChoiceWidget<'_, T>,
841    area: Rect,
842    buf: &mut Buffer,
843    state: &mut ChoiceState<T>,
844) {
845    state.area = area;
846    state.behave_focus.set(widget.behave_focus);
847    state.behave_select = widget.behave_select;
848    state.behave_close = widget.behave_close;
849
850    if !state.popup.is_active() {
851        let len = widget
852            .len
853            .unwrap_or_else(|| min(5, widget.items.borrow().len()) as u16);
854        state.popup_scroll.max_offset = widget.items.borrow().len().saturating_sub(len as usize);
855        state.popup_scroll.page_len = len as usize;
856        if let Some(selected) = state.core.selected() {
857            state.popup_scroll.scroll_to_pos(selected);
858        }
859    }
860
861    state.nav_char.clear();
862    state.nav_char.extend(widget.items.borrow().iter().map(|v| {
863        v.spans
864            .first()
865            .and_then(|v| v.content.as_ref().chars().next())
866            .map_or(Vec::default(), |c| c.to_lowercase().collect::<Vec<_>>())
867    }));
868
869    let inner = widget.block.inner_if_some(area);
870
871    state.item_area = Rect::new(
872        inner.x,
873        inner.y,
874        inner.width.saturating_sub(3),
875        inner.height,
876    );
877    state.button_area = Rect::new(
878        inner.right().saturating_sub(min(3, inner.width)),
879        inner.y,
880        3,
881        inner.height,
882    );
883
884    let style = widget.style;
885    let focus_style = widget.focus_style.unwrap_or(revert_style(widget.style));
886
887    if state.is_focused() {
888        if let Some(block) = &widget.block {
889            block.render(area, buf);
890        } else {
891            buf.set_style(inner, style);
892        }
893        buf.set_style(inner, focus_style);
894    } else {
895        if let Some(block) = &widget.block {
896            block.render(area, buf);
897        } else {
898            buf.set_style(inner, style);
899        }
900        if let Some(button_style) = widget.button_style {
901            buf.set_style(state.button_area, button_style);
902        }
903    }
904
905    if !widget.skip_item_render {
906        if let Some(selected) = state.core.selected() {
907            if let Some(item) = widget.items.borrow().get(selected) {
908                item.render(state.item_area, buf);
909            }
910        }
911    }
912
913    let dy = if (state.button_area.height & 1) == 1 {
914        state.button_area.height / 2
915    } else {
916        state.button_area.height.saturating_sub(1) / 2
917    };
918    let bc = if state.is_popup_active() {
919        " â—† "
920    } else {
921        " â–¼ "
922    };
923    Span::from(bc).render(
924        Rect::new(state.button_area.x, state.button_area.y + dy, 3, 1),
925        buf,
926    );
927}
928
929impl<T> ChoicePopup<'_, T>
930where
931    T: PartialEq + Clone + Default,
932{
933    /// Calculate the layout for the popup before rendering.
934    /// Area is the area of the ChoiceWidget not the ChoicePopup.
935    pub fn layout(&self, area: Rect, buf: &mut Buffer, state: &mut ChoiceState<T>) -> Rect {
936        if state.popup.is_active() {
937            let len = min(
938                self.popup_len.unwrap_or(5),
939                self.items.borrow().len() as u16,
940            );
941            let padding = block_padding(&self.popup_block);
942            let popup_len = len + padding.top + padding.bottom;
943            let pop_area = Rect::new(0, 0, area.width, popup_len);
944
945            self.popup
946                .ref_constraint(
947                    self.popup_placement
948                        .into_constraint(self.popup_alignment, area),
949                )
950                .layout(pop_area, buf)
951        } else {
952            Rect::default()
953        }
954    }
955}
956
957impl<T> StatefulWidget for &ChoicePopup<'_, T>
958where
959    T: PartialEq + Clone + Default,
960{
961    type State = ChoiceState<T>;
962
963    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
964        render_popup(self, area, buf, state);
965    }
966}
967
968impl<T> StatefulWidget for ChoicePopup<'_, T>
969where
970    T: PartialEq + Clone + Default,
971{
972    type State = ChoiceState<T>;
973
974    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
975        render_popup(&self, area, buf, state);
976    }
977}
978
979fn render_popup<T: PartialEq + Clone + Default>(
980    widget: &ChoicePopup<'_, T>,
981    area: Rect,
982    buf: &mut Buffer,
983    state: &mut ChoiceState<T>,
984) {
985    if state.popup.is_active() {
986        let popup_style = widget.popup_style;
987        let select_style = widget.select_style.unwrap_or(revert_style(widget.style));
988
989        {
990            let len = min(
991                widget.popup_len.unwrap_or(5),
992                widget.items.borrow().len() as u16,
993            );
994            let padding = block_padding(&widget.popup_block);
995            let popup_len = len + padding.top + padding.bottom;
996            let pop_area = Rect::new(0, 0, area.width, popup_len);
997
998            widget
999                .popup
1000                .ref_constraint(
1001                    widget
1002                        .popup_placement
1003                        .into_constraint(widget.popup_alignment, area),
1004                )
1005                .render(pop_area, buf, &mut state.popup);
1006        }
1007
1008        let sa = ScrollArea::new()
1009            .style(fallback_popup_style(widget.popup_style))
1010            .block(widget.popup_block.as_ref())
1011            .v_scroll(widget.popup_scroll.as_ref());
1012
1013        let inner = sa.inner(state.popup.area, None, Some(&state.popup_scroll));
1014
1015        sa.render(
1016            state.popup.area,
1017            buf,
1018            &mut ScrollAreaState::new().v_scroll(&mut state.popup_scroll),
1019        );
1020
1021        state.popup_scroll.max_offset = widget
1022            .items
1023            .borrow()
1024            .len()
1025            .saturating_sub(inner.height as usize);
1026        state.popup_scroll.page_len = inner.height as usize;
1027
1028        state.item_areas.clear();
1029        let mut row = inner.y;
1030        let mut idx = state.popup_scroll.offset;
1031        loop {
1032            if row >= inner.bottom() {
1033                break;
1034            }
1035
1036            let item_area = Rect::new(inner.x, row, inner.width, 1);
1037            state.item_areas.push(item_area);
1038
1039            if let Some(item) = widget.items.borrow().get(idx) {
1040                let style = if state.core.selected() == Some(idx) {
1041                    popup_style.patch(select_style)
1042                } else {
1043                    popup_style
1044                };
1045
1046                buf.set_style(item_area, style);
1047                item.render(item_area, buf);
1048            } else {
1049                // noop?
1050            }
1051
1052            row += 1;
1053            idx += 1;
1054        }
1055    } else {
1056        state.popup.clear_areas();
1057    }
1058}
1059
1060impl<T> Clone for ChoiceState<T>
1061where
1062    T: PartialEq + Clone + Default,
1063{
1064    fn clone(&self) -> Self {
1065        let popup = self.popup.clone();
1066        let behave_focus = Rc::new(Cell::new(self.behave_focus.get()));
1067        let focus = focus_cb(
1068            popup.active.clone(),
1069            behave_focus.clone(),
1070            Default::default(),
1071        );
1072
1073        Self {
1074            area: self.area,
1075            nav_char: self.nav_char.clone(),
1076            item_area: self.item_area,
1077            button_area: self.button_area,
1078            item_areas: self.item_areas.clone(),
1079            core: self.core.clone(),
1080            popup,
1081            popup_scroll: self.popup_scroll.clone(),
1082            behave_focus,
1083            behave_select: self.behave_select,
1084            behave_close: self.behave_close,
1085            focus,
1086            mouse: Default::default(),
1087            non_exhaustive: NonExhaustive,
1088        }
1089    }
1090}
1091
1092impl<T> Default for ChoiceState<T>
1093where
1094    T: PartialEq + Clone + Default,
1095{
1096    fn default() -> Self {
1097        let popup = PopupCoreState::default();
1098        let behave_focus = Rc::new(Cell::new(ChoiceFocus::default()));
1099        let focus = focus_cb(
1100            popup.active.clone(),
1101            behave_focus.clone(),
1102            Default::default(),
1103        );
1104
1105        Self {
1106            area: Default::default(),
1107            nav_char: Default::default(),
1108            item_area: Default::default(),
1109            button_area: Default::default(),
1110            item_areas: Default::default(),
1111            core: Default::default(),
1112            popup,
1113            popup_scroll: Default::default(),
1114            behave_focus,
1115            behave_select: Default::default(),
1116            behave_close: Default::default(),
1117            focus,
1118            mouse: Default::default(),
1119            non_exhaustive: NonExhaustive,
1120        }
1121    }
1122}
1123
1124fn focus_cb(
1125    active: Rc<Cell<bool>>,
1126    behave_focus: Rc<Cell<ChoiceFocus>>,
1127    flag: FocusFlag,
1128) -> FocusFlag {
1129    let active_clone = active.clone();
1130    flag.on_lost(move || {
1131        if active_clone.get() {
1132            active_clone.set(false);
1133        }
1134    });
1135    flag.on_gained(move || {
1136        if !active.get() {
1137            if behave_focus.get() == ChoiceFocus::OpenOnFocusGained {
1138                active.set(true);
1139            }
1140        }
1141    });
1142    flag
1143}
1144
1145impl<T> HasFocus for ChoiceState<T>
1146where
1147    T: PartialEq + Clone + Default,
1148{
1149    fn build(&self, builder: &mut FocusBuilder) {
1150        builder.widget_with_flags(self.focus(), self.area(), 0, self.navigable());
1151        builder.widget_with_flags(self.focus(), self.popup.area, 1, Navigation::Mouse);
1152    }
1153
1154    fn focus(&self) -> FocusFlag {
1155        self.focus.clone()
1156    }
1157
1158    fn area(&self) -> Rect {
1159        self.area
1160    }
1161}
1162
1163impl<T> RelocatableState for ChoiceState<T>
1164where
1165    T: PartialEq + Clone + Default,
1166{
1167    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1168        self.area = relocate_area(self.area, shift, clip);
1169        self.item_area = relocate_area(self.item_area, shift, clip);
1170        self.button_area = relocate_area(self.button_area, shift, clip);
1171        relocate_areas(&mut self.item_areas, shift, clip);
1172        self.popup.relocate(shift, clip);
1173    }
1174}
1175
1176impl<T> ChoiceState<T>
1177where
1178    T: PartialEq + Clone + Default,
1179{
1180    pub fn new() -> Self {
1181        Self::default()
1182    }
1183
1184    pub fn named(name: &str) -> Self {
1185        let mut z = Self::default();
1186        z.focus = focus_cb(
1187            z.popup.active.clone(),
1188            z.behave_focus.clone(),
1189            FocusFlag::named(name),
1190        );
1191        z
1192    }
1193
1194    /// Popup is active?
1195    pub fn is_popup_active(&self) -> bool {
1196        self.popup.is_active()
1197    }
1198
1199    /// Flip the popup state.
1200    pub fn flip_popup_active(&mut self) {
1201        self.popup.flip_active();
1202    }
1203
1204    /// Show the popup.
1205    pub fn set_popup_active(&mut self, active: bool) -> bool {
1206        self.popup.set_active(active)
1207    }
1208
1209    /// Set a default-value other than T::default()
1210    ///
1211    /// The starting value will still be T::default()
1212    /// after this. You must call clear() to use this
1213    /// default.
1214    ///
1215    /// This default will be overridden by a default set
1216    /// on the widget.
1217    pub fn set_default_value(&mut self, default_value: Option<T>) {
1218        self.core.set_default_value(default_value);
1219    }
1220
1221    /// A default value.
1222    pub fn default_value(&self) -> &Option<T> {
1223        self.core.default_value()
1224    }
1225
1226    /// Select the given value.
1227    ///
1228    /// If the value doesn't exist in the list or the list is
1229    /// empty the value will still be set, but selected will be
1230    /// None. The list will be empty before the first render, but
1231    /// the first thing render will do is set the list of values.
1232    /// This will adjust the selected index if possible.
1233    /// It's still ok to set a value here that can not be represented.
1234    /// As long as there is no user interaction, the same value
1235    /// will be returned by value().
1236    pub fn set_value(&mut self, value: T) -> bool {
1237        self.core.set_value(value)
1238    }
1239
1240    /// Get the selected value.
1241    pub fn value(&self) -> T {
1242        self.core.value()
1243    }
1244
1245    /// Select the default value or T::default.
1246    pub fn clear(&mut self) -> bool {
1247        self.core.clear()
1248    }
1249
1250    /// Select the value at index. This will set the value
1251    /// to the given index in the value-list. If the index is
1252    /// out of bounds or the value-list is empty it will
1253    /// set selected to None and leave the value as is.
1254    /// The list is empty before the first render so this
1255    /// may not work as expected.
1256    ///
1257    /// The selected index is a best effort artefact, the main
1258    /// thing is the value itself.
1259    ///
1260    /// Use of set_value() is preferred.
1261    pub fn select(&mut self, select: usize) -> bool {
1262        self.core.set_selected(select)
1263    }
1264
1265    /// Returns the selected index or None if the
1266    /// value is not in the list or the list is empty.
1267    ///
1268    /// You can still get the value set with set_value() though.
1269    pub fn selected(&self) -> Option<usize> {
1270        self.core.selected()
1271    }
1272
1273    /// Any items?
1274    pub fn is_empty(&self) -> bool {
1275        self.core.values().is_empty()
1276    }
1277
1278    /// Number of items.
1279    pub fn len(&self) -> usize {
1280        self.core.values().len()
1281    }
1282
1283    /// Scroll offset for the item list.
1284    pub fn clear_offset(&mut self) {
1285        self.popup_scroll.set_offset(0);
1286    }
1287
1288    /// Scroll offset for the item list.
1289    pub fn set_offset(&mut self, offset: usize) -> bool {
1290        self.popup_scroll.set_offset(offset)
1291    }
1292
1293    /// Scroll offset for the item list.
1294    pub fn offset(&self) -> usize {
1295        self.popup_scroll.offset()
1296    }
1297
1298    /// Scroll offset for the item list.
1299    pub fn max_offset(&self) -> usize {
1300        self.popup_scroll.max_offset()
1301    }
1302
1303    /// Page length for the item list.
1304    pub fn page_len(&self) -> usize {
1305        self.popup_scroll.page_len()
1306    }
1307
1308    /// Scroll unit for the item list.
1309    pub fn scroll_by(&self) -> usize {
1310        self.popup_scroll.scroll_by()
1311    }
1312
1313    /// Scroll the item list to the selected value.
1314    pub fn scroll_to_selected(&mut self) -> bool {
1315        if let Some(selected) = self.core.selected() {
1316            self.popup_scroll.scroll_to_pos(selected)
1317        } else {
1318            false
1319        }
1320    }
1321}
1322
1323impl<T> ChoiceState<T>
1324where
1325    T: PartialEq + Clone + Default,
1326{
1327    /// Select by first character.
1328    pub fn select_by_char(&mut self, c: char) -> bool {
1329        if self.nav_char.is_empty() {
1330            return false;
1331        }
1332        let c = c.to_lowercase().collect::<Vec<_>>();
1333
1334        let (mut idx, end_loop) = if let Some(idx) = self.core.selected() {
1335            (idx + 1, idx)
1336        } else {
1337            if self.nav_char[0] == c {
1338                self.core.set_selected(0);
1339                return true;
1340            } else {
1341                (1, 0)
1342            }
1343        };
1344        loop {
1345            if idx >= self.nav_char.len() {
1346                idx = 0;
1347            }
1348            if idx == end_loop {
1349                break;
1350            }
1351
1352            if self.nav_char[idx] == c {
1353                self.core.set_selected(idx);
1354                return true;
1355            }
1356
1357            idx += 1;
1358        }
1359        false
1360    }
1361
1362    /// Select by first character. Reverse direction
1363    pub fn reverse_select_by_char(&mut self, c: char) -> bool {
1364        if self.nav_char.is_empty() {
1365            return false;
1366        }
1367        let c = c.to_lowercase().collect::<Vec<_>>();
1368
1369        let (mut idx, end_loop) = if let Some(idx) = self.core.selected() {
1370            if idx == 0 {
1371                (self.nav_char.len() - 1, 0)
1372            } else {
1373                (idx - 1, idx)
1374            }
1375        } else {
1376            if self.nav_char.last() == Some(&c) {
1377                self.core.set_selected(self.nav_char.len() - 1);
1378                return true;
1379            } else {
1380                (self.nav_char.len() - 1, 0)
1381            }
1382        };
1383        loop {
1384            if self.nav_char[idx] == c {
1385                self.core.set_selected(idx);
1386                return true;
1387            }
1388
1389            if idx == end_loop {
1390                break;
1391            }
1392
1393            if idx == 0 {
1394                idx = self.nav_char.len() - 1;
1395            } else {
1396                idx -= 1;
1397            }
1398        }
1399        false
1400    }
1401
1402    /// Select at position
1403    pub fn move_to(&mut self, n: usize) -> ChoiceOutcome {
1404        let old_selected = self.selected();
1405        let r1 = self.popup.set_active(true);
1406        let r2 = self.select(n);
1407        let r3 = self.scroll_to_selected();
1408        if old_selected != self.selected() {
1409            ChoiceOutcome::Value
1410        } else if r1 || r2 || r3 {
1411            ChoiceOutcome::Changed
1412        } else {
1413            ChoiceOutcome::Continue
1414        }
1415    }
1416
1417    /// Select next entry.
1418    pub fn move_down(&mut self, n: usize) -> ChoiceOutcome {
1419        if self.core.is_empty() {
1420            return ChoiceOutcome::Continue;
1421        }
1422
1423        let old_selected = self.selected();
1424        let r1 = self.popup.set_active(true);
1425        let idx = if let Some(idx) = self.core.selected() {
1426            idx + n
1427        } else {
1428            n.saturating_sub(1)
1429        };
1430        let idx = idx.clamp(0, self.len() - 1);
1431        let r2 = self.core.set_selected(idx);
1432        let r3 = self.scroll_to_selected();
1433
1434        if old_selected != self.selected() {
1435            ChoiceOutcome::Value
1436        } else if r1 || r2 || r3 {
1437            ChoiceOutcome::Changed
1438        } else {
1439            ChoiceOutcome::Continue
1440        }
1441    }
1442
1443    /// Select prev entry.
1444    pub fn move_up(&mut self, n: usize) -> ChoiceOutcome {
1445        if self.core.is_empty() {
1446            return ChoiceOutcome::Continue;
1447        }
1448
1449        let old_selected = self.selected();
1450        let r1 = self.popup.set_active(true);
1451        let idx = if let Some(idx) = self.core.selected() {
1452            idx.saturating_sub(n)
1453        } else {
1454            0
1455        };
1456        let idx = idx.clamp(0, self.len() - 1);
1457        let r2 = self.core.set_selected(idx);
1458        let r3 = self.scroll_to_selected();
1459
1460        if old_selected != self.selected() {
1461            ChoiceOutcome::Value
1462        } else if r1 || r2 || r3 {
1463            ChoiceOutcome::Changed
1464        } else {
1465            ChoiceOutcome::Continue
1466        }
1467    }
1468}
1469
1470impl<T: PartialEq + Clone + Default> HandleEvent<crossterm::event::Event, Popup, ChoiceOutcome>
1471    for ChoiceState<T>
1472{
1473    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Popup) -> ChoiceOutcome {
1474        let r = if self.is_focused() {
1475            match event {
1476                ct_event!(key press ' ') | ct_event!(keycode press Enter) => {
1477                    self.flip_popup_active();
1478                    ChoiceOutcome::Changed
1479                }
1480                ct_event!(keycode press Esc) => {
1481                    if self.set_popup_active(false) {
1482                        ChoiceOutcome::Changed
1483                    } else {
1484                        ChoiceOutcome::Continue
1485                    }
1486                }
1487                ct_event!(key press c) => {
1488                    if self.select_by_char(*c) {
1489                        self.scroll_to_selected();
1490                        ChoiceOutcome::Value
1491                    } else {
1492                        ChoiceOutcome::Continue
1493                    }
1494                }
1495                ct_event!(key press SHIFT-c) => {
1496                    if self.reverse_select_by_char(*c) {
1497                        self.scroll_to_selected();
1498                        ChoiceOutcome::Value
1499                    } else {
1500                        ChoiceOutcome::Continue
1501                    }
1502                }
1503                ct_event!(keycode press Delete) | ct_event!(keycode press Backspace) => {
1504                    if self.clear() {
1505                        ChoiceOutcome::Value
1506                    } else {
1507                        ChoiceOutcome::Continue
1508                    }
1509                }
1510                ct_event!(keycode press Down) => self.move_down(1),
1511                ct_event!(keycode press Up) => self.move_up(1),
1512                ct_event!(keycode press PageUp) => self.move_up(self.page_len()),
1513                ct_event!(keycode press PageDown) => self.move_down(self.page_len()),
1514                ct_event!(keycode press Home) => self.move_to(0),
1515                ct_event!(keycode press End) => self.move_to(self.len().saturating_sub(1)),
1516                _ => ChoiceOutcome::Continue,
1517            }
1518        } else {
1519            ChoiceOutcome::Continue
1520        };
1521
1522        if !r.is_consumed() {
1523            self.handle(event, MouseOnly)
1524        } else {
1525            r
1526        }
1527    }
1528}
1529
1530impl<T: PartialEq + Clone + Default> HandleEvent<crossterm::event::Event, MouseOnly, ChoiceOutcome>
1531    for ChoiceState<T>
1532{
1533    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> ChoiceOutcome {
1534        let r0 = handle_mouse(self, event);
1535        let r1 = handle_select(self, event);
1536        let r2 = handle_close(self, event);
1537        let mut r = max(r0, max(r1, r2));
1538
1539        r = r.or_else(|| mouse_trap(event, self.popup.area).into());
1540
1541        r
1542    }
1543}
1544
1545fn handle_mouse<T: PartialEq + Clone + Default>(
1546    state: &mut ChoiceState<T>,
1547    event: &crossterm::event::Event,
1548) -> ChoiceOutcome {
1549    match event {
1550        ct_event!(mouse down Left for x,y)
1551            if state.item_area.contains((*x, *y).into())
1552                || state.button_area.contains((*x, *y).into()) =>
1553        {
1554            if !state.gained_focus() {
1555                state.flip_popup_active();
1556                ChoiceOutcome::Changed
1557            } else {
1558                // hide is down by self.popup.handle() as this click
1559                // is outside the popup area!!
1560                ChoiceOutcome::Continue
1561            }
1562        }
1563        ct_event!(mouse down Left for x,y)
1564        | ct_event!(mouse down Right for x,y)
1565        | ct_event!(mouse down Middle for x,y)
1566            if !state.item_area.contains((*x, *y).into())
1567                && !state.button_area.contains((*x, *y).into()) =>
1568        {
1569            match state.popup.handle(event, Popup) {
1570                PopupOutcome::Hide => {
1571                    state.set_popup_active(false);
1572                    ChoiceOutcome::Changed
1573                }
1574                r => r.into(),
1575            }
1576        }
1577        _ => ChoiceOutcome::Continue,
1578    }
1579}
1580
1581fn handle_select<T: PartialEq + Clone + Default>(
1582    state: &mut ChoiceState<T>,
1583    event: &crossterm::event::Event,
1584) -> ChoiceOutcome {
1585    match state.behave_select {
1586        ChoiceSelect::MouseScroll => {
1587            let mut sas = ScrollAreaState::new()
1588                .area(state.popup.area)
1589                .v_scroll(&mut state.popup_scroll);
1590            let mut r = match sas.handle(event, MouseOnly) {
1591                ScrollOutcome::Up(n) => state.move_up(n),
1592                ScrollOutcome::Down(n) => state.move_down(n),
1593                ScrollOutcome::VPos(n) => state.move_to(n),
1594                _ => ChoiceOutcome::Continue,
1595            };
1596
1597            r = r.or_else(|| match event {
1598                ct_event!(mouse down Left for x,y)
1599                    if state.popup.area.contains((*x, *y).into()) =>
1600                {
1601                    if let Some(n) = item_at(&state.item_areas, *x, *y) {
1602                        state.move_to(state.offset() + n)
1603                    } else {
1604                        ChoiceOutcome::Unchanged
1605                    }
1606                }
1607                ct_event!(mouse drag Left for x,y)
1608                    if state.popup.area.contains((*x, *y).into()) =>
1609                {
1610                    if let Some(n) = item_at(&state.item_areas, *x, *y) {
1611                        state.move_to(state.offset() + n)
1612                    } else {
1613                        ChoiceOutcome::Unchanged
1614                    }
1615                }
1616                _ => ChoiceOutcome::Continue,
1617            });
1618            r
1619        }
1620        ChoiceSelect::MouseMove => {
1621            // effect: move the content below the mouse and keep visible selection.
1622            let mut r = if let Some(selected) = state.core.selected() {
1623                let rel_sel = selected.saturating_sub(state.offset());
1624                let mut sas = ScrollAreaState::new()
1625                    .area(state.popup.area)
1626                    .v_scroll(&mut state.popup_scroll);
1627                match sas.handle(event, MouseOnly) {
1628                    ScrollOutcome::Up(n) => {
1629                        state.popup_scroll.scroll_up(n);
1630                        if state.select(state.offset() + rel_sel) {
1631                            ChoiceOutcome::Value
1632                        } else {
1633                            ChoiceOutcome::Unchanged
1634                        }
1635                    }
1636                    ScrollOutcome::Down(n) => {
1637                        state.popup_scroll.scroll_down(n);
1638                        if state.select(state.offset() + rel_sel) {
1639                            ChoiceOutcome::Value
1640                        } else {
1641                            ChoiceOutcome::Unchanged
1642                        }
1643                    }
1644                    ScrollOutcome::VPos(n) => {
1645                        if state.popup_scroll.set_offset(n) {
1646                            ChoiceOutcome::Value
1647                        } else {
1648                            ChoiceOutcome::Unchanged
1649                        }
1650                    }
1651                    _ => ChoiceOutcome::Continue,
1652                }
1653            } else {
1654                ChoiceOutcome::Continue
1655            };
1656
1657            r = r.or_else(|| match event {
1658                ct_event!(mouse moved for x,y) if state.popup.area.contains((*x, *y).into()) => {
1659                    if let Some(n) = item_at(&state.item_areas, *x, *y) {
1660                        state.move_to(state.offset() + n)
1661                    } else {
1662                        ChoiceOutcome::Unchanged
1663                    }
1664                }
1665                _ => ChoiceOutcome::Continue,
1666            });
1667            r
1668        }
1669        ChoiceSelect::MouseClick => {
1670            // effect: move the content below the mouse and keep visible selection.
1671            let mut sas = ScrollAreaState::new()
1672                .area(state.popup.area)
1673                .v_scroll(&mut state.popup_scroll);
1674            let mut r = match sas.handle(event, MouseOnly) {
1675                ScrollOutcome::Up(n) => {
1676                    if state.popup_scroll.scroll_up(n) {
1677                        ChoiceOutcome::Changed
1678                    } else {
1679                        ChoiceOutcome::Unchanged
1680                    }
1681                }
1682                ScrollOutcome::Down(n) => {
1683                    if state.popup_scroll.scroll_down(n) {
1684                        ChoiceOutcome::Changed
1685                    } else {
1686                        ChoiceOutcome::Unchanged
1687                    }
1688                }
1689                ScrollOutcome::VPos(n) => {
1690                    if state.popup_scroll.set_offset(n) {
1691                        ChoiceOutcome::Changed
1692                    } else {
1693                        ChoiceOutcome::Unchanged
1694                    }
1695                }
1696                _ => ChoiceOutcome::Continue,
1697            };
1698
1699            r = r.or_else(|| match event {
1700                ct_event!(mouse down Left for x,y)
1701                    if state.popup.area.contains((*x, *y).into()) =>
1702                {
1703                    if let Some(n) = item_at(&state.item_areas, *x, *y) {
1704                        state.move_to(state.offset() + n)
1705                    } else {
1706                        ChoiceOutcome::Unchanged
1707                    }
1708                }
1709                ct_event!(mouse drag Left for x,y)
1710                    if state.popup.area.contains((*x, *y).into()) =>
1711                {
1712                    if let Some(n) = item_at(&state.item_areas, *x, *y) {
1713                        state.move_to(state.offset() + n)
1714                    } else {
1715                        ChoiceOutcome::Unchanged
1716                    }
1717                }
1718                _ => ChoiceOutcome::Continue,
1719            });
1720            r
1721        }
1722    }
1723}
1724
1725fn handle_close<T: PartialEq + Clone + Default>(
1726    state: &mut ChoiceState<T>,
1727    event: &crossterm::event::Event,
1728) -> ChoiceOutcome {
1729    match state.behave_close {
1730        ChoiceClose::SingleClick => match event {
1731            ct_event!(mouse down Left for x,y) if state.popup.area.contains((*x, *y).into()) => {
1732                if let Some(n) = item_at(&state.item_areas, *x, *y) {
1733                    let r = state.move_to(state.offset() + n);
1734                    let s = if state.set_popup_active(false) {
1735                        ChoiceOutcome::Changed
1736                    } else {
1737                        ChoiceOutcome::Unchanged
1738                    };
1739                    max(r, s)
1740                } else {
1741                    ChoiceOutcome::Unchanged
1742                }
1743            }
1744            _ => ChoiceOutcome::Continue,
1745        },
1746        ChoiceClose::DoubleClick => match event {
1747            ct_event!(mouse any for m) if state.mouse.doubleclick(state.popup.area, m) => {
1748                if let Some(n) = item_at(&state.item_areas, m.column, m.row) {
1749                    let r = state.move_to(state.offset() + n);
1750                    let s = if state.set_popup_active(false) {
1751                        ChoiceOutcome::Changed
1752                    } else {
1753                        ChoiceOutcome::Unchanged
1754                    };
1755                    max(r, s)
1756                } else {
1757                    ChoiceOutcome::Unchanged
1758                }
1759            }
1760            _ => ChoiceOutcome::Continue,
1761        },
1762    }
1763}
1764
1765/// Handle events for the popup.
1766/// Call before other handlers to deal with intersections
1767/// with other widgets.
1768pub fn handle_events<T: PartialEq + Clone + Default>(
1769    state: &mut ChoiceState<T>,
1770    focus: bool,
1771    event: &crossterm::event::Event,
1772) -> ChoiceOutcome {
1773    state.focus.set(focus);
1774    HandleEvent::handle(state, event, Popup)
1775}
1776
1777/// Handle only mouse-events.
1778pub fn handle_mouse_events<T: PartialEq + Clone + Default>(
1779    state: &mut ChoiceState<T>,
1780    event: &crossterm::event::Event,
1781) -> ChoiceOutcome {
1782    HandleEvent::handle(state, event, MouseOnly)
1783}