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