Skip to main content

rat_widget/
list.rs

1//!
2//! A list widget.
3//!
4//! Supports all of ratatui's List features and adds
5//! * different selection models.
6//! * exact navigation for lists with varying row-heights.
7//!
8
9use crate::_private::NonExhaustive;
10use crate::event::util::MouseFlags;
11use crate::list::selection::{RowSelection, RowSetSelection};
12use crate::text::HasScreenCursor;
13use crate::util::{fallback_select_style, revert_style};
14use rat_event::{HandleEvent, MouseOnly, Outcome, Regular};
15use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
16use rat_reloc::{RelocatableState, relocate_areas};
17use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState, ScrollStyle};
18use ratatui_core::buffer::Buffer;
19use ratatui_core::layout::Rect;
20use ratatui_core::style::Style;
21use ratatui_core::text::{Line, Text};
22use ratatui_core::widgets::{StatefulWidget, Widget};
23use ratatui_crossterm::crossterm::event::Event;
24use ratatui_widgets::block::Block;
25use ratatui_widgets::list::ListDirection;
26use ratatui_widgets::table::HighlightSpacing;
27use std::cmp::{max, min};
28use std::collections::HashSet;
29use std::marker::PhantomData;
30
31pub mod edit;
32
33/// Trait for list-selection.
34pub trait ListSelection {
35    /// Number of selected rows.
36    fn count(&self) -> usize;
37
38    /// Is selected.
39    fn is_selected(&self, n: usize) -> bool;
40
41    /// Selection lead.
42    fn lead_selection(&self) -> Option<usize>;
43
44    /// Scroll the selection instead of the offset.
45    fn scroll_selected(&self) -> bool {
46        false
47    }
48
49    /// Validate the selected row against the number of rows.
50    #[allow(unused_variables)]
51    fn validate_rows(&mut self, rows: usize) {}
52
53    /// Correct the selection for added items.
54    #[allow(unused_variables)]
55    fn items_added(&mut self, pos: usize, n: usize) {}
56
57    /// Correct the selection for removed items.
58    #[allow(unused_variables)]
59    fn items_removed(&mut self, pos: usize, n: usize, rows: usize) {}
60}
61
62/// List widget.
63///
64/// Fully compatible with ratatui List.
65/// Adds Scroll, selection models, and event-handling.
66#[derive(Debug, Default, Clone)]
67pub struct List<'a, Selection = RowSelection> {
68    items: Vec<ListItem<'a>>,
69
70    style: Style,
71    block: Option<Block<'a>>,
72    scroll: Option<Scroll<'a>>,
73    scroll_padding: usize,
74    varying_heights: bool,
75
76    select_style: Option<Style>,
77    focus_style: Option<Style>,
78    direction: ListDirection,
79    highlight_spacing: HighlightSpacing,
80    highlight_symbol: Option<Line<'a>>,
81    repeat_highlight_symbol: bool,
82    highlight_style: Option<Style>,
83
84    _phantom: PhantomData<Selection>,
85}
86
87#[derive(Debug, Clone, Eq, PartialEq, Hash)]
88pub struct ListItem<'a> {
89    pub(crate) content: Text<'a>,
90    pub(crate) style: Style,
91}
92
93/// Collected styles.
94#[derive(Debug, Clone)]
95pub struct ListStyle {
96    /// Style
97    pub style: Style,
98    /// List Block
99    pub block: Option<Block<'static>>,
100    /// Border
101    pub border_style: Option<Style>,
102    /// Title
103    pub title_style: Option<Style>,
104    /// Scroll
105    pub scroll: Option<ScrollStyle>,
106
107    /// Always show some items above and below the selection.
108    pub scroll_padding: Option<usize>,
109
110    /// Style for selection
111    pub select: Option<Style>,
112    /// Style for selection when focused.
113    pub focus: Option<Style>,
114
115    /// Selection marker.
116    pub highlight_spacing: Option<HighlightSpacing>,
117    /// Selection marker.
118    pub highlight_symbol: Option<&'static str>,
119    /// Selection marker.
120    pub repeat_highlight_symbol: Option<bool>,
121    /// Selection marker.
122    pub highlight_style: Option<Style>,
123
124    pub non_exhaustive: NonExhaustive,
125}
126
127/// Stores row-heights in the state.
128#[derive(Debug, Clone)]
129pub enum RowHeights {
130    /// One row-height for all the list-items.
131    Uniform(u8),
132    /// Each list-item has its own row-height.
133    Varying(Vec<u8>),
134}
135
136impl Default for RowHeights {
137    fn default() -> Self {
138        RowHeights::Uniform(1)
139    }
140}
141
142impl RowHeights {
143    pub(crate) fn clear(&mut self) {
144        match self {
145            RowHeights::Uniform(_) => {}
146            RowHeights::Varying(h) => h.clear(),
147        }
148    }
149
150    pub(crate) fn push(&mut self, row_height: u8, varying: bool) {
151        if varying {
152            match self {
153                RowHeights::Uniform(_) => {
154                    *self = RowHeights::Varying(vec![row_height]);
155                }
156                RowHeights::Varying(h) => h.push(row_height),
157            }
158        } else {
159            *self = RowHeights::Uniform(row_height);
160        }
161    }
162
163    pub(crate) fn items_added(&mut self, pos: usize, n: usize) {
164        match self {
165            RowHeights::Uniform(_) => {}
166            RowHeights::Varying(h) => {
167                if pos <= h.len() {
168                    for _ in 0..n {
169                        h.insert(pos, 1);
170                    }
171                }
172            }
173        }
174    }
175
176    pub(crate) fn items_added_with_heights(&mut self, pos: usize, height: &[u8]) {
177        match self {
178            RowHeights::Uniform(_) => {}
179            RowHeights::Varying(h) => {
180                if pos <= h.len() {
181                    h.splice(pos..pos, height.into_iter().copied());
182                }
183            }
184        }
185    }
186
187    pub(crate) fn items_removed(&mut self, pos: usize, n: usize) {
188        match self {
189            RowHeights::Uniform(_) => {}
190            RowHeights::Varying(h) => {
191                if pos < h.len() {
192                    if pos + n < h.len() {
193                        h.drain(pos..pos + n);
194                    } else {
195                        h.drain(pos..);
196                    }
197                }
198            }
199        }
200    }
201}
202
203/// State & event handling.
204#[derive(Debug)]
205pub struct ListState<Selection = RowSelection> {
206    /// Total area
207    /// __readonly__. renewed for each render.
208    pub area: Rect,
209    /// Area inside the block.
210    /// __readonly__. renewed for each render.
211    pub inner: Rect,
212    /// Areas for the rendered items.
213    /// __readonly__. renewed for each render.
214    pub row_areas: Vec<Rect>,
215    /// Row heights for all list rows.
216    /// __readonly__. renewed for each render.
217    pub row_heights: RowHeights,
218    /// Scroll padding.
219    /// __readonly__. renewed for each render.
220    pub scroll_padding: usize,
221    /// Widget direction.
222    /// __readonly__. renewed for each render.
223    pub direction: ListDirection,
224
225    /// Length in items.
226    /// __mostly readonly__. renewed for each render.
227    pub rows: usize,
228    /// Offset etc.
229    /// __read+write__
230    pub scroll: ScrollState,
231
232    /// Focus
233    /// __read+write__
234    pub focus: FocusFlag,
235    /// Selection model
236    /// __read+write__
237    pub selection: Selection,
238
239    /// Helper for mouse events.
240    /// __used for mouse interaction__
241    pub mouse: MouseFlags,
242
243    pub non_exhaustive: NonExhaustive,
244}
245
246impl Default for ListStyle {
247    fn default() -> Self {
248        Self {
249            style: Default::default(),
250            block: Default::default(),
251            border_style: Default::default(),
252            title_style: Default::default(),
253            scroll: Default::default(),
254            scroll_padding: Default::default(),
255            select: Default::default(),
256            focus: Default::default(),
257            highlight_spacing: Default::default(),
258            highlight_symbol: Default::default(),
259            repeat_highlight_symbol: Default::default(),
260            highlight_style: Default::default(),
261            non_exhaustive: NonExhaustive,
262        }
263    }
264}
265
266impl<'a> ListItem<'a> {
267    /// Creates a new [`ListItem`]
268    /// The `content` parameter accepts any value that can be converted into [`Text`].
269    pub fn new<T>(content: T) -> Self
270    where
271        T: Into<Text<'a>>,
272    {
273        Self {
274            content: content.into(),
275            style: Style::default(),
276        }
277    }
278
279    /// Sets the item style.
280    pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
281        self.style = style.into();
282        self
283    }
284
285    /// Returns the item height.
286    pub fn height(&self) -> usize {
287        self.content.height()
288    }
289
290    /// Returns the max width of all the lines
291    pub fn width(&self) -> usize {
292        self.content.width()
293    }
294}
295
296impl<'a, T> From<T> for ListItem<'a>
297where
298    T: Into<Text<'a>>,
299{
300    fn from(value: T) -> Self {
301        Self::new(value)
302    }
303}
304
305impl<'a, Selection> List<'a, Selection> {
306    /// New list.
307    pub fn new<T>(items: T) -> Self
308    where
309        T: IntoIterator,
310        T::Item: Into<ListItem<'a>>,
311    {
312        let items = items.into_iter().map(|v| v.into()).collect();
313
314        Self {
315            block: None,
316            scroll: None,
317            items,
318            style: Default::default(),
319            select_style: Default::default(),
320            focus_style: Default::default(),
321            direction: Default::default(),
322            highlight_spacing: Default::default(),
323            highlight_symbol: Default::default(),
324            repeat_highlight_symbol: Default::default(),
325            highlight_style: Default::default(),
326            scroll_padding: 0,
327            varying_heights: Default::default(),
328            _phantom: Default::default(),
329        }
330    }
331
332    /// Set items.
333    pub fn items<T>(mut self, items: T) -> Self
334    where
335        T: IntoIterator,
336        T::Item: Into<ListItem<'a>>,
337    {
338        let items = items.into_iter().map(|v| v.into()).collect();
339        self.items = items;
340        self
341    }
342
343    /// Border support.
344    #[inline]
345    pub fn block(mut self, block: Block<'a>) -> Self {
346        self.block = Some(block);
347        self.block = self.block.map(|v| v.style(self.style));
348        self
349    }
350
351    /// Scroll support.
352    #[inline]
353    pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
354        self.scroll = Some(scroll);
355        self
356    }
357
358    /// Set all styles.
359    #[inline]
360    pub fn styles_opt(self, styles: Option<ListStyle>) -> Self {
361        if let Some(styles) = styles {
362            self.styles(styles)
363        } else {
364            self
365        }
366    }
367
368    /// Set all styles.
369    #[inline]
370    pub fn styles(mut self, styles: ListStyle) -> Self {
371        self.style = styles.style;
372        if styles.block.is_some() {
373            self.block = styles.block;
374        }
375        if let Some(border_style) = styles.border_style {
376            self.block = self.block.map(|v| v.border_style(border_style));
377        }
378        if let Some(title_style) = styles.title_style {
379            self.block = self.block.map(|v| v.title_style(title_style));
380        }
381        self.block = self.block.map(|v| v.style(self.style));
382
383        if let Some(styles) = styles.scroll {
384            self.scroll = self.scroll.map(|v| v.styles(styles));
385        }
386        if let Some(scroll_padding) = styles.scroll_padding {
387            self.scroll_padding = scroll_padding;
388        }
389
390        if styles.select.is_some() {
391            self.select_style = styles.select;
392        }
393        if styles.focus.is_some() {
394            self.focus_style = styles.focus;
395        }
396        if let Some(highlight_spacing) = styles.highlight_spacing {
397            self.highlight_spacing = highlight_spacing;
398        }
399        if let Some(highlight_symbol) = styles.highlight_symbol {
400            self.highlight_symbol = Some(highlight_symbol.into());
401        }
402        if let Some(repeat_highlight_symbol) = styles.repeat_highlight_symbol {
403            self.repeat_highlight_symbol = repeat_highlight_symbol;
404        }
405        if let Some(highlight_style) = styles.highlight_style {
406            self.highlight_style = Some(highlight_style);
407        }
408        self
409    }
410
411    /// Base style
412    #[inline]
413    pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
414        self.style = style.into();
415        self.block = self.block.map(|v| v.style(self.style));
416        self
417    }
418
419    /// Select style.
420    #[inline]
421    pub fn select_style<S: Into<Style>>(mut self, select_style: S) -> Self {
422        self.select_style = Some(select_style.into());
423        self
424    }
425
426    /// Focused style.
427    #[inline]
428    pub fn focus_style<S: Into<Style>>(mut self, focus_style: S) -> Self {
429        self.focus_style = Some(focus_style.into());
430        self
431    }
432
433    /// If your list uses varying row-heights for each row
434    /// calculating the correct offset to show a specific
435    /// item will fail.
436    ///
437    /// With this flag the row-height for each row is
438    /// stored as part of the state.
439    pub fn varying_heights(mut self, varying: bool) -> Self {
440        self.varying_heights = varying;
441        self
442    }
443
444    /// Defines the direction how the items are displayed.
445    ///
446    /// With bottom to top, the item at offset is rendered at
447    /// the bottom of the area and moves up for the next items.
448    /// Navigation with the arrow keys will also reverse and
449    /// follows the screen directions.
450    #[inline]
451    pub fn direction(mut self, direction: ListDirection) -> Self {
452        self.direction = direction;
453        self
454    }
455
456    /// Set the symbol to be displayed in front of the selected item.
457    /// By default, there are no highlight symbol.
458    #[inline]
459    pub fn highlight_symbol<L: Into<Line<'a>>>(mut self, highlight_symbol: L) -> Self {
460        self.highlight_symbol = Some(highlight_symbol.into());
461        self
462    }
463
464    /// Separate style for the highlight symbol.
465    ///
466    /// When no set the symbol uses the list-style and the select-style.
467    #[inline]
468    pub fn highlight_style<S: Into<Style>>(mut self, style: S) -> Self {
469        self.highlight_style = Some(style.into());
470        self
471    }
472
473    /// Set whether to repeat the highlight symbol and style over
474    /// selected multi-line items.
475    /// This is false by default.
476    #[inline]
477    pub fn repeat_highlight_symbol(mut self, repeat: bool) -> Self {
478        self.repeat_highlight_symbol = repeat;
479        self
480    }
481
482    /// Set when to show the highlight spacing
483    ///
484    /// The highlight spacing is the spacing that is allocated for
485    /// the selection symbol (if enabled) and is used to shift the
486    /// list when an item is selected. This method allows you to
487    /// configure when this spacing is allocated.
488    ///
489    /// * HighlightSpacing::Always will always allocate the spacing,
490    /// regardless of whether an item is selected or not. This means
491    /// that the table will never change size, regardless of if an
492    /// item is selected or not.
493    /// * HighlightSpacing::WhenSelected will only allocate the
494    /// spacing if an item is selected. This means that the table
495    /// will shift when an item is selected. This is the default
496    /// setting for backwards compatibility, but it is recommended
497    /// to use HighlightSpacing::Always for a better user experience.
498    /// * HighlightSpacing::Never will never allocate the spacing,
499    /// regardless of whether an item is selected or not. This means
500    /// that the highlight symbol will never be drawn.
501    pub fn highlight_spacing(mut self, value: HighlightSpacing) -> Self {
502        self.highlight_spacing = value;
503        self
504    }
505
506    /// Sets the number of items around the currently selected item
507    /// that should be kept visible.
508    pub fn scroll_padding(mut self, padding: usize) -> Self {
509        self.scroll_padding = padding;
510        self
511    }
512
513    /// Number of items.
514    #[inline]
515    pub fn len(&self) -> usize {
516        self.items.len()
517    }
518
519    /// Empty?
520    #[inline]
521    pub fn is_empty(&self) -> bool {
522        self.items.is_empty()
523    }
524
525    /// Inherent width
526    pub fn width(&self) -> u16 {
527        let mut width = 0;
528        for item in &self.items {
529            width = max(width, item.width() as u16);
530        }
531        width
532    }
533
534    /// Inherent height
535    pub fn height(&self) -> u16 {
536        let mut height = 0u16;
537        for item in &self.items {
538            height += height.saturating_add(item.height() as u16);
539        }
540        height
541    }
542}
543
544impl<'a, Item, Selection> FromIterator<Item> for List<'a, Selection>
545where
546    Item: Into<ListItem<'a>>,
547{
548    fn from_iter<Iter: IntoIterator<Item = Item>>(iter: Iter) -> Self {
549        Self::new(iter)
550    }
551}
552
553impl<Selection: ListSelection> StatefulWidget for List<'_, Selection> {
554    type State = ListState<Selection>;
555
556    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
557        render_list(self, area, buf, state)
558    }
559}
560
561fn render_list<Selection: ListSelection>(
562    widget: List<'_, Selection>,
563    area: Rect,
564    buf: &mut Buffer,
565    state: &mut ListState<Selection>,
566) {
567    state.rows = widget.items.len();
568    state.area = area;
569    state.selection.validate_rows(state.rows);
570    state.scroll_padding = widget.scroll_padding;
571    state.direction = widget.direction;
572
573    let sa = ScrollArea::new()
574        .style(widget.style)
575        .block(widget.block.as_ref())
576        .v_scroll(widget.scroll.as_ref());
577    state.inner = sa.inner(area, None, Some(&state.scroll));
578    sa.render(
579        area,
580        buf,
581        &mut ScrollAreaState::new().v_scroll(&mut state.scroll),
582    );
583
584    // max_offset
585    let mut n = 0;
586    let mut height = 0;
587    for item in widget.items.iter().rev() {
588        height += item.height();
589        if height > state.inner.height as usize {
590            break;
591        }
592        n += 1;
593    }
594    state.scroll.set_max_offset(state.rows.saturating_sub(n));
595
596    let (high_area, item_area) = layout_columns(&widget, state.inner, state);
597
598    let focus_style = widget.focus_style.unwrap_or(revert_style(widget.style));
599    let select_style = widget
600        .select_style
601        .unwrap_or(fallback_select_style(widget.style));
602    let (style, select_style) = if state.is_focused() {
603        (widget.style, focus_style)
604    } else {
605        (widget.style, select_style)
606    };
607    let high_style = if let Some(highlight_style) = widget.highlight_style {
608        style.patch(highlight_style)
609    } else {
610        style
611    };
612    let high_sym = if let Some(high_sym) = widget.highlight_symbol {
613        high_sym
614    } else {
615        Line::default()
616    };
617
618    state.row_areas.clear();
619    state.row_heights.clear();
620    state.scroll.page_len = 0;
621
622    if widget.direction == ListDirection::TopToBottom {
623        let mut row_y = state.inner.y;
624        for (row, item) in widget.items.into_iter().enumerate() {
625            let row_height = item.height() as u8;
626            state.row_heights.push(row_height, widget.varying_heights);
627
628            if row < state.offset() || row_y >= state.inner.bottom() {
629                continue;
630            }
631
632            let row_area = Rect::new(state.inner.x, row_y, state.inner.width, row_height as u16)
633                .intersection(state.inner);
634
635            state.row_areas.push(row_area);
636            state.scroll.page_len += 1;
637
638            let style = if state.selection.is_selected(row) {
639                style.patch(select_style).patch(item.style)
640            } else {
641                style.patch(item.style)
642            };
643            buf.set_style(row_area, style);
644            buf.set_style(row_area, item.content.style);
645
646            for mut line in item.content.lines.into_iter() {
647                if row_y >= state.inner.bottom() {
648                    break;
649                }
650
651                line.alignment = line.alignment.or(item.content.alignment);
652                line.render(
653                    Rect::new(row_area.x + item_area.x, row_y, item_area.width, 1),
654                    buf,
655                );
656
657                row_y += 1;
658            }
659
660            if state.selection.is_selected(row) {
661                for yy in row_area.y..row_area.bottom() {
662                    let high_area = Rect::new(
663                        row_area.x + high_area.x, //
664                        yy,
665                        high_area.width,
666                        1,
667                    );
668                    if widget.repeat_highlight_symbol || yy == row_area.y {
669                        buf.set_style(high_area, high_style);
670                        (&high_sym).render(high_area, buf);
671                    }
672                }
673            }
674        }
675    } else if widget.direction == ListDirection::BottomToTop {
676        let mut row_y = state.inner.bottom();
677        for (row, item) in widget.items.into_iter().enumerate() {
678            let row_height = item.height() as u8;
679            state.row_heights.push(row_height, widget.varying_heights);
680
681            if row < state.offset() || row_y <= state.inner.y {
682                continue;
683            }
684
685            let row_area = Rect::new(
686                state.inner.x,
687                row_y.saturating_sub(row_height as u16),
688                state.inner.width,
689                row_height as u16,
690            )
691            .intersection(state.inner);
692
693            state.row_areas.push(row_area);
694            state.scroll.page_len += 1;
695
696            let style = if state.selection.is_selected(row) {
697                style.patch(select_style).patch(item.style)
698            } else {
699                style.patch(item.style)
700            };
701            buf.set_style(row_area, style);
702            buf.set_style(row_area, item.content.style);
703
704            // render line-wise from bottom to top and clip at the top.
705            for mut line in item.content.lines.into_iter().rev() {
706                if row_y <= state.inner.y {
707                    break;
708                }
709                row_y -= 1;
710
711                line.alignment = line.alignment.or(item.content.alignment);
712                line.render(
713                    Rect::new(row_area.x + item_area.x, row_y, item_area.width, 1),
714                    buf,
715                );
716            }
717
718            if state.selection.is_selected(row) {
719                for yy in row_area.y..row_area.bottom() {
720                    let high_area = Rect::new(
721                        row_area.x + high_area.x, //
722                        yy,
723                        high_area.width,
724                        1,
725                    );
726                    if widget.repeat_highlight_symbol || yy == row_area.y {
727                        buf.set_style(high_area, high_style);
728                        (&high_sym).render(high_area, buf);
729                    }
730                }
731            }
732        }
733    }
734}
735
736fn layout_columns<Selection: ListSelection>(
737    widget: &List<'_, Selection>,
738    area: Rect,
739    state: &ListState<Selection>,
740) -> (Rect, Rect) {
741    let is_highlight = match widget.highlight_spacing {
742        HighlightSpacing::Always => true,
743        HighlightSpacing::WhenSelected => state.selection.lead_selection().is_some(),
744        HighlightSpacing::Never => false,
745    };
746
747    let highlight_area = if let Some(highlight_symbol) = &widget.highlight_symbol
748        && is_highlight
749    {
750        Rect::new(0, 0, highlight_symbol.width() as u16, 0)
751    } else {
752        Rect::new(0, 0, 0, 0)
753    };
754
755    let item_area = Rect::new(
756        highlight_area.right(),
757        0,
758        area.width.saturating_sub(highlight_area.right()),
759        0,
760    );
761
762    (highlight_area, item_area)
763}
764
765impl<Selection> HasFocus for ListState<Selection> {
766    fn build(&self, builder: &mut FocusBuilder) {
767        builder.leaf_widget(self);
768    }
769
770    #[inline]
771    fn focus(&self) -> FocusFlag {
772        self.focus.clone()
773    }
774
775    #[inline]
776    fn area(&self) -> Rect {
777        self.area
778    }
779}
780
781impl<Selection> HasScreenCursor for ListState<Selection> {
782    fn screen_cursor(&self) -> Option<(u16, u16)> {
783        None
784    }
785}
786
787impl<Selection: Default> Default for ListState<Selection> {
788    fn default() -> Self {
789        Self {
790            area: Default::default(),
791            inner: Default::default(),
792            row_areas: Default::default(),
793            row_heights: Default::default(),
794            scroll_padding: Default::default(),
795            direction: Default::default(),
796            rows: Default::default(),
797            scroll: Default::default(),
798            focus: Default::default(),
799            selection: Default::default(),
800            mouse: Default::default(),
801            non_exhaustive: NonExhaustive,
802        }
803    }
804}
805
806impl<Selection: Clone> Clone for ListState<Selection> {
807    fn clone(&self) -> Self {
808        Self {
809            area: self.area,
810            inner: self.inner,
811            row_areas: self.row_areas.clone(),
812            row_heights: self.row_heights.clone(),
813            scroll_padding: self.scroll_padding,
814            direction: self.direction,
815            rows: self.rows,
816            scroll: self.scroll.clone(),
817            focus: self.focus.new_instance(),
818            selection: self.selection.clone(),
819            mouse: Default::default(),
820            non_exhaustive: NonExhaustive,
821        }
822    }
823}
824
825impl<Selection> RelocatableState for ListState<Selection> {
826    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
827        self.area.relocate(shift, clip);
828        self.inner.relocate(shift, clip);
829        relocate_areas(self.row_areas.as_mut_slice(), shift, clip);
830        self.scroll.relocate(shift, clip);
831    }
832}
833
834impl<Selection: ListSelection> ListState<Selection> {
835    /// New initial state.
836    pub fn new() -> Self
837    where
838        Selection: Default,
839    {
840        Default::default()
841    }
842
843    /// New state with a focus name
844    pub fn named(name: &str) -> Self
845    where
846        Selection: Default,
847    {
848        let mut z = Self::default();
849        z.focus = z.focus.with_name(name);
850        z
851    }
852
853    #[inline]
854    pub fn rows(&self) -> usize {
855        self.rows
856    }
857
858    /// Update the state to match adding items.
859    ///
860    /// This corrects the number of rows, offset and selection.
861    pub fn items_added(&mut self, pos: usize, n: usize) {
862        self.rows = self.rows.saturating_add(n);
863        self.scroll.items_added(pos, n);
864        self.row_heights.items_added(pos, n);
865        self.selection.items_added(pos, n);
866    }
867
868    /// Update the state to match adding items.
869    /// Use this function if you have varying row-heights.
870    ///
871    /// This corrects the number of rows, offset and selection.
872    /// This also corrects the stored row-heights with the
873    /// given heights.
874    pub fn items_added_with_heights(&mut self, pos: usize, height: &[u8]) {
875        self.rows = self.rows.saturating_add(height.len());
876        self.scroll.items_added(pos, height.len());
877        self.row_heights.items_added_with_heights(pos, height);
878        self.selection.items_added(pos, height.len());
879    }
880
881    /// Update the state to match removing items.
882    ///
883    /// This corrects the number of rows, offset and selection.
884    pub fn items_removed(&mut self, pos: usize, n: usize) {
885        self.rows = self.rows.saturating_sub(n);
886        self.scroll.items_removed(pos, n);
887        self.row_heights.items_removed(pos, n);
888        self.selection.items_removed(pos, n, self.rows);
889    }
890
891    /// Update the number of rows.
892    /// This corrects the number of rows *during* event-handling.
893    /// A number of functions depend on the number of rows,
894    /// but this value is only updated during render.
895    ///
896    /// If you encounter such a case, manually changing the number of rows
897    /// will fix it.
898    ///
899    /// This corrects the number of rows, offset and selection.
900    pub fn rows_changed(&mut self, rows: usize) {
901        self.selection.validate_rows(rows);
902        self.scroll.rows_changed(rows);
903        self.row_heights.clear();
904        self.rows = rows;
905    }
906
907    #[inline]
908    pub fn clear_offset(&mut self) {
909        self.scroll.set_offset(0);
910    }
911
912    #[inline]
913    pub fn max_offset(&self) -> usize {
914        self.scroll.max_offset()
915    }
916
917    #[inline]
918    pub fn set_max_offset(&mut self, max: usize) {
919        self.scroll.set_max_offset(max);
920    }
921
922    #[inline]
923    pub fn offset(&self) -> usize {
924        self.scroll.offset()
925    }
926
927    #[inline]
928    pub fn set_offset(&mut self, offset: usize) -> bool {
929        self.scroll.set_offset(offset)
930    }
931
932    #[inline]
933    pub fn page_len(&self) -> usize {
934        self.scroll.page_len()
935    }
936
937    pub fn scroll_by(&self) -> usize {
938        self.scroll.scroll_by()
939    }
940
941    /// Scroll to selected.
942    #[inline]
943    pub fn scroll_to_selected(&mut self) -> bool {
944        if let Some(selected) = self.selection.lead_selection() {
945            self.scroll_to(selected)
946        } else {
947            false
948        }
949    }
950
951    #[inline]
952    pub fn scroll_to(&mut self, mut pos: usize) -> bool {
953        if pos >= self.rows {
954            false
955        } else if self.inner.is_empty() {
956            // area is empty before first render too.
957            self.set_offset(pos)
958        } else if let RowHeights::Uniform(row_height) = &self.row_heights {
959            let page_len = self.inner.height.div_ceil(*row_height as u16) as usize;
960            let scroll_padding = min((page_len - 1) / 2, self.scroll_padding);
961            if pos
962                >= self
963                    .offset()
964                    .saturating_add(page_len - 1)
965                    .saturating_sub(scroll_padding)
966            {
967                if page_len as u16 * (*row_height) as u16 > self.inner.height {
968                    self.set_offset(pos.saturating_sub(page_len - 2))
969                } else {
970                    self.set_offset(pos.saturating_sub(page_len - 1))
971                }
972            } else if pos < self.offset() {
973                self.set_offset(pos.saturating_sub(scroll_padding))
974            } else {
975                false
976            }
977        } else if let RowHeights::Varying(row_heights) = &self.row_heights {
978            let scroll_padding = min((self.page_len() - 1) / 2, self.scroll_padding);
979            if pos
980                >= self
981                    .offset()
982                    .saturating_add(self.page_len() - 1)
983                    .saturating_sub(scroll_padding)
984            {
985                if pos >= row_heights.len() {
986                    self.set_offset(pos)
987                } else {
988                    pos = min(pos + scroll_padding, row_heights.len().saturating_sub(1));
989                    let mut sum_height = row_heights[pos] as u16;
990                    if sum_height < self.inner.height {
991                        loop {
992                            if pos < 1 {
993                                break;
994                            }
995                            sum_height += row_heights[pos - 1] as u16;
996                            if sum_height > self.inner.height {
997                                break;
998                            }
999                            pos -= 1;
1000                        }
1001                    }
1002                    self.set_offset(pos)
1003                }
1004            } else if pos < self.offset().saturating_add(scroll_padding) {
1005                self.set_offset(pos.saturating_sub(scroll_padding))
1006            } else {
1007                false
1008            }
1009        } else {
1010            false
1011        }
1012    }
1013
1014    #[inline]
1015    pub fn scroll_up(&mut self, n: usize) -> bool {
1016        if self.direction == ListDirection::TopToBottom {
1017            self.scroll.scroll_up(n)
1018        } else {
1019            self.scroll.scroll_down(n)
1020        }
1021    }
1022
1023    #[inline]
1024    pub fn scroll_down(&mut self, n: usize) -> bool {
1025        if self.direction == ListDirection::TopToBottom {
1026            self.scroll.scroll_down(n)
1027        } else {
1028            self.scroll.scroll_up(n)
1029        }
1030    }
1031}
1032
1033impl<Selection: ListSelection> ListState<Selection> {
1034    /// Returns the row-area for the given row, if it is visible.
1035    pub fn row_area(&self, row: usize) -> Option<Rect> {
1036        if row < self.scroll.offset() || row >= self.scroll.offset() + self.scroll.page_len() {
1037            return None;
1038        }
1039        let row = row - self.scroll.offset;
1040        if row >= self.row_areas.len() {
1041            None
1042        } else {
1043            Some(self.row_areas[row])
1044        }
1045    }
1046
1047    #[inline]
1048    pub fn row_at_clicked(&self, pos: (u16, u16)) -> Option<usize> {
1049        self.mouse
1050            .row_at(&self.row_areas, pos.1)
1051            .map(|v| self.scroll.offset() + v)
1052    }
1053
1054    /// Row when dragging. Can go outside the area.
1055    #[inline]
1056    pub fn row_at_drag(&self, pos: (u16, u16)) -> usize {
1057        match self.mouse.row_at_drag(self.inner, &self.row_areas, pos.1) {
1058            Ok(v) => self.scroll.offset() + v,
1059            Err(v) if v <= 0 => self.scroll.offset().saturating_sub((-v) as usize),
1060            Err(v) => self.scroll.offset() + self.row_areas.len() + v as usize,
1061        }
1062    }
1063}
1064
1065impl ListState<RowSelection> {
1066    /// When scrolling the table, change the selection instead of the offset.
1067    #[inline]
1068    pub fn set_scroll_selection(&mut self, scroll: bool) {
1069        self.selection.set_scroll_selected(scroll);
1070    }
1071
1072    /// Scroll delivers a value between 0 and max_offset as offset.
1073    /// This remaps the ratio to the selection with a range 0..row_len.
1074    ///
1075    pub(crate) fn remap_offset_selection(&self, offset: usize) -> usize {
1076        if self.scroll.max_offset() > 0 {
1077            (self.rows * offset) / self.scroll.max_offset()
1078        } else {
1079            0 // ???
1080        }
1081    }
1082
1083    /// Clear the selection.
1084    #[inline]
1085    pub fn clear_selection(&mut self) {
1086        self.selection.clear();
1087    }
1088
1089    /// Anything selected?
1090    #[inline]
1091    pub fn has_selection(&mut self) -> bool {
1092        self.selection.has_selection()
1093    }
1094
1095    /// Returns the lead selection.
1096    #[inline]
1097    pub fn selected(&self) -> Option<usize> {
1098        self.selection.lead_selection()
1099    }
1100
1101    /// Returns the lead selection. Ensures the index is
1102    /// less than rows.
1103    #[inline]
1104    pub fn selected_checked(&self) -> Option<usize> {
1105        self.selection.lead_selection().filter(|v| *v < self.rows)
1106    }
1107
1108    #[inline]
1109    pub fn select(&mut self, row: Option<usize>) -> bool {
1110        self.selection.select(row)
1111    }
1112
1113    /// Move the selection to the given row. Limits the movement to the row-count.
1114    /// Ensures the row is visible afterward.
1115    #[inline]
1116    pub fn move_to(&mut self, row: usize) -> bool {
1117        let r = self.selection.move_to(row, self.rows.saturating_sub(1));
1118        let s = self.scroll_to(self.selection.selected().expect("row"));
1119        r || s
1120    }
1121
1122    /// Move the selection up n rows.
1123    /// Ensures the row is visible afterward.
1124    #[inline]
1125    pub fn move_up(&mut self, n: usize) -> bool {
1126        let r = if self.direction == ListDirection::TopToBottom {
1127            self.selection.move_up(n, self.rows.saturating_sub(1))
1128        } else {
1129            self.selection.move_down(n, self.rows.saturating_sub(1))
1130        };
1131        let s = self.scroll_to(self.selection.selected().expect("row"));
1132        r || s
1133    }
1134
1135    /// Move the selection down n rows.
1136    /// Ensures the row is visible afterward.
1137    #[inline]
1138    pub fn move_down(&mut self, n: usize) -> bool {
1139        let r = if self.direction == ListDirection::TopToBottom {
1140            self.selection.move_down(n, self.rows.saturating_sub(1))
1141        } else {
1142            self.selection.move_up(n, self.rows.saturating_sub(1))
1143        };
1144        let s = self.scroll_to(self.selection.selected().expect("row"));
1145        r || s
1146    }
1147}
1148
1149impl ListState<RowSetSelection> {
1150    /// Clear the selection.
1151    #[inline]
1152    pub fn clear_selection(&mut self) {
1153        self.selection.clear();
1154    }
1155
1156    /// Anything selected?
1157    #[inline]
1158    pub fn has_selection(&mut self) -> bool {
1159        self.selection.has_selection()
1160    }
1161
1162    #[inline]
1163    pub fn selected(&self) -> HashSet<usize> {
1164        self.selection.selected()
1165    }
1166
1167    /// Change the lead-selection. Limits the value to the number of rows.
1168    /// If extend is false the current selection is cleared and both lead and
1169    /// anchor are set to the given value.
1170    /// If extend is true, the anchor is kept where it is and lead is changed.
1171    /// Everything in the range `anchor..lead` is selected. It doesn't matter
1172    /// if anchor < lead.
1173    #[inline]
1174    pub fn set_lead(&mut self, row: Option<usize>, extend: bool) -> bool {
1175        if let Some(row) = row {
1176            self.selection
1177                .set_lead(Some(min(row, self.rows.saturating_sub(1))), extend)
1178        } else {
1179            self.selection.set_lead(None, extend)
1180        }
1181    }
1182
1183    /// Current lead.
1184    #[inline]
1185    pub fn lead(&self) -> Option<usize> {
1186        self.selection.lead()
1187    }
1188
1189    /// Current anchor.
1190    #[inline]
1191    pub fn anchor(&self) -> Option<usize> {
1192        self.selection.anchor()
1193    }
1194
1195    /// Set a new lead, at the same time limit the lead to max.
1196    #[inline]
1197    pub fn set_lead_clamped(&mut self, lead: usize, max: usize, extend: bool) {
1198        self.selection.move_to(lead, max, extend);
1199    }
1200
1201    /// Retire the current anchor/lead selection to the set of selected rows.
1202    /// Resets lead and anchor and starts a new selection round.
1203    #[inline]
1204    pub fn retire_selection(&mut self) {
1205        self.selection.retire_selection();
1206    }
1207
1208    /// Add to selection.
1209    #[inline]
1210    pub fn add_selected(&mut self, idx: usize) {
1211        self.selection.add(idx);
1212    }
1213
1214    /// Remove from selection. Only works for retired selections, not for the
1215    /// active anchor-lead range.
1216    #[inline]
1217    pub fn remove_selected(&mut self, idx: usize) {
1218        self.selection.remove(idx);
1219    }
1220
1221    /// Move the selection to the given row.
1222    /// Ensures the row is visible afterward.
1223    #[inline]
1224    pub fn move_to(&mut self, row: usize, extend: bool) -> bool {
1225        let r = self
1226            .selection
1227            .move_to(row, self.rows.saturating_sub(1), extend);
1228        let s = self.scroll_to(self.selection.lead().expect("row"));
1229        r || s
1230    }
1231
1232    /// Move the selection up n rows.
1233    /// Ensures the row is visible afterward.
1234    #[inline]
1235    pub fn move_up(&mut self, n: usize, extend: bool) -> bool {
1236        let r = if self.direction == ListDirection::TopToBottom {
1237            self.selection
1238                .move_up(n, self.rows.saturating_sub(1), extend)
1239        } else {
1240            self.selection
1241                .move_down(n, self.rows.saturating_sub(1), extend)
1242        };
1243        let s = self.scroll_to(self.selection.lead().expect("row"));
1244        r || s
1245    }
1246
1247    /// Move the selection down n rows.
1248    /// Ensures the row is visible afterward.
1249    #[inline]
1250    pub fn move_down(&mut self, n: usize, extend: bool) -> bool {
1251        let r = if self.direction == ListDirection::TopToBottom {
1252            self.selection
1253                .move_down(n, self.rows.saturating_sub(1), extend)
1254        } else {
1255            self.selection
1256                .move_up(n, self.rows.saturating_sub(1), extend)
1257        };
1258        let s = self.scroll_to(self.selection.lead().expect("row"));
1259        r || s
1260    }
1261}
1262
1263pub mod selection {
1264    //!
1265    //! Different selection models.
1266    //!
1267
1268    use crate::event::{HandleEvent, MouseOnly, Outcome, Regular, ct_event};
1269    use crate::list::{ListSelection, ListState};
1270    use rat_event::event_flow;
1271    use rat_focus::HasFocus;
1272    use rat_ftable::TableSelection;
1273    use rat_scrolled::ScrollAreaState;
1274    use rat_scrolled::event::ScrollOutcome;
1275    use ratatui_crossterm::crossterm::event::{Event, KeyModifiers};
1276    use std::cmp::max;
1277
1278    /// No selection
1279    pub type NoSelection = rat_ftable::selection::NoSelection;
1280
1281    impl ListSelection for NoSelection {
1282        fn count(&self) -> usize {
1283            0
1284        }
1285
1286        #[inline]
1287        fn is_selected(&self, _n: usize) -> bool {
1288            false
1289        }
1290
1291        #[inline]
1292        fn lead_selection(&self) -> Option<usize> {
1293            None
1294        }
1295
1296        fn validate_rows(&mut self, _rows: usize) {}
1297
1298        fn items_added(&mut self, _pos: usize, _n: usize) {}
1299
1300        fn items_removed(&mut self, _pos: usize, _n: usize, _rows: usize) {}
1301    }
1302
1303    impl HandleEvent<Event, Regular, Outcome> for ListState<NoSelection> {
1304        fn handle(&mut self, event: &Event, _keymap: Regular) -> Outcome {
1305            let res = if self.is_focused() {
1306                match event {
1307                    ct_event!(keycode press Down) => self.scroll_down(1).into(),
1308                    ct_event!(keycode press Up) => self.scroll_up(1).into(),
1309                    ct_event!(keycode press CONTROL-Down) | ct_event!(keycode press End) => {
1310                        self.scroll_to(self.max_offset()).into()
1311                    }
1312                    ct_event!(keycode press CONTROL-Up) | ct_event!(keycode press Home) => {
1313                        self.scroll_to(0).into()
1314                    }
1315                    ct_event!(keycode press PageUp) => {
1316                        self.scroll_up(self.page_len().saturating_sub(1)).into()
1317                    }
1318                    ct_event!(keycode press PageDown) => {
1319                        self.scroll_down(self.page_len().saturating_sub(1)).into()
1320                    }
1321                    _ => Outcome::Continue,
1322                }
1323            } else {
1324                Outcome::Continue
1325            };
1326
1327            if res == Outcome::Continue {
1328                self.handle(event, MouseOnly)
1329            } else {
1330                res
1331            }
1332        }
1333    }
1334
1335    impl HandleEvent<Event, MouseOnly, Outcome> for ListState<NoSelection> {
1336        fn handle(&mut self, event: &Event, _keymap: MouseOnly) -> Outcome {
1337            if self.is_obscured() {
1338                return Outcome::Continue;
1339            }
1340
1341            let mut sas = ScrollAreaState::new()
1342                .area(self.inner)
1343                .v_scroll(&mut self.scroll);
1344            let r = match sas.handle(event, MouseOnly) {
1345                ScrollOutcome::Up(v) => self.scroll_up(v),
1346                ScrollOutcome::Down(v) => self.scroll_down(v),
1347                ScrollOutcome::VPos(v) => self.set_offset(v),
1348                ScrollOutcome::Left(_) => false,
1349                ScrollOutcome::Right(_) => false,
1350                ScrollOutcome::HPos(_) => false,
1351
1352                ScrollOutcome::Continue => false,
1353                ScrollOutcome::Unchanged => false,
1354                ScrollOutcome::Changed => true,
1355            };
1356            if r {
1357                return Outcome::Changed;
1358            }
1359
1360            Outcome::Unchanged
1361        }
1362    }
1363
1364    /// Single element selection.
1365    pub type RowSelection = rat_ftable::selection::RowSelection;
1366
1367    impl ListSelection for RowSelection {
1368        fn count(&self) -> usize {
1369            <Self as TableSelection>::count(self)
1370        }
1371
1372        #[inline]
1373        fn is_selected(&self, n: usize) -> bool {
1374            <Self as TableSelection>::is_selected_row(self, n)
1375        }
1376
1377        #[inline]
1378        fn lead_selection(&self) -> Option<usize> {
1379            <Self as TableSelection>::lead_selection(self).map(|(_, y)| y)
1380        }
1381
1382        fn scroll_selected(&self) -> bool {
1383            RowSelection::scroll_selected(self)
1384        }
1385
1386        fn validate_rows(&mut self, rows: usize) {
1387            <Self as TableSelection>::validate_rows(self, rows);
1388        }
1389
1390        fn items_added(&mut self, pos: usize, n: usize) {
1391            <Self as TableSelection>::items_added(self, pos, n);
1392        }
1393
1394        fn items_removed(&mut self, pos: usize, n: usize, rows: usize) {
1395            <Self as TableSelection>::items_removed(self, pos, n, rows);
1396        }
1397    }
1398
1399    impl HandleEvent<Event, Regular, Outcome> for ListState<RowSelection> {
1400        fn handle(&mut self, event: &Event, _keymap: Regular) -> Outcome {
1401            let res = if self.is_focused() {
1402                match event {
1403                    ct_event!(keycode press Down) => self.move_down(1).into(),
1404                    ct_event!(keycode press Up) => self.move_up(1).into(),
1405                    ct_event!(keycode press CONTROL-Down) | ct_event!(keycode press End) => {
1406                        self.move_to(self.rows.saturating_sub(1)).into()
1407                    }
1408                    ct_event!(keycode press CONTROL-Up) | ct_event!(keycode press Home) => {
1409                        self.move_to(0).into()
1410                    }
1411                    ct_event!(keycode press PageUp) => self
1412                        .move_up(max(1, self.page_len().saturating_sub(1)))
1413                        .into(),
1414                    ct_event!(keycode press PageDown) => self
1415                        .move_down(max(1, self.page_len().saturating_sub(1)))
1416                        .into(),
1417                    _ => Outcome::Continue,
1418                }
1419            } else {
1420                Outcome::Continue
1421            };
1422
1423            if res == Outcome::Continue {
1424                self.handle(event, MouseOnly)
1425            } else {
1426                res
1427            }
1428        }
1429    }
1430
1431    impl HandleEvent<Event, MouseOnly, Outcome> for ListState<RowSelection> {
1432        fn handle(&mut self, event: &Event, _keymap: MouseOnly) -> Outcome {
1433            if self.is_obscured() {
1434                return Outcome::Continue;
1435            }
1436
1437            event_flow!(
1438                return match event {
1439                    ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1440                        self.move_to(self.row_at_drag((m.column, m.row))).into()
1441                    }
1442                    ct_event!(mouse down Left for column, row) => {
1443                        if self.inner.contains((*column, *row).into()) {
1444                            if let Some(new_row) = self.row_at_clicked((*column, *row)) {
1445                                self.move_to(new_row).into()
1446                            } else {
1447                                Outcome::Continue
1448                            }
1449                        } else {
1450                            Outcome::Continue
1451                        }
1452                    }
1453
1454                    _ => Outcome::Continue,
1455                }
1456            );
1457
1458            let mut sas = ScrollAreaState::new()
1459                .area(self.inner)
1460                .v_scroll(&mut self.scroll);
1461            let r = match sas.handle(event, MouseOnly) {
1462                ScrollOutcome::Up(v) => {
1463                    if ListSelection::scroll_selected(&self.selection) {
1464                        self.move_up(1)
1465                    } else {
1466                        self.scroll_up(v)
1467                    }
1468                }
1469                ScrollOutcome::Down(v) => {
1470                    if ListSelection::scroll_selected(&self.selection) {
1471                        self.move_down(1)
1472                    } else {
1473                        self.scroll_down(v)
1474                    }
1475                }
1476                ScrollOutcome::VPos(v) => {
1477                    if ListSelection::scroll_selected(&self.selection) {
1478                        self.move_to(self.remap_offset_selection(v))
1479                    } else {
1480                        self.set_offset(v)
1481                    }
1482                }
1483                ScrollOutcome::Left(_) => false,
1484                ScrollOutcome::Right(_) => false,
1485                ScrollOutcome::HPos(_) => false,
1486
1487                ScrollOutcome::Continue => false,
1488                ScrollOutcome::Unchanged => false,
1489                ScrollOutcome::Changed => true,
1490            };
1491            if r {
1492                return Outcome::Changed;
1493            }
1494
1495            Outcome::Continue
1496        }
1497    }
1498
1499    pub type RowSetSelection = rat_ftable::selection::RowSetSelection;
1500
1501    impl ListSelection for RowSetSelection {
1502        fn count(&self) -> usize {
1503            <Self as TableSelection>::count(self)
1504        }
1505
1506        fn is_selected(&self, n: usize) -> bool {
1507            <Self as TableSelection>::is_selected_row(self, n)
1508        }
1509
1510        fn lead_selection(&self) -> Option<usize> {
1511            <Self as TableSelection>::lead_selection(self).map(|(_, y)| y)
1512        }
1513
1514        fn validate_rows(&mut self, rows: usize) {
1515            <Self as TableSelection>::validate_rows(self, rows);
1516        }
1517
1518        fn items_added(&mut self, pos: usize, n: usize) {
1519            <Self as TableSelection>::items_added(self, pos, n);
1520        }
1521
1522        fn items_removed(&mut self, pos: usize, n: usize, rows: usize) {
1523            <Self as TableSelection>::items_removed(self, pos, n, rows);
1524        }
1525    }
1526
1527    impl HandleEvent<Event, Regular, Outcome> for ListState<RowSetSelection> {
1528        fn handle(&mut self, event: &Event, _: Regular) -> Outcome {
1529            let res = if self.is_focused() {
1530                match event {
1531                    ct_event!(keycode press Down) => self.move_down(1, false).into(),
1532                    ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
1533                    ct_event!(keycode press Up) => self.move_up(1, false).into(),
1534                    ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
1535                    ct_event!(keycode press CONTROL-Down) | ct_event!(keycode press End) => {
1536                        self.move_to(self.rows.saturating_sub(1), false).into()
1537                    }
1538                    ct_event!(keycode press SHIFT-End) => {
1539                        self.move_to(self.rows.saturating_sub(1), true).into()
1540                    }
1541                    ct_event!(keycode press CONTROL-Up) | ct_event!(keycode press Home) => {
1542                        self.move_to(0, false).into()
1543                    }
1544                    ct_event!(keycode press SHIFT-Home) => self.move_to(0, true).into(),
1545
1546                    ct_event!(keycode press PageUp) => self
1547                        .move_up(max(1, self.page_len().saturating_sub(1)), false)
1548                        .into(),
1549                    ct_event!(keycode press SHIFT-PageUp) => self
1550                        .move_up(max(1, self.page_len().saturating_sub(1)), true)
1551                        .into(),
1552                    ct_event!(keycode press PageDown) => self
1553                        .move_down(max(1, self.page_len().saturating_sub(1)), false)
1554                        .into(),
1555                    ct_event!(keycode press SHIFT-PageDown) => self
1556                        .move_down(max(1, self.page_len().saturating_sub(1)), true)
1557                        .into(),
1558                    _ => Outcome::Continue,
1559                }
1560            } else {
1561                Outcome::Continue
1562            };
1563
1564            if res == Outcome::Continue {
1565                self.handle(event, MouseOnly)
1566            } else {
1567                res
1568            }
1569        }
1570    }
1571
1572    impl HandleEvent<Event, MouseOnly, Outcome> for ListState<RowSetSelection> {
1573        fn handle(&mut self, event: &Event, _: MouseOnly) -> Outcome {
1574            if self.is_obscured() {
1575                return Outcome::Continue;
1576            }
1577
1578            event_flow!(
1579                return match event {
1580                    ct_event!(mouse any for m) | ct_event!(mouse any CONTROL for m)
1581                        if self.mouse.drag(self.inner, m)
1582                            || self.mouse.drag2(self.inner, m, KeyModifiers::CONTROL) =>
1583                    {
1584                        self.move_to(self.row_at_drag((m.column, m.row)), true)
1585                            .into()
1586                    }
1587                    ct_event!(mouse down Left for column, row) => {
1588                        let pos = (*column, *row);
1589                        if self.inner.contains(pos.into()) {
1590                            if let Some(new_row) = self.row_at_clicked(pos) {
1591                                self.move_to(new_row, false).into()
1592                            } else {
1593                                Outcome::Continue
1594                            }
1595                        } else {
1596                            Outcome::Continue
1597                        }
1598                    }
1599                    ct_event!(mouse down ALT-Left for column, row) => {
1600                        let pos = (*column, *row);
1601                        if self.area.contains(pos.into()) {
1602                            if let Some(new_row) = self.row_at_clicked(pos) {
1603                                self.move_to(new_row, true).into()
1604                            } else {
1605                                Outcome::Continue
1606                            }
1607                        } else {
1608                            Outcome::Continue
1609                        }
1610                    }
1611                    ct_event!(mouse down CONTROL-Left for column, row) => {
1612                        let pos = (*column, *row);
1613                        if self.area.contains(pos.into()) {
1614                            if let Some(new_row) = self.row_at_clicked(pos) {
1615                                self.retire_selection();
1616                                if self.selection.is_selected_row(new_row) {
1617                                    self.selection.remove(new_row);
1618                                } else {
1619                                    self.move_to(new_row, true);
1620                                }
1621                                Outcome::Changed
1622                            } else {
1623                                Outcome::Continue
1624                            }
1625                        } else {
1626                            Outcome::Continue
1627                        }
1628                    }
1629                    _ => Outcome::Continue,
1630                }
1631            );
1632
1633            let mut sas = ScrollAreaState::new()
1634                .area(self.inner)
1635                .v_scroll(&mut self.scroll);
1636            let r = match sas.handle(event, MouseOnly) {
1637                ScrollOutcome::Up(v) => self.scroll_up(v),
1638                ScrollOutcome::Down(v) => self.scroll_down(v),
1639                ScrollOutcome::VPos(v) => self.set_offset(v),
1640                ScrollOutcome::Left(_) => false,
1641                ScrollOutcome::Right(_) => false,
1642                ScrollOutcome::HPos(_) => false,
1643
1644                ScrollOutcome::Continue => false,
1645                ScrollOutcome::Unchanged => false,
1646                ScrollOutcome::Changed => true,
1647            };
1648            if r {
1649                return Outcome::Changed;
1650            }
1651
1652            Outcome::Continue
1653        }
1654    }
1655}
1656
1657/// Handle events for the popup.
1658/// Call before other handlers to deal with intersections
1659/// with other widgets.
1660pub fn handle_events(state: &mut ListState, focus: bool, event: &Event) -> Outcome {
1661    state.focus.set(focus);
1662    HandleEvent::handle(state, event, Regular)
1663}
1664
1665/// Handle only mouse-events.
1666pub fn handle_mouse_events(state: &mut ListState, event: &Event) -> Outcome {
1667    HandleEvent::handle(state, event, MouseOnly)
1668}