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