Skip to main content

rat_widget/
list.rs

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