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