1use crate::_private::NonExhaustive;
6use crate::event::util::MouseFlags;
7use crate::list::selection::{RowSelection, RowSetSelection};
8use crate::util::{fallback_select_style, revert_style};
9use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
10use rat_reloc::{relocate_area, relocate_areas, RelocatableState};
11use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState, ScrollStyle};
12use ratatui::buffer::Buffer;
13use ratatui::layout::Rect;
14use ratatui::style::Style;
15use ratatui::widgets::{Block, ListDirection, ListItem, StatefulWidget};
16use std::cmp::min;
17use std::collections::HashSet;
18use std::marker::PhantomData;
19
20pub mod edit;
21
22pub trait ListSelection {
24 fn count(&self) -> usize;
26
27 fn is_selected(&self, n: usize) -> bool;
29
30 fn lead_selection(&self) -> Option<usize>;
32
33 fn scroll_selected(&self) -> bool {
35 false
36 }
37}
38
39#[derive(Debug, Default, Clone)]
44pub struct List<'a, Selection> {
45 block: Option<Block<'a>>,
46 scroll: Option<Scroll<'a>>,
47
48 items: Vec<ListItem<'a>>,
49
50 style: Style,
51 select_style: Option<Style>,
52 focus_style: Option<Style>,
53 direction: ListDirection,
54
55 _phantom: PhantomData<Selection>,
56}
57
58#[derive(Debug, Clone)]
60pub struct ListStyle {
61 pub style: Style,
63 pub select: Option<Style>,
65 pub focus: Option<Style>,
67
68 pub block: Option<Block<'static>>,
69 pub scroll: Option<ScrollStyle>,
70
71 pub non_exhaustive: NonExhaustive,
72}
73
74#[derive(Debug, PartialEq, Eq)]
76pub struct ListState<Selection> {
77 pub area: Rect,
80 pub inner: Rect,
83 pub row_areas: Vec<Rect>,
86
87 pub rows: usize,
90 pub scroll: ScrollState,
93
94 pub focus: FocusFlag,
97 pub selection: Selection,
100
101 pub mouse: MouseFlags,
104}
105
106impl Default for ListStyle {
107 fn default() -> Self {
108 Self {
109 style: Default::default(),
110 select: None,
111 focus: None,
112 block: None,
113 scroll: None,
114 non_exhaustive: NonExhaustive,
115 }
116 }
117}
118
119impl<'a, Selection> List<'a, Selection> {
120 pub fn new<T>(items: T) -> Self
122 where
123 T: IntoIterator,
124 T::Item: Into<ListItem<'a>>,
125 {
126 let items = items.into_iter().map(|v| v.into()).collect();
127
128 Self {
129 block: None,
130 scroll: None,
131 items,
132 style: Default::default(),
133 select_style: Default::default(),
134 focus_style: Default::default(),
135 direction: Default::default(),
136 _phantom: Default::default(),
137 }
138 }
139
140 pub fn items<T>(mut self, items: T) -> Self
142 where
143 T: IntoIterator,
144 T::Item: Into<ListItem<'a>>,
145 {
146 let items = items.into_iter().map(|v| v.into()).collect();
147 self.items = items;
148 self
149 }
150
151 #[inline]
153 pub fn block(mut self, block: Block<'a>) -> Self {
154 self.block = Some(block);
155 self.block = self.block.map(|v| v.style(self.style));
156 self
157 }
158
159 #[inline]
161 pub fn scroll(mut self, scroll: Scroll<'a>) -> Self {
162 self.scroll = Some(scroll);
163 self
164 }
165
166 #[inline]
168 pub fn styles_opt(self, styles: Option<ListStyle>) -> Self {
169 if let Some(styles) = styles {
170 self.styles(styles)
171 } else {
172 self
173 }
174 }
175
176 #[inline]
178 pub fn styles(mut self, styles: ListStyle) -> Self {
179 self.style = styles.style;
180 if styles.select.is_some() {
181 self.select_style = styles.select;
182 }
183 if styles.focus.is_some() {
184 self.focus_style = styles.focus;
185 }
186 if let Some(styles) = styles.scroll {
187 self.scroll = self.scroll.map(|v| v.styles(styles));
188 }
189 if let Some(block) = styles.block {
190 self.block = Some(block);
191 }
192 self.block = self.block.map(|v| v.style(self.style));
193 self
194 }
195
196 #[inline]
198 pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
199 self.style = style.into();
200 self.block = self.block.map(|v| v.style(self.style));
201 self
202 }
203
204 #[inline]
206 pub fn select_style<S: Into<Style>>(mut self, select_style: S) -> Self {
207 self.select_style = Some(select_style.into());
208 self
209 }
210
211 #[inline]
213 pub fn focus_style<S: Into<Style>>(mut self, focus_style: S) -> Self {
214 self.focus_style = Some(focus_style.into());
215 self
216 }
217
218 #[inline]
220 pub fn direction(mut self, direction: ListDirection) -> Self {
221 self.direction = direction;
222 self
223 }
224
225 #[inline]
227 pub fn len(&self) -> usize {
228 self.items.len()
229 }
230
231 #[inline]
233 pub fn is_empty(&self) -> bool {
234 self.items.is_empty()
235 }
236}
237
238impl<'a, Item, Selection> FromIterator<Item> for List<'a, Selection>
239where
240 Item: Into<ListItem<'a>>,
241{
242 fn from_iter<Iter: IntoIterator<Item = Item>>(iter: Iter) -> Self {
243 Self::new(iter)
244 }
245}
246
247impl<Selection: ListSelection> StatefulWidget for List<'_, Selection> {
248 type State = ListState<Selection>;
249
250 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
251 render_list(self, area, buf, state)
252 }
253}
254
255fn render_list<Selection: ListSelection>(
256 widget: List<'_, Selection>,
257 area: Rect,
258 buf: &mut Buffer,
259 state: &mut ListState<Selection>,
260) {
261 state.area = area;
262 state.rows = widget.items.len();
263
264 let sa = ScrollArea::new()
265 .block(widget.block.as_ref())
266 .v_scroll(widget.scroll.as_ref());
267 state.inner = sa.inner(area, None, Some(&state.scroll));
268
269 state.row_areas.clear();
271 let mut item_area = Rect::new(state.inner.x, state.inner.y, state.inner.width, 1);
272 let mut total_height = 0;
273 for item in widget.items.iter().skip(state.offset()) {
274 item_area.height = item.height() as u16;
275
276 state.row_areas.push(item_area);
277
278 item_area.y += item_area.height;
279 total_height += item_area.height;
280 if total_height >= state.inner.height {
281 break;
282 }
283 }
284 if total_height < state.inner.height {
285 state.scroll.set_page_len(
286 state.row_areas.len() + state.inner.height as usize - total_height as usize,
287 );
288 } else {
289 state.scroll.set_page_len(state.row_areas.len());
290 }
291
292 let focus_style = widget.focus_style.unwrap_or(revert_style(widget.style));
293 let select_style = widget
294 .select_style
295 .unwrap_or(fallback_select_style(widget.style));
296
297 let mut n = 0;
299 let mut height = 0;
300 for item in widget.items.iter().rev() {
301 height += item.height();
302 if height > state.inner.height as usize {
303 break;
304 }
305 n += 1;
306 }
307 state.scroll.set_max_offset(state.rows.saturating_sub(n));
308
309 let (style, select_style) = if state.is_focused() {
310 (widget.style, focus_style)
311 } else {
312 (widget.style, select_style)
313 };
314
315 sa.render(
316 area,
317 buf,
318 &mut ScrollAreaState::new().v_scroll(&mut state.scroll),
319 );
320
321 let items = widget
323 .items
324 .into_iter()
325 .enumerate()
326 .map(|(i, v)| {
327 if state.selection.is_selected(i) {
328 v.style(style.patch(select_style))
329 } else {
330 v.style(style)
331 }
332 })
333 .collect::<Vec<_>>();
334
335 let mut list_state = ratatui::widgets::ListState::default().with_offset(state.scroll.offset());
336
337 StatefulWidget::render(
338 ratatui::widgets::List::default()
339 .items(items)
340 .style(widget.style)
341 .direction(widget.direction),
342 state.inner,
343 buf,
344 &mut list_state,
345 );
346}
347
348impl<Selection> HasFocus for ListState<Selection> {
349 fn build(&self, builder: &mut FocusBuilder) {
350 builder.leaf_widget(self);
351 }
352
353 #[inline]
354 fn focus(&self) -> FocusFlag {
355 self.focus.clone()
356 }
357
358 #[inline]
359 fn area(&self) -> Rect {
360 self.area
361 }
362}
363
364impl<Selection: Default> Default for ListState<Selection> {
365 fn default() -> Self {
366 Self {
367 area: Default::default(),
368 inner: Default::default(),
369 row_areas: Default::default(),
370 rows: Default::default(),
371 scroll: Default::default(),
372 focus: Default::default(),
373 selection: Default::default(),
374 mouse: Default::default(),
375 }
376 }
377}
378
379impl<Selection: Clone> Clone for ListState<Selection> {
380 fn clone(&self) -> Self {
381 Self {
382 area: self.area,
383 inner: self.inner,
384 row_areas: self.row_areas.clone(),
385 rows: self.rows,
386 scroll: self.scroll.clone(),
387 focus: FocusFlag::named(self.focus.name()),
388 selection: self.selection.clone(),
389 mouse: Default::default(),
390 }
391 }
392}
393
394impl<Selection> RelocatableState for ListState<Selection> {
395 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
396 self.area = relocate_area(self.area, shift, clip);
397 self.inner = relocate_area(self.inner, shift, clip);
398 relocate_areas(self.row_areas.as_mut_slice(), shift, clip);
399 self.scroll.relocate(shift, clip);
400 }
401}
402
403impl<Selection: ListSelection> ListState<Selection> {
404 pub fn new() -> Self
406 where
407 Selection: Default,
408 {
409 Default::default()
410 }
411
412 pub fn named(name: &str) -> Self
414 where
415 Selection: Default,
416 {
417 Self {
418 focus: FocusFlag::named(name),
419 ..Default::default()
420 }
421 }
422
423 #[inline]
424 pub fn rows(&self) -> usize {
425 self.rows
426 }
427
428 #[inline]
429 pub fn clear_offset(&mut self) {
430 self.scroll.set_offset(0);
431 }
432
433 #[inline]
434 pub fn max_offset(&self) -> usize {
435 self.scroll.max_offset()
436 }
437
438 #[inline]
439 pub fn set_max_offset(&mut self, max: usize) {
440 self.scroll.set_max_offset(max);
441 }
442
443 #[inline]
444 pub fn offset(&self) -> usize {
445 self.scroll.offset()
446 }
447
448 #[inline]
449 pub fn set_offset(&mut self, offset: usize) -> bool {
450 self.scroll.set_offset(offset)
451 }
452
453 #[inline]
454 pub fn page_len(&self) -> usize {
455 self.scroll.page_len()
456 }
457
458 pub fn scroll_by(&self) -> usize {
459 self.scroll.scroll_by()
460 }
461
462 #[inline]
464 pub fn scroll_to_selected(&mut self) -> bool {
465 if let Some(selected) = self.selection.lead_selection() {
466 self.scroll_to(selected)
467 } else {
468 false
469 }
470 }
471
472 #[inline]
473 pub fn scroll_to(&mut self, pos: usize) -> bool {
474 if pos >= self.offset() + self.page_len() {
475 self.set_offset(pos - self.page_len() + 1)
476 } else if pos < self.offset() {
477 self.set_offset(pos)
478 } else {
479 false
480 }
481 }
482
483 #[inline]
484 pub fn scroll_up(&mut self, n: usize) -> bool {
485 self.scroll.scroll_up(n)
486 }
487
488 #[inline]
489 pub fn scroll_down(&mut self, n: usize) -> bool {
490 self.scroll.scroll_down(n)
491 }
492}
493
494impl<Selection: ListSelection> ListState<Selection> {
495 pub fn row_area(&self, row: usize) -> Option<Rect> {
497 if row < self.scroll.offset() || row >= self.scroll.offset() + self.scroll.page_len() {
498 return None;
499 }
500
501 Some(self.row_areas[row - self.scroll.offset])
502 }
503
504 #[inline]
505 pub fn row_at_clicked(&self, pos: (u16, u16)) -> Option<usize> {
506 self.mouse
507 .row_at(&self.row_areas, pos.1)
508 .map(|v| self.scroll.offset() + v)
509 }
510
511 #[inline]
513 pub fn row_at_drag(&self, pos: (u16, u16)) -> usize {
514 match self.mouse.row_at_drag(self.inner, &self.row_areas, pos.1) {
515 Ok(v) => self.scroll.offset() + v,
516 Err(v) if v <= 0 => self.scroll.offset().saturating_sub((-v) as usize),
517 Err(v) => self.scroll.offset() + self.row_areas.len() + v as usize,
518 }
519 }
520}
521
522impl ListState<RowSelection> {
523 pub fn items_added(&mut self, pos: usize, n: usize) {
527 self.scroll.items_added(pos, n);
528 self.selection.items_added(pos, n);
529 self.rows += n;
530 }
531
532 pub fn items_removed(&mut self, pos: usize, n: usize) {
536 self.scroll.items_removed(pos, n);
537 self.selection
538 .items_removed(pos, n, self.rows.saturating_sub(1));
539 self.rows -= n;
540 }
541
542 #[inline]
544 pub fn set_scroll_selection(&mut self, scroll: bool) {
545 self.selection.set_scroll_selected(scroll);
546 }
547
548 pub(crate) fn remap_offset_selection(&self, offset: usize) -> usize {
552 if self.scroll.max_offset() > 0 {
553 (self.rows * offset) / self.scroll.max_offset()
554 } else {
555 0 }
557 }
558
559 #[inline]
561 pub fn clear_selection(&mut self) {
562 self.selection.clear();
563 }
564
565 #[inline]
567 pub fn has_selection(&mut self) -> bool {
568 self.selection.has_selection()
569 }
570
571 #[inline]
573 pub fn selected(&self) -> Option<usize> {
574 self.selection.lead_selection()
575 }
576
577 #[inline]
578 pub fn select(&mut self, row: Option<usize>) -> bool {
579 self.selection.select(row)
580 }
581
582 #[inline]
585 pub fn move_to(&mut self, row: usize) -> bool {
586 let r = self.selection.move_to(row, self.rows.saturating_sub(1));
587 let s = self.scroll_to(self.selection.selected().expect("row"));
588 r || s
589 }
590
591 #[inline]
594 pub fn move_up(&mut self, n: usize) -> bool {
595 let r = self.selection.move_up(n, self.rows.saturating_sub(1));
596 let s = self.scroll_to(self.selection.selected().expect("row"));
597 r || s
598 }
599
600 #[inline]
603 pub fn move_down(&mut self, n: usize) -> bool {
604 let r = self.selection.move_down(n, self.rows.saturating_sub(1));
605 let s = self.scroll_to(self.selection.selected().expect("row"));
606 r || s
607 }
608}
609
610impl ListState<RowSetSelection> {
611 #[inline]
613 pub fn clear_selection(&mut self) {
614 self.selection.clear();
615 }
616
617 #[inline]
619 pub fn has_selection(&mut self) -> bool {
620 self.selection.has_selection()
621 }
622
623 #[inline]
624 pub fn selected(&self) -> HashSet<usize> {
625 self.selection.selected()
626 }
627
628 #[inline]
635 pub fn set_lead(&mut self, row: Option<usize>, extend: bool) -> bool {
636 if let Some(row) = row {
637 self.selection
638 .set_lead(Some(min(row, self.rows.saturating_sub(1))), extend)
639 } else {
640 self.selection.set_lead(None, extend)
641 }
642 }
643
644 #[inline]
646 pub fn lead(&self) -> Option<usize> {
647 self.selection.lead()
648 }
649
650 #[inline]
652 pub fn anchor(&self) -> Option<usize> {
653 self.selection.anchor()
654 }
655
656 #[inline]
658 pub fn set_lead_clamped(&mut self, lead: usize, max: usize, extend: bool) {
659 self.selection.move_to(lead, max, extend);
660 }
661
662 #[inline]
665 pub fn retire_selection(&mut self) {
666 self.selection.retire_selection();
667 }
668
669 #[inline]
671 pub fn add_selected(&mut self, idx: usize) {
672 self.selection.add(idx);
673 }
674
675 #[inline]
678 pub fn remove_selected(&mut self, idx: usize) {
679 self.selection.remove(idx);
680 }
681
682 #[inline]
685 pub fn move_to(&mut self, row: usize, extend: bool) -> bool {
686 let r = self
687 .selection
688 .move_to(row, self.rows.saturating_sub(1), extend);
689 let s = self.scroll_to(self.selection.lead().expect("row"));
690 r || s
691 }
692
693 #[inline]
696 pub fn move_up(&mut self, n: usize, extend: bool) -> bool {
697 let r = self
698 .selection
699 .move_up(n, self.rows.saturating_sub(1), extend);
700 let s = self.scroll_to(self.selection.lead().expect("row"));
701 r || s
702 }
703
704 #[inline]
707 pub fn move_down(&mut self, n: usize, extend: bool) -> bool {
708 let r = self
709 .selection
710 .move_down(n, self.rows.saturating_sub(1), extend);
711 let s = self.scroll_to(self.selection.lead().expect("row"));
712 r || s
713 }
714}
715
716pub mod selection {
717 use crate::event::{ct_event, flow, HandleEvent, MouseOnly, Outcome, Regular};
718 use crate::list::{ListSelection, ListState};
719 use crossterm::event::KeyModifiers;
720 use rat_focus::HasFocus;
721 use rat_ftable::TableSelection;
722 use rat_scrolled::event::ScrollOutcome;
723 use rat_scrolled::ScrollAreaState;
724 use std::mem;
725
726 pub type NoSelection = rat_ftable::selection::NoSelection;
728
729 impl ListSelection for NoSelection {
730 fn count(&self) -> usize {
731 0
732 }
733
734 #[inline]
735 fn is_selected(&self, _n: usize) -> bool {
736 false
737 }
738
739 #[inline]
740 fn lead_selection(&self) -> Option<usize> {
741 None
742 }
743 }
744
745 impl HandleEvent<crossterm::event::Event, Regular, Outcome> for ListState<NoSelection> {
746 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> Outcome {
747 let res = if self.is_focused() {
748 match event {
749 ct_event!(keycode press Down) => self.scroll_down(1).into(),
750 ct_event!(keycode press Up) => self.scroll_up(1).into(),
751 ct_event!(keycode press CONTROL-Down) | ct_event!(keycode press End) => {
752 self.scroll_to(self.max_offset()).into()
753 }
754 ct_event!(keycode press CONTROL-Up) | ct_event!(keycode press Home) => {
755 self.scroll_to(0).into()
756 }
757 ct_event!(keycode press PageUp) => {
758 self.scroll_up(self.page_len().saturating_sub(1)).into()
759 }
760 ct_event!(keycode press PageDown) => {
761 self.scroll_down(self.page_len().saturating_sub(1)).into()
762 }
763 _ => Outcome::Continue,
764 }
765 } else {
766 Outcome::Continue
767 };
768
769 if res == Outcome::Continue {
770 self.handle(event, MouseOnly)
771 } else {
772 res
773 }
774 }
775 }
776
777 impl HandleEvent<crossterm::event::Event, MouseOnly, Outcome> for ListState<NoSelection> {
778 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> Outcome {
779 let mut sas = ScrollAreaState::new()
780 .area(self.inner)
781 .v_scroll(&mut self.scroll);
782 let r = match sas.handle(event, MouseOnly) {
783 ScrollOutcome::Up(v) => self.scroll_up(v),
784 ScrollOutcome::Down(v) => self.scroll_down(v),
785 ScrollOutcome::VPos(v) => self.set_offset(v),
786 ScrollOutcome::Left(_) => false,
787 ScrollOutcome::Right(_) => false,
788 ScrollOutcome::HPos(_) => false,
789
790 ScrollOutcome::Continue => false,
791 ScrollOutcome::Unchanged => false,
792 ScrollOutcome::Changed => true,
793 };
794 if r {
795 return Outcome::Changed;
796 }
797
798 Outcome::Unchanged
799 }
800 }
801
802 pub type RowSelection = rat_ftable::selection::RowSelection;
804
805 impl ListSelection for RowSelection {
806 fn count(&self) -> usize {
807 if self.lead_row.is_some() {
808 1
809 } else {
810 0
811 }
812 }
813
814 #[inline]
815 fn is_selected(&self, n: usize) -> bool {
816 self.lead_row == Some(n)
817 }
818
819 #[inline]
820 fn lead_selection(&self) -> Option<usize> {
821 self.lead_row
822 }
823
824 fn scroll_selected(&self) -> bool {
825 self.scroll_selected
826 }
827 }
828
829 impl HandleEvent<crossterm::event::Event, Regular, Outcome> for ListState<RowSelection> {
830 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> Outcome {
831 let res = if self.is_focused() {
832 match event {
833 ct_event!(keycode press Down) => self.move_down(1).into(),
834 ct_event!(keycode press Up) => self.move_up(1).into(),
835 ct_event!(keycode press CONTROL-Down) | ct_event!(keycode press End) => {
836 self.move_to(self.rows.saturating_sub(1)).into()
837 }
838 ct_event!(keycode press CONTROL-Up) | ct_event!(keycode press Home) => {
839 self.move_to(0).into()
840 }
841 ct_event!(keycode press PageUp) => {
842 self.move_up(self.page_len().saturating_sub(1)).into()
843 }
844 ct_event!(keycode press PageDown) => {
845 self.move_down(self.page_len().saturating_sub(1)).into()
846 }
847 _ => Outcome::Continue,
848 }
849 } else {
850 Outcome::Continue
851 };
852
853 if res == Outcome::Continue {
854 self.handle(event, MouseOnly)
855 } else {
856 res
857 }
858 }
859 }
860
861 impl HandleEvent<crossterm::event::Event, MouseOnly, Outcome> for ListState<RowSelection> {
862 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> Outcome {
863 flow!(match event {
864 ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
865 self.move_to(self.row_at_drag((m.column, m.row))).into()
866 }
867 ct_event!(mouse down Left for column, row) => {
868 if self.inner.contains((*column, *row).into()) {
869 if let Some(new_row) = self.row_at_clicked((*column, *row)) {
870 self.move_to(new_row).into()
871 } else {
872 Outcome::Continue
873 }
874 } else {
875 Outcome::Continue
876 }
877 }
878
879 _ => Outcome::Continue,
880 });
881
882 let mut sas = ScrollAreaState::new()
883 .area(self.inner)
884 .v_scroll(&mut self.scroll);
885 let r = match sas.handle(event, MouseOnly) {
886 ScrollOutcome::Up(v) => {
887 if ListSelection::scroll_selected(&self.selection) {
888 self.move_up(1)
889 } else {
890 self.scroll_up(v)
891 }
892 }
893 ScrollOutcome::Down(v) => {
894 if ListSelection::scroll_selected(&self.selection) {
895 self.move_down(1)
896 } else {
897 self.scroll_down(v)
898 }
899 }
900 ScrollOutcome::VPos(v) => {
901 if ListSelection::scroll_selected(&self.selection) {
902 self.move_to(self.remap_offset_selection(v))
903 } else {
904 self.set_offset(v)
905 }
906 }
907 ScrollOutcome::Left(_) => false,
908 ScrollOutcome::Right(_) => false,
909 ScrollOutcome::HPos(_) => false,
910
911 ScrollOutcome::Continue => false,
912 ScrollOutcome::Unchanged => false,
913 ScrollOutcome::Changed => true,
914 };
915 if r {
916 return Outcome::Changed;
917 }
918
919 Outcome::Continue
920 }
921 }
922
923 pub type RowSetSelection = rat_ftable::selection::RowSetSelection;
924
925 impl ListSelection for RowSetSelection {
926 fn count(&self) -> usize {
927 let n = if let Some(anchor) = self.anchor_row {
928 if let Some(lead) = self.lead_row {
929 anchor.abs_diff(lead) + 1
930 } else {
931 0
932 }
933 } else {
934 0
935 };
936
937 n + self.selected.len()
938 }
939
940 fn is_selected(&self, n: usize) -> bool {
941 if let Some(mut anchor) = self.anchor_row {
942 if let Some(mut lead) = self.lead_row {
943 if lead < anchor {
944 mem::swap(&mut lead, &mut anchor);
945 }
946
947 if n >= anchor && n <= lead {
948 return true;
949 }
950 }
951 } else {
952 if let Some(lead) = self.lead_row {
953 if n == lead {
954 return true;
955 }
956 }
957 }
958
959 self.selected.contains(&n)
960 }
961
962 fn lead_selection(&self) -> Option<usize> {
963 self.lead_row
964 }
965 }
966
967 impl HandleEvent<crossterm::event::Event, Regular, Outcome> for ListState<RowSetSelection> {
968 fn handle(&mut self, event: &crossterm::event::Event, _: Regular) -> Outcome {
969 let res = if self.is_focused() {
970 match event {
971 ct_event!(keycode press Down) => self.move_down(1, false).into(),
972 ct_event!(keycode press SHIFT-Down) => self.move_down(1, true).into(),
973 ct_event!(keycode press Up) => self.move_up(1, false).into(),
974 ct_event!(keycode press SHIFT-Up) => self.move_up(1, true).into(),
975 ct_event!(keycode press CONTROL-Down) | ct_event!(keycode press End) => {
976 self.move_to(self.rows.saturating_sub(1), false).into()
977 }
978 ct_event!(keycode press SHIFT-End) => {
979 self.move_to(self.rows.saturating_sub(1), true).into()
980 }
981 ct_event!(keycode press CONTROL-Up) | ct_event!(keycode press Home) => {
982 self.move_to(0, false).into()
983 }
984 ct_event!(keycode press SHIFT-Home) => self.move_to(0, true).into(),
985
986 ct_event!(keycode press PageUp) => self
987 .move_up(self.page_len().saturating_sub(1), false)
988 .into(),
989 ct_event!(keycode press SHIFT-PageUp) => {
990 self.move_up(self.page_len().saturating_sub(1), true).into()
991 }
992 ct_event!(keycode press PageDown) => self
993 .move_down(self.page_len().saturating_sub(1), false)
994 .into(),
995 ct_event!(keycode press SHIFT-PageDown) => self
996 .move_down(self.page_len().saturating_sub(1), true)
997 .into(),
998 _ => Outcome::Continue,
999 }
1000 } else {
1001 Outcome::Continue
1002 };
1003
1004 if res == Outcome::Continue {
1005 self.handle(event, MouseOnly)
1006 } else {
1007 res
1008 }
1009 }
1010 }
1011
1012 impl HandleEvent<crossterm::event::Event, MouseOnly, Outcome> for ListState<RowSetSelection> {
1013 fn handle(&mut self, event: &crossterm::event::Event, _: MouseOnly) -> Outcome {
1014 flow!(match event {
1015 ct_event!(mouse any for m) | ct_event!(mouse any CONTROL for m)
1016 if self.mouse.drag(self.inner, m)
1017 || self.mouse.drag2(self.inner, m, KeyModifiers::CONTROL) =>
1018 {
1019 self.move_to(self.row_at_drag((m.column, m.row)), true)
1020 .into()
1021 }
1022 ct_event!(mouse down Left for column, row) => {
1023 let pos = (*column, *row);
1024 if self.inner.contains(pos.into()) {
1025 if let Some(new_row) = self.row_at_clicked(pos) {
1026 self.move_to(new_row, false).into()
1027 } else {
1028 Outcome::Continue
1029 }
1030 } else {
1031 Outcome::Continue
1032 }
1033 }
1034 ct_event!(mouse down ALT-Left for column, row) => {
1035 let pos = (*column, *row);
1036 if self.area.contains(pos.into()) {
1037 if let Some(new_row) = self.row_at_clicked(pos) {
1038 self.move_to(new_row, true).into()
1039 } else {
1040 Outcome::Continue
1041 }
1042 } else {
1043 Outcome::Continue
1044 }
1045 }
1046 ct_event!(mouse down CONTROL-Left for column, row) => {
1047 let pos = (*column, *row);
1048 if self.area.contains(pos.into()) {
1049 if let Some(new_row) = self.row_at_clicked(pos) {
1050 self.retire_selection();
1051 if self.selection.is_selected_row(new_row) {
1052 self.selection.remove(new_row);
1053 } else {
1054 self.move_to(new_row, true);
1055 }
1056 Outcome::Changed
1057 } else {
1058 Outcome::Continue
1059 }
1060 } else {
1061 Outcome::Continue
1062 }
1063 }
1064 _ => Outcome::Continue,
1065 });
1066
1067 let mut sas = ScrollAreaState::new()
1068 .area(self.inner)
1069 .v_scroll(&mut self.scroll);
1070 let r = match sas.handle(event, MouseOnly) {
1071 ScrollOutcome::Up(v) => self.scroll_up(v),
1072 ScrollOutcome::Down(v) => self.scroll_down(v),
1073 ScrollOutcome::VPos(v) => self.set_offset(v),
1074 ScrollOutcome::Left(_) => false,
1075 ScrollOutcome::Right(_) => false,
1076 ScrollOutcome::HPos(_) => false,
1077
1078 ScrollOutcome::Continue => false,
1079 ScrollOutcome::Unchanged => false,
1080 ScrollOutcome::Changed => true,
1081 };
1082 if r {
1083 return Outcome::Changed;
1084 }
1085
1086 Outcome::Unchanged
1087 }
1088 }
1089}