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