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