1use 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::widgets::StatefulWidget;
18use ratatui_crossterm::crossterm::event::Event;
19use ratatui_widgets::block::Block;
20use ratatui_widgets::list::{ListDirection, ListItem};
21use ratatui_widgets::table::HighlightSpacing;
22use std::cmp::min;
23use std::collections::HashSet;
24use std::marker::PhantomData;
25
26pub mod edit;
27
28pub trait ListSelection {
30 fn count(&self) -> usize;
32
33 fn is_selected(&self, n: usize) -> bool;
35
36 fn lead_selection(&self) -> Option<usize>;
38
39 fn scroll_selected(&self) -> bool {
41 false
42 }
43
44 #[allow(unused_variables)]
46 fn validate_rows(&mut self, rows: usize) {}
47
48 #[allow(unused_variables)]
50 fn items_added(&mut self, pos: usize, n: usize) {}
51
52 #[allow(unused_variables)]
54 fn items_removed(&mut self, pos: usize, n: usize, rows: usize) {}
55}
56
57#[derive(Debug, Default, Clone)]
62pub struct List<'a, Selection = RowSelection> {
63 items: Vec<ListItem<'a>>,
64
65 style: Style,
66 block: Option<Block<'a>>,
67 scroll: Option<Scroll<'a>>,
68 scroll_padding: usize,
69
70 select_style: Option<Style>,
71 focus_style: Option<Style>,
72 direction: ListDirection,
73 highlight_spacing: HighlightSpacing,
74 highlight_symbol: Option<&'static str>,
75 repeat_highlight_symbol: bool,
76
77 _phantom: PhantomData<Selection>,
78}
79
80#[derive(Debug, Clone)]
82pub struct ListStyle {
83 pub style: Style,
85 pub block: Option<Block<'static>>,
86 pub border_style: Option<Style>,
87 pub title_style: Option<Style>,
88 pub scroll: Option<ScrollStyle>,
89 pub scroll_padding: Option<usize>,
90
91 pub select: Option<Style>,
93 pub focus: Option<Style>,
95
96 pub highlight_spacing: Option<HighlightSpacing>,
97 pub highlight_symbol: Option<&'static str>,
98 pub repeat_highlight_symbol: Option<bool>,
99
100 pub non_exhaustive: NonExhaustive,
101}
102
103#[derive(Debug, PartialEq, Eq)]
105pub struct ListState<Selection = RowSelection> {
106 pub area: Rect,
109 pub inner: Rect,
112 pub row_areas: Vec<Rect>,
115
116 pub rows: usize,
119 pub scroll: ScrollState,
122
123 pub focus: FocusFlag,
126 pub selection: Selection,
129
130 pub mouse: MouseFlags,
133
134 pub non_exhaustive: NonExhaustive,
135}
136
137impl Default for ListStyle {
138 fn default() -> Self {
139 Self {
140 style: Default::default(),
141 select: Default::default(),
142 focus: Default::default(),
143 block: Default::default(),
144 border_style: Default::default(),
145 title_style: Default::default(),
146 scroll: Default::default(),
147 highlight_spacing: Default::default(),
148 highlight_symbol: Default::default(),
149 repeat_highlight_symbol: Default::default(),
150 scroll_padding: Default::default(),
151 non_exhaustive: NonExhaustive,
152 }
153 }
154}
155
156impl<'a, Selection> List<'a, Selection> {
157 pub fn new<T>(items: T) -> Self
159 where
160 T: IntoIterator,
161 T::Item: Into<ListItem<'a>>,
162 {
163 let items = items.into_iter().map(|v| v.into()).collect();
164
165 Self {
166 block: None,
167 scroll: None,
168 items,
169 style: Default::default(),
170 select_style: Default::default(),
171 focus_style: Default::default(),
172 direction: Default::default(),
173 highlight_spacing: Default::default(),
174 highlight_symbol: Default::default(),
175 repeat_highlight_symbol: false,
176 scroll_padding: 0,
177 _phantom: Default::default(),
178 }
179 }
180
181 pub fn items<T>(mut self, items: T) -> Self
183 where
184 T: IntoIterator,
185 T::Item: Into<ListItem<'a>>,
186 {
187 let items = items.into_iter().map(|v| v.into()).collect();
188 self.items = items;
189 self
190 }
191
192 #[inline]
194 pub fn block(mut self, block: Block<'a>) -> Self {
195 self.block = Some(block);
196 self.block = self.block.map(|v| v.style(self.style));
197 self
198 }
199
200 #[inline]
202 pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
203 self.scroll = Some(scroll);
204 self
205 }
206
207 #[inline]
209 pub fn styles_opt(self, styles: Option<ListStyle>) -> Self {
210 if let Some(styles) = styles {
211 self.styles(styles)
212 } else {
213 self
214 }
215 }
216
217 #[inline]
219 pub fn styles(mut self, styles: ListStyle) -> Self {
220 self.style = styles.style;
221 if styles.block.is_some() {
222 self.block = styles.block;
223 }
224 if let Some(border_style) = styles.border_style {
225 self.block = self.block.map(|v| v.border_style(border_style));
226 }
227 if let Some(title_style) = styles.title_style {
228 self.block = self.block.map(|v| v.title_style(title_style));
229 }
230 self.block = self.block.map(|v| v.style(self.style));
231
232 if let Some(styles) = styles.scroll {
233 self.scroll = self.scroll.map(|v| v.styles(styles));
234 }
235 if let Some(scroll_padding) = styles.scroll_padding {
236 self.scroll_padding = scroll_padding;
237 }
238
239 if styles.select.is_some() {
240 self.select_style = styles.select;
241 }
242 if styles.focus.is_some() {
243 self.focus_style = styles.focus;
244 }
245 if let Some(highlight_spacing) = styles.highlight_spacing {
246 self.highlight_spacing = highlight_spacing;
247 }
248 if let Some(highlight_symbol) = styles.highlight_symbol {
249 self.highlight_symbol = Some(highlight_symbol);
250 }
251 if let Some(repeat_highlight_symbol) = styles.repeat_highlight_symbol {
252 self.repeat_highlight_symbol = repeat_highlight_symbol;
253 }
254 self
255 }
256
257 #[inline]
259 pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
260 self.style = style.into();
261 self.block = self.block.map(|v| v.style(self.style));
262 self
263 }
264
265 #[inline]
267 pub fn select_style<S: Into<Style>>(mut self, select_style: S) -> Self {
268 self.select_style = Some(select_style.into());
269 self
270 }
271
272 #[inline]
274 pub fn focus_style<S: Into<Style>>(mut self, focus_style: S) -> Self {
275 self.focus_style = Some(focus_style.into());
276 self
277 }
278
279 #[inline]
281 pub fn direction(mut self, direction: ListDirection) -> Self {
282 self.direction = direction;
283 self
284 }
285
286 #[inline]
288 pub fn len(&self) -> usize {
289 self.items.len()
290 }
291
292 #[inline]
294 pub fn is_empty(&self) -> bool {
295 self.items.is_empty()
296 }
297}
298
299impl<'a, Item, Selection> FromIterator<Item> for List<'a, Selection>
300where
301 Item: Into<ListItem<'a>>,
302{
303 fn from_iter<Iter: IntoIterator<Item = Item>>(iter: Iter) -> Self {
304 Self::new(iter)
305 }
306}
307
308impl<Selection: ListSelection> StatefulWidget for List<'_, Selection> {
309 type State = ListState<Selection>;
310
311 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
312 render_list(self, area, buf, state)
313 }
314}
315
316fn render_list<Selection: ListSelection>(
317 widget: List<'_, Selection>,
318 area: Rect,
319 buf: &mut Buffer,
320 state: &mut ListState<Selection>,
321) {
322 state.area = area;
323 state.rows = widget.items.len();
324 state.selection.validate_rows(state.rows);
325
326 let sa = ScrollArea::new()
327 .block(widget.block.as_ref())
328 .v_scroll(widget.scroll.as_ref());
329 state.inner = sa.inner(area, None, Some(&state.scroll));
330
331 state.row_areas.clear();
333 let mut item_area = Rect::new(state.inner.x, state.inner.y, state.inner.width, 1);
334 let mut total_height = 0;
335 for item in widget.items.iter().skip(state.offset()) {
336 item_area.height = item.height() as u16;
337
338 state.row_areas.push(item_area);
339
340 item_area.y += item_area.height;
341 total_height += item_area.height;
342 if total_height >= state.inner.height {
343 break;
344 }
345 }
346 if total_height < state.inner.height {
347 state.scroll.set_page_len(
348 state.row_areas.len() + state.inner.height as usize - total_height as usize,
349 );
350 } else {
351 state.scroll.set_page_len(state.row_areas.len());
352 }
353
354 let focus_style = widget.focus_style.unwrap_or(revert_style(widget.style));
355 let select_style = widget
356 .select_style
357 .unwrap_or(fallback_select_style(widget.style));
358
359 let mut n = 0;
361 let mut height = 0;
362 for item in widget.items.iter().rev() {
363 height += item.height();
364 if height > state.inner.height as usize {
365 break;
366 }
367 n += 1;
368 }
369 state.scroll.set_max_offset(state.rows.saturating_sub(n));
370
371 let (style, select_style) = if state.is_focused() {
372 (widget.style, focus_style)
373 } else {
374 (widget.style, select_style)
375 };
376
377 sa.render(
378 area,
379 buf,
380 &mut ScrollAreaState::new().v_scroll(&mut state.scroll),
381 );
382
383 let items = widget
385 .items
386 .into_iter()
387 .enumerate()
388 .map(|(i, v)| {
389 if state.selection.is_selected(i) {
390 v.style(style.patch(select_style))
391 } else {
392 v.style(style)
393 }
394 })
395 .collect::<Vec<_>>();
396
397 let mut list_state =
398 ratatui_widgets::list::ListState::default().with_offset(state.scroll.offset());
399
400 let mut list = ratatui_widgets::list::List::default()
401 .items(items)
402 .style(widget.style)
403 .direction(widget.direction)
404 .highlight_spacing(widget.highlight_spacing)
405 .repeat_highlight_symbol(widget.repeat_highlight_symbol)
406 .scroll_padding(widget.scroll_padding);
407 if let Some(highlight_symbol) = widget.highlight_symbol {
408 list = list.highlight_symbol(highlight_symbol);
409 }
410 list.render(state.inner, buf, &mut list_state);
411}
412
413impl<Selection> HasFocus for ListState<Selection> {
414 fn build(&self, builder: &mut FocusBuilder) {
415 builder.leaf_widget(self);
416 }
417
418 #[inline]
419 fn focus(&self) -> FocusFlag {
420 self.focus.clone()
421 }
422
423 #[inline]
424 fn area(&self) -> Rect {
425 self.area
426 }
427}
428
429impl<Selection> HasScreenCursor for ListState<Selection> {
430 fn screen_cursor(&self) -> Option<(u16, u16)> {
431 None
432 }
433}
434
435impl<Selection: Default> Default for ListState<Selection> {
436 fn default() -> Self {
437 Self {
438 area: Default::default(),
439 inner: Default::default(),
440 row_areas: Default::default(),
441 rows: Default::default(),
442 scroll: Default::default(),
443 focus: Default::default(),
444 selection: Default::default(),
445 mouse: Default::default(),
446 non_exhaustive: NonExhaustive,
447 }
448 }
449}
450
451impl<Selection: Clone> Clone for ListState<Selection> {
452 fn clone(&self) -> Self {
453 Self {
454 area: self.area,
455 inner: self.inner,
456 row_areas: self.row_areas.clone(),
457 rows: self.rows,
458 scroll: self.scroll.clone(),
459 focus: self.focus.new_instance(),
460 selection: self.selection.clone(),
461 mouse: Default::default(),
462 non_exhaustive: NonExhaustive,
463 }
464 }
465}
466
467impl<Selection> RelocatableState for ListState<Selection> {
468 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
469 self.area.relocate(shift, clip);
470 self.inner.relocate(shift, clip);
471 relocate_areas(self.row_areas.as_mut_slice(), shift, clip);
472 self.scroll.relocate(shift, clip);
473 }
474}
475
476impl<Selection: ListSelection> ListState<Selection> {
477 pub fn new() -> Self
479 where
480 Selection: Default,
481 {
482 Default::default()
483 }
484
485 pub fn named(name: &str) -> Self
487 where
488 Selection: Default,
489 {
490 let mut z = Self::default();
491 z.focus = z.focus.with_name(name);
492 z
493 }
494
495 #[inline]
496 pub fn rows(&self) -> usize {
497 self.rows
498 }
499
500 pub fn items_added(&mut self, pos: usize, n: usize) {
504 self.rows = self.rows.saturating_add(n);
505 self.scroll.items_added(pos, n);
506 self.selection.items_added(pos, n);
507 }
508
509 pub fn items_removed(&mut self, pos: usize, n: usize) {
513 self.rows = self.rows.saturating_sub(n);
514 self.scroll.items_removed(pos, n);
515 self.selection.items_removed(pos, n, self.rows);
516 }
517
518 pub fn rows_changed(&mut self, rows: usize) {
528 self.selection.validate_rows(rows);
529 self.scroll
530 .set_max_offset(self.rows.saturating_sub(self.inner.height as usize));
531 self.scroll.offset = self.scroll.limited_offset(self.scroll.offset);
532 self.rows = rows;
533 }
534
535 #[inline]
536 pub fn clear_offset(&mut self) {
537 self.scroll.set_offset(0);
538 }
539
540 #[inline]
541 pub fn max_offset(&self) -> usize {
542 self.scroll.max_offset()
543 }
544
545 #[inline]
546 pub fn set_max_offset(&mut self, max: usize) {
547 self.scroll.set_max_offset(max);
548 }
549
550 #[inline]
551 pub fn offset(&self) -> usize {
552 self.scroll.offset()
553 }
554
555 #[inline]
556 pub fn set_offset(&mut self, offset: usize) -> bool {
557 self.scroll.set_offset(offset)
558 }
559
560 #[inline]
561 pub fn page_len(&self) -> usize {
562 self.scroll.page_len()
563 }
564
565 pub fn scroll_by(&self) -> usize {
566 self.scroll.scroll_by()
567 }
568
569 #[inline]
571 pub fn scroll_to_selected(&mut self) -> bool {
572 if let Some(selected) = self.selection.lead_selection() {
573 self.scroll_to(selected)
574 } else {
575 false
576 }
577 }
578
579 #[inline]
580 pub fn scroll_to(&mut self, pos: usize) -> bool {
581 if pos >= self.offset() + self.page_len() {
582 self.set_offset(pos - self.page_len() + 1)
583 } else if pos < self.offset() {
584 self.set_offset(pos)
585 } else {
586 false
587 }
588 }
589
590 #[inline]
591 pub fn scroll_up(&mut self, n: usize) -> bool {
592 self.scroll.scroll_up(n)
593 }
594
595 #[inline]
596 pub fn scroll_down(&mut self, n: usize) -> bool {
597 self.scroll.scroll_down(n)
598 }
599}
600
601impl<Selection: ListSelection> ListState<Selection> {
602 pub fn row_area(&self, row: usize) -> Option<Rect> {
604 if row < self.scroll.offset() || row >= self.scroll.offset() + self.scroll.page_len() {
605 return None;
606 }
607 let row = row - self.scroll.offset;
608 if row >= self.row_areas.len() {
609 return None;
610 }
611 Some(self.row_areas[row - self.scroll.offset])
612 }
613
614 #[inline]
615 pub fn row_at_clicked(&self, pos: (u16, u16)) -> Option<usize> {
616 self.mouse
617 .row_at(&self.row_areas, pos.1)
618 .map(|v| self.scroll.offset() + v)
619 }
620
621 #[inline]
623 pub fn row_at_drag(&self, pos: (u16, u16)) -> usize {
624 match self.mouse.row_at_drag(self.inner, &self.row_areas, pos.1) {
625 Ok(v) => self.scroll.offset() + v,
626 Err(v) if v <= 0 => self.scroll.offset().saturating_sub((-v) as usize),
627 Err(v) => self.scroll.offset() + self.row_areas.len() + v as usize,
628 }
629 }
630}
631
632impl ListState<RowSelection> {
633 #[inline]
635 pub fn set_scroll_selection(&mut self, scroll: bool) {
636 self.selection.set_scroll_selected(scroll);
637 }
638
639 pub(crate) fn remap_offset_selection(&self, offset: usize) -> usize {
643 if self.scroll.max_offset() > 0 {
644 (self.rows * offset) / self.scroll.max_offset()
645 } else {
646 0 }
648 }
649
650 #[inline]
652 pub fn clear_selection(&mut self) {
653 self.selection.clear();
654 }
655
656 #[inline]
658 pub fn has_selection(&mut self) -> bool {
659 self.selection.has_selection()
660 }
661
662 #[inline]
664 pub fn selected(&self) -> Option<usize> {
665 self.selection.lead_selection()
666 }
667
668 #[inline]
671 pub fn selected_checked(&self) -> Option<usize> {
672 self.selection.lead_selection().filter(|v| *v < self.rows)
673 }
674
675 #[inline]
676 pub fn select(&mut self, row: Option<usize>) -> bool {
677 self.selection.select(row)
678 }
679
680 #[inline]
683 pub fn move_to(&mut self, row: usize) -> bool {
684 let r = self.selection.move_to(row, self.rows.saturating_sub(1));
685 let s = self.scroll_to(self.selection.selected().expect("row"));
686 r || s
687 }
688
689 #[inline]
692 pub fn move_up(&mut self, n: usize) -> bool {
693 let r = self.selection.move_up(n, self.rows.saturating_sub(1));
694 let s = self.scroll_to(self.selection.selected().expect("row"));
695 r || s
696 }
697
698 #[inline]
701 pub fn move_down(&mut self, n: usize) -> bool {
702 let r = self.selection.move_down(n, self.rows.saturating_sub(1));
703 let s = self.scroll_to(self.selection.selected().expect("row"));
704 r || s
705 }
706}
707
708impl ListState<RowSetSelection> {
709 #[inline]
711 pub fn clear_selection(&mut self) {
712 self.selection.clear();
713 }
714
715 #[inline]
717 pub fn has_selection(&mut self) -> bool {
718 self.selection.has_selection()
719 }
720
721 #[inline]
722 pub fn selected(&self) -> HashSet<usize> {
723 self.selection.selected()
724 }
725
726 #[inline]
733 pub fn set_lead(&mut self, row: Option<usize>, extend: bool) -> bool {
734 if let Some(row) = row {
735 self.selection
736 .set_lead(Some(min(row, self.rows.saturating_sub(1))), extend)
737 } else {
738 self.selection.set_lead(None, extend)
739 }
740 }
741
742 #[inline]
744 pub fn lead(&self) -> Option<usize> {
745 self.selection.lead()
746 }
747
748 #[inline]
750 pub fn anchor(&self) -> Option<usize> {
751 self.selection.anchor()
752 }
753
754 #[inline]
756 pub fn set_lead_clamped(&mut self, lead: usize, max: usize, extend: bool) {
757 self.selection.move_to(lead, max, extend);
758 }
759
760 #[inline]
763 pub fn retire_selection(&mut self) {
764 self.selection.retire_selection();
765 }
766
767 #[inline]
769 pub fn add_selected(&mut self, idx: usize) {
770 self.selection.add(idx);
771 }
772
773 #[inline]
776 pub fn remove_selected(&mut self, idx: usize) {
777 self.selection.remove(idx);
778 }
779
780 #[inline]
783 pub fn move_to(&mut self, row: usize, extend: bool) -> bool {
784 let r = self
785 .selection
786 .move_to(row, self.rows.saturating_sub(1), extend);
787 let s = self.scroll_to(self.selection.lead().expect("row"));
788 r || s
789 }
790
791 #[inline]
794 pub fn move_up(&mut self, n: usize, extend: bool) -> bool {
795 let r = self
796 .selection
797 .move_up(n, self.rows.saturating_sub(1), extend);
798 let s = self.scroll_to(self.selection.lead().expect("row"));
799 r || s
800 }
801
802 #[inline]
805 pub fn move_down(&mut self, n: usize, extend: bool) -> bool {
806 let r = self
807 .selection
808 .move_down(n, self.rows.saturating_sub(1), extend);
809 let s = self.scroll_to(self.selection.lead().expect("row"));
810 r || s
811 }
812}
813
814pub mod selection {
815 use crate::event::{HandleEvent, MouseOnly, Outcome, Regular, ct_event};
820 use crate::list::{ListSelection, ListState};
821 use rat_event::event_flow;
822 use rat_focus::HasFocus;
823 use rat_ftable::TableSelection;
824 use rat_scrolled::ScrollAreaState;
825 use rat_scrolled::event::ScrollOutcome;
826 use ratatui_crossterm::crossterm::event::{Event, KeyModifiers};
827
828 pub type NoSelection = rat_ftable::selection::NoSelection;
830
831 impl ListSelection for NoSelection {
832 fn count(&self) -> usize {
833 0
834 }
835
836 #[inline]
837 fn is_selected(&self, _n: usize) -> bool {
838 false
839 }
840
841 #[inline]
842 fn lead_selection(&self) -> Option<usize> {
843 None
844 }
845
846 fn validate_rows(&mut self, _rows: usize) {}
847
848 fn items_added(&mut self, _pos: usize, _n: usize) {}
849
850 fn items_removed(&mut self, _pos: usize, _n: usize, _rows: usize) {}
851 }
852
853 impl HandleEvent<Event, Regular, Outcome> for ListState<NoSelection> {
854 fn handle(&mut self, event: &Event, _keymap: Regular) -> Outcome {
855 let res = if self.is_focused() {
856 match event {
857 ct_event!(keycode press Down) => self.scroll_down(1).into(),
858 ct_event!(keycode press Up) => self.scroll_up(1).into(),
859 ct_event!(keycode press CONTROL-Down) | ct_event!(keycode press End) => {
860 self.scroll_to(self.max_offset()).into()
861 }
862 ct_event!(keycode press CONTROL-Up) | ct_event!(keycode press Home) => {
863 self.scroll_to(0).into()
864 }
865 ct_event!(keycode press PageUp) => {
866 self.scroll_up(self.page_len().saturating_sub(1)).into()
867 }
868 ct_event!(keycode press PageDown) => {
869 self.scroll_down(self.page_len().saturating_sub(1)).into()
870 }
871 _ => Outcome::Continue,
872 }
873 } else {
874 Outcome::Continue
875 };
876
877 if res == Outcome::Continue {
878 self.handle(event, MouseOnly)
879 } else {
880 res
881 }
882 }
883 }
884
885 impl HandleEvent<Event, MouseOnly, Outcome> for ListState<NoSelection> {
886 fn handle(&mut self, event: &Event, _keymap: MouseOnly) -> Outcome {
887 let mut sas = ScrollAreaState::new()
888 .area(self.inner)
889 .v_scroll(&mut self.scroll);
890 let r = match sas.handle(event, MouseOnly) {
891 ScrollOutcome::Up(v) => self.scroll_up(v),
892 ScrollOutcome::Down(v) => self.scroll_down(v),
893 ScrollOutcome::VPos(v) => self.set_offset(v),
894 ScrollOutcome::Left(_) => false,
895 ScrollOutcome::Right(_) => false,
896 ScrollOutcome::HPos(_) => false,
897
898 ScrollOutcome::Continue => false,
899 ScrollOutcome::Unchanged => false,
900 ScrollOutcome::Changed => true,
901 };
902 if r {
903 return Outcome::Changed;
904 }
905
906 Outcome::Unchanged
907 }
908 }
909
910 pub type RowSelection = rat_ftable::selection::RowSelection;
912
913 impl ListSelection for RowSelection {
914 fn count(&self) -> usize {
915 <Self as TableSelection>::count(self)
916 }
917
918 #[inline]
919 fn is_selected(&self, n: usize) -> bool {
920 <Self as TableSelection>::is_selected_row(self, n)
921 }
922
923 #[inline]
924 fn lead_selection(&self) -> Option<usize> {
925 <Self as TableSelection>::lead_selection(self).map(|(_, y)| y)
926 }
927
928 fn scroll_selected(&self) -> bool {
929 RowSelection::scroll_selected(self)
930 }
931
932 fn validate_rows(&mut self, rows: usize) {
933 <Self as TableSelection>::validate_rows(self, rows);
934 }
935
936 fn items_added(&mut self, pos: usize, n: usize) {
937 <Self as TableSelection>::items_added(self, pos, n);
938 }
939
940 fn items_removed(&mut self, pos: usize, n: usize, rows: usize) {
941 <Self as TableSelection>::items_removed(self, pos, n, rows);
942 }
943 }
944
945 impl HandleEvent<Event, Regular, Outcome> for ListState<RowSelection> {
946 fn handle(&mut self, event: &Event, _keymap: Regular) -> Outcome {
947 let res = if self.is_focused() {
948 match event {
949 ct_event!(keycode press Down) => self.move_down(1).into(),
950 ct_event!(keycode press Up) => self.move_up(1).into(),
951 ct_event!(keycode press CONTROL-Down) | ct_event!(keycode press End) => {
952 self.move_to(self.rows.saturating_sub(1)).into()
953 }
954 ct_event!(keycode press CONTROL-Up) | ct_event!(keycode press Home) => {
955 self.move_to(0).into()
956 }
957 ct_event!(keycode press PageUp) => {
958 self.move_up(self.page_len().saturating_sub(1)).into()
959 }
960 ct_event!(keycode press PageDown) => {
961 self.move_down(self.page_len().saturating_sub(1)).into()
962 }
963 _ => Outcome::Continue,
964 }
965 } else {
966 Outcome::Continue
967 };
968
969 if res == Outcome::Continue {
970 self.handle(event, MouseOnly)
971 } else {
972 res
973 }
974 }
975 }
976
977 impl HandleEvent<Event, MouseOnly, Outcome> for ListState<RowSelection> {
978 fn handle(&mut self, event: &Event, _keymap: MouseOnly) -> Outcome {
979 event_flow!(
980 return match event {
981 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
982 self.move_to(self.row_at_drag((m.column, m.row))).into()
983 }
984 ct_event!(mouse down Left for column, row) => {
985 if self.inner.contains((*column, *row).into()) {
986 if let Some(new_row) = self.row_at_clicked((*column, *row)) {
987 self.move_to(new_row).into()
988 } else {
989 Outcome::Continue
990 }
991 } else {
992 Outcome::Continue
993 }
994 }
995
996 _ => Outcome::Continue,
997 }
998 );
999
1000 let mut sas = ScrollAreaState::new()
1001 .area(self.inner)
1002 .v_scroll(&mut self.scroll);
1003 let r = match sas.handle(event, MouseOnly) {
1004 ScrollOutcome::Up(v) => {
1005 if ListSelection::scroll_selected(&self.selection) {
1006 self.move_up(1)
1007 } else {
1008 self.scroll_up(v)
1009 }
1010 }
1011 ScrollOutcome::Down(v) => {
1012 if ListSelection::scroll_selected(&self.selection) {
1013 self.move_down(1)
1014 } else {
1015 self.scroll_down(v)
1016 }
1017 }
1018 ScrollOutcome::VPos(v) => {
1019 if ListSelection::scroll_selected(&self.selection) {
1020 self.move_to(self.remap_offset_selection(v))
1021 } else {
1022 self.set_offset(v)
1023 }
1024 }
1025 ScrollOutcome::Left(_) => false,
1026 ScrollOutcome::Right(_) => false,
1027 ScrollOutcome::HPos(_) => false,
1028
1029 ScrollOutcome::Continue => false,
1030 ScrollOutcome::Unchanged => false,
1031 ScrollOutcome::Changed => true,
1032 };
1033 if r {
1034 return Outcome::Changed;
1035 }
1036
1037 Outcome::Continue
1038 }
1039 }
1040
1041 pub type RowSetSelection = rat_ftable::selection::RowSetSelection;
1042
1043 impl ListSelection for RowSetSelection {
1044 fn count(&self) -> usize {
1045 <Self as TableSelection>::count(self)
1046 }
1047
1048 fn is_selected(&self, n: usize) -> bool {
1049 <Self as TableSelection>::is_selected_row(self, n)
1050 }
1051
1052 fn lead_selection(&self) -> Option<usize> {
1053 <Self as TableSelection>::lead_selection(self).map(|(_, y)| y)
1054 }
1055
1056 fn validate_rows(&mut self, rows: usize) {
1057 <Self as TableSelection>::validate_rows(self, rows);
1058 }
1059
1060 fn items_added(&mut self, pos: usize, n: usize) {
1061 <Self as TableSelection>::items_added(self, pos, n);
1062 }
1063
1064 fn items_removed(&mut self, pos: usize, n: usize, rows: usize) {
1065 <Self as TableSelection>::items_removed(self, pos, n, rows);
1066 }
1067 }
1068
1069 impl HandleEvent<Event, Regular, Outcome> for ListState<RowSetSelection> {
1070 fn handle(&mut self, event: &Event, _: Regular) -> Outcome {
1071 let res = if self.is_focused() {
1072 match event {
1073 ct_event!(keycode press Down) => self.move_down(1, false).into(),
1074 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
1075 ct_event!(keycode press Up) => self.move_up(1, false).into(),
1076 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
1077 ct_event!(keycode press CONTROL-Down) | ct_event!(keycode press End) => {
1078 self.move_to(self.rows.saturating_sub(1), false).into()
1079 }
1080 ct_event!(keycode press SHIFT-End) => {
1081 self.move_to(self.rows.saturating_sub(1), true).into()
1082 }
1083 ct_event!(keycode press CONTROL-Up) | ct_event!(keycode press Home) => {
1084 self.move_to(0, false).into()
1085 }
1086 ct_event!(keycode press SHIFT-Home) => self.move_to(0, true).into(),
1087
1088 ct_event!(keycode press PageUp) => self
1089 .move_up(self.page_len().saturating_sub(1), false)
1090 .into(),
1091 ct_event!(keycode press SHIFT-PageUp) => {
1092 self.move_up(self.page_len().saturating_sub(1), true).into()
1093 }
1094 ct_event!(keycode press PageDown) => self
1095 .move_down(self.page_len().saturating_sub(1), false)
1096 .into(),
1097 ct_event!(keycode press SHIFT-PageDown) => self
1098 .move_down(self.page_len().saturating_sub(1), true)
1099 .into(),
1100 _ => Outcome::Continue,
1101 }
1102 } else {
1103 Outcome::Continue
1104 };
1105
1106 if res == Outcome::Continue {
1107 self.handle(event, MouseOnly)
1108 } else {
1109 res
1110 }
1111 }
1112 }
1113
1114 impl HandleEvent<Event, MouseOnly, Outcome> for ListState<RowSetSelection> {
1115 fn handle(&mut self, event: &Event, _: MouseOnly) -> Outcome {
1116 event_flow!(
1117 return match event {
1118 ct_event!(mouse any for m) | ct_event!(mouse any CONTROL for m)
1119 if self.mouse.drag(self.inner, m)
1120 || self.mouse.drag2(self.inner, m, KeyModifiers::CONTROL) =>
1121 {
1122 self.move_to(self.row_at_drag((m.column, m.row)), true)
1123 .into()
1124 }
1125 ct_event!(mouse down Left for column, row) => {
1126 let pos = (*column, *row);
1127 if self.inner.contains(pos.into()) {
1128 if let Some(new_row) = self.row_at_clicked(pos) {
1129 self.move_to(new_row, false).into()
1130 } else {
1131 Outcome::Continue
1132 }
1133 } else {
1134 Outcome::Continue
1135 }
1136 }
1137 ct_event!(mouse down ALT-Left for column, row) => {
1138 let pos = (*column, *row);
1139 if self.area.contains(pos.into()) {
1140 if let Some(new_row) = self.row_at_clicked(pos) {
1141 self.move_to(new_row, true).into()
1142 } else {
1143 Outcome::Continue
1144 }
1145 } else {
1146 Outcome::Continue
1147 }
1148 }
1149 ct_event!(mouse down CONTROL-Left for column, row) => {
1150 let pos = (*column, *row);
1151 if self.area.contains(pos.into()) {
1152 if let Some(new_row) = self.row_at_clicked(pos) {
1153 self.retire_selection();
1154 if self.selection.is_selected_row(new_row) {
1155 self.selection.remove(new_row);
1156 } else {
1157 self.move_to(new_row, true);
1158 }
1159 Outcome::Changed
1160 } else {
1161 Outcome::Continue
1162 }
1163 } else {
1164 Outcome::Continue
1165 }
1166 }
1167 _ => Outcome::Continue,
1168 }
1169 );
1170
1171 let mut sas = ScrollAreaState::new()
1172 .area(self.inner)
1173 .v_scroll(&mut self.scroll);
1174 let r = match sas.handle(event, MouseOnly) {
1175 ScrollOutcome::Up(v) => self.scroll_up(v),
1176 ScrollOutcome::Down(v) => self.scroll_down(v),
1177 ScrollOutcome::VPos(v) => self.set_offset(v),
1178 ScrollOutcome::Left(_) => false,
1179 ScrollOutcome::Right(_) => false,
1180 ScrollOutcome::HPos(_) => false,
1181
1182 ScrollOutcome::Continue => false,
1183 ScrollOutcome::Unchanged => false,
1184 ScrollOutcome::Changed => true,
1185 };
1186 if r {
1187 return Outcome::Changed;
1188 }
1189
1190 Outcome::Continue
1191 }
1192 }
1193}
1194
1195pub fn handle_events(state: &mut ListState, focus: bool, event: &Event) -> Outcome {
1199 state.focus.set(focus);
1200 HandleEvent::handle(state, event, Regular)
1201}
1202
1203pub fn handle_mouse_events(state: &mut ListState, event: &Event) -> Outcome {
1205 HandleEvent::handle(state, event, MouseOnly)
1206}