1use std::{fmt, hash::Hash, ops::RangeInclusive, sync::Arc};
15
16use gpui_kit::base::AutoScroll;
17use gpui_kit::base::actions::{
18 Cancel, Confirm, SelectDown, SelectFirst, SelectLast, SelectNextColumn, SelectPageDown,
19 SelectPageUp, SelectPrevColumn, SelectUp,
20};
21use gpui_kit::{
22 Context, InteractiveElement, Modifiers, MouseButton, MouseDownEvent, Pixels, Point, px,
23};
24use rustc_hash::FxHashMap;
25
26use super::{
27 ColumnOf, KeyOf, LiveGridDelegate, LiveGridEvent, LiveGridState, SelectionOrigin, ViewColumnIx,
28 a11y::SelectionUnit,
29 columns::{ColumnLayout, column_of},
30 scroll::page_size,
31};
32use crate::{
33 actions::{
34 ExtendSelectionDown, ExtendSelectionNextColumn, ExtendSelectionPageDown,
35 ExtendSelectionPageUp, ExtendSelectionPrevColumn, ExtendSelectionToFirst,
36 ExtendSelectionToFirstCell, ExtendSelectionToFirstRow, ExtendSelectionToLast,
37 ExtendSelectionToLastCell, ExtendSelectionToLastRow, ExtendSelectionUp, SelectAll,
38 SelectFirstCell, SelectFirstRow, SelectLastCell, SelectLastRow, ToggleSelection,
39 },
40 engine::{DisplayRow, GroupInfo, GroupPath, Projection},
41 model::{ColumnIx, ColumnSet, GridColumns, GridRow},
42 policy::{
43 ContextMenuGesture, ContextMenuGestureContext, ExpandArea, FocusCaptions, GestureInput,
44 GridArea, LineScopeContext, SelectableLine, SelectionEdit, SelectionGesture,
45 SelectionGestureContext, SelectionPolicy, SelectionScope, SelectionScopeContext,
46 },
47 store::{PinnedEdge, RowId, StoreSnapshot},
48};
49
50const DRAG_THRESHOLD: Pixels = px(2.);
53
54pub(super) fn is_past_drag_threshold(origin: Point<Pixels>, position: Point<Pixels>) -> bool {
57 (position.x - origin.x).abs() > DRAG_THRESHOLD || (position.y - origin.y).abs() > DRAG_THRESHOLD
58}
59
60#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
62pub enum SelectionMode {
63 #[default]
65 RowSingle,
66 RowSet,
68 CellSingle,
70 CellSet,
72}
73
74impl SelectionMode {
75 fn selects_cells(self) -> bool {
76 matches!(self, Self::CellSingle | Self::CellSet)
77 }
78
79 pub(super) fn is_single(self) -> bool {
82 matches!(self, Self::RowSingle | Self::CellSingle)
83 }
84
85 pub(super) fn selection_unit(self) -> SelectionUnit {
88 if self.selects_cells() {
89 SelectionUnit::Cell
90 } else {
91 SelectionUnit::Row
92 }
93 }
94}
95
96#[derive(Clone)]
99pub struct GridSelection<K> {
100 rows: Arc<FxHashMap<K, ColumnSet>>,
101}
102
103impl<K> Default for GridSelection<K> {
104 fn default() -> Self {
105 Self {
106 rows: Arc::default(),
107 }
108 }
109}
110
111impl<K: fmt::Debug> fmt::Debug for GridSelection<K> {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 let mut debug = f.debug_struct("GridSelection");
115 debug.field("len", &self.rows.len());
116 if self.rows.len() <= 8 {
117 debug.field("rows", &*self.rows);
118 }
119 debug.finish()
120 }
121}
122
123impl<K: Eq + Hash> GridSelection<K> {
124 fn from_map(rows: FxHashMap<K, ColumnSet>) -> Self {
125 Self {
126 rows: Arc::new(rows),
127 }
128 }
129
130 pub(crate) fn from_rows(rows: impl IntoIterator<Item = (K, ColumnSet)>) -> Self {
133 Self::from_map(
134 rows.into_iter()
135 .filter(|(_, columns)| !columns.is_empty())
136 .collect(),
137 )
138 }
139
140 pub fn len(&self) -> usize {
142 self.rows.len()
143 }
144
145 pub fn is_empty(&self) -> bool {
147 self.rows.is_empty()
148 }
149
150 pub fn contains(&self, key: &K) -> bool {
152 self.rows.contains_key(key)
153 }
154
155 pub fn contains_cell(&self, key: &K, column: ColumnIx) -> bool {
157 self.columns(key).contains(column)
158 }
159
160 pub fn columns(&self, key: &K) -> ColumnSet {
163 self.rows.get(key).copied().unwrap_or(ColumnSet::EMPTY)
164 }
165
166 pub fn iter(&self) -> impl Iterator<Item = &K> + '_ {
168 self.rows.keys()
169 }
170
171 fn same_as(&self, other: &Self) -> bool {
173 Arc::ptr_eq(&self.rows, &other.rows)
174 || (self.rows.len() == other.rows.len()
175 && self
176 .rows
177 .iter()
178 .all(|(key, columns)| other.rows.get(key) == Some(columns)))
179 }
180}
181
182impl<K: Clone + Eq + Hash> GridSelection<K> {
183 fn rows_mut(&mut self) -> &mut FxHashMap<K, ColumnSet> {
185 Arc::make_mut(&mut self.rows)
186 }
187
188 fn union_with(&mut self, other: &Self) -> bool {
190 let new = other.rows.iter().any(|(key, columns)| {
191 let mut union = self.columns(key);
192 union.union_with(*columns);
193 union != self.columns(key)
194 });
195 if new {
196 let rows = self.rows_mut();
197 for (key, columns) in other.rows.iter() {
198 rows.entry(key.clone()).or_default().union_with(*columns);
199 }
200 }
201 new
202 }
203
204 fn retain(&mut self, missing: impl Fn(&K) -> bool) -> bool {
206 let gone = self.rows.keys().any(&missing);
207 if gone {
208 self.rows_mut().retain(|key, _| !missing(key));
209 }
210 gone
211 }
212
213 fn flip(&mut self, key: &K, column: ColumnIx, item: ColumnSet) {
216 let selected = self.columns(key);
217 let rows = self.rows_mut();
218 if selected.contains(column) {
219 let rest = difference(selected, item);
220 if rest.is_empty() {
221 rows.remove(key);
222 } else {
223 rows.insert(key.clone(), rest);
224 }
225 } else {
226 let mut columns = selected;
227 columns.union_with(item);
228 rows.insert(key.clone(), columns);
229 }
230 }
231}
232
233#[derive(Clone, Debug, PartialEq, Eq)]
236pub struct FocusedCell<K, C> {
237 row: Option<K>,
238 group: Option<GroupPath>,
239 column: C,
240}
241
242impl<K, C: Copy> FocusedCell<K, C> {
243 pub(crate) fn new(row: K, column: C) -> Self {
244 Self {
245 row: Some(row),
246 group: None,
247 column,
248 }
249 }
250
251 pub(crate) fn caption(path: GroupPath, column: C) -> Self {
254 Self {
255 row: None,
256 group: Some(path),
257 column,
258 }
259 }
260
261 pub fn row(&self) -> Option<&K> {
263 self.row.as_ref()
264 }
265
266 pub fn group(&self) -> Option<&GroupPath> {
268 self.group.as_ref()
269 }
270
271 pub fn column(&self) -> C {
274 self.column
275 }
276}
277
278#[derive(Clone, Debug, PartialEq, Eq)]
281pub(crate) enum LineTarget<K> {
282 Row(K),
283 Group(GroupPath),
284}
285
286impl<K: Clone, C: Copy> FocusedCell<K, C> {
287 pub(crate) fn line(&self) -> Option<LineTarget<K>> {
289 match (&self.row, &self.group) {
290 (Some(key), _) => Some(LineTarget::Row(key.clone())),
291 (None, Some(path)) => Some(LineTarget::Group(path.clone())),
292 (None, None) => None,
293 }
294 }
295}
296
297#[derive(Clone, Debug)]
300pub struct ScopedSelection<K> {
301 keys: Arc<[K]>,
302 columns: Arc<[ColumnSet]>,
303}
304
305impl<K> Default for ScopedSelection<K> {
306 fn default() -> Self {
307 Self {
308 keys: Vec::new().into(),
309 columns: Vec::new().into(),
310 }
311 }
312}
313
314impl<K> FromIterator<(K, ColumnSet)> for ScopedSelection<K> {
315 fn from_iter<I: IntoIterator<Item = (K, ColumnSet)>>(rows: I) -> Self {
316 let (keys, columns): (Vec<K>, Vec<ColumnSet>) = rows.into_iter().unzip();
317 Self {
318 keys: keys.into(),
319 columns: columns.into(),
320 }
321 }
322}
323
324impl<K> ScopedSelection<K> {
325 pub fn keys(&self) -> &[K] {
327 &self.keys
328 }
329
330 pub fn columns(&self, ix: usize) -> ColumnSet {
333 self.columns.get(ix).copied().unwrap_or(ColumnSet::EMPTY)
334 }
335
336 pub fn len(&self) -> usize {
338 self.keys.len()
339 }
340
341 pub fn is_empty(&self) -> bool {
343 self.keys.is_empty()
344 }
345
346 pub(crate) fn shared_keys(&self) -> Arc<[K]> {
348 self.keys.clone()
349 }
350}
351
352#[derive(Clone, Debug)]
356pub(crate) struct PointerTarget<K> {
357 row: Option<K>,
360 group: Option<GroupPath>,
362 column: Option<ColumnIx>,
364 button: MouseButton,
365 position: Point<Pixels>,
366 pinned: Option<PinnedEdge>,
369}
370
371impl<K> PointerTarget<K> {
372 pub(crate) fn row(&self) -> Option<&K> {
373 self.row.as_ref()
374 }
375
376 pub(crate) fn group(&self) -> Option<&GroupPath> {
378 self.group.as_ref()
379 }
380
381 pub(crate) fn pinned(&self) -> Option<PinnedEdge> {
383 self.pinned
384 }
385
386 pub(crate) fn column(&self) -> Option<ColumnIx> {
387 self.column
388 }
389
390 #[expect(dead_code, reason = "no caller reads the pressed button yet")]
391 pub(crate) fn button(&self) -> MouseButton {
392 self.button
393 }
394
395 pub(crate) fn position(&self) -> Point<Pixels> {
396 self.position
397 }
398}
399
400pub(super) struct ShownRows<'a, R: GridRow> {
407 projection: &'a Projection,
408 snapshot: &'a StoreSnapshot<R>,
409 policy: &'a dyn SelectionPolicy<R>,
410}
411
412impl<R: GridRow> Clone for ShownRows<'_, R> {
413 fn clone(&self) -> Self {
414 *self
415 }
416}
417
418impl<R: GridRow> Copy for ShownRows<'_, R> {}
419
420impl<'a, R: GridRow> ShownRows<'a, R> {
421 pub(super) fn new(projection: &'a Projection, snapshot: &'a StoreSnapshot<R>) -> Self {
423 Self {
424 projection,
425 snapshot,
426 policy: &FocusCaptions,
427 }
428 }
429
430 pub(super) fn with_policy(mut self, policy: &'a dyn SelectionPolicy<R>) -> Self {
431 self.policy = policy;
432 self
433 }
434
435 fn len(&self) -> usize {
436 self.projection.len()
437 }
438
439 fn key_at(&self, display_ix: usize) -> Option<&'a R::Key> {
441 let id = self.projection.row(display_ix)?;
442 self.snapshot.row(id).map(|row| row.key())
443 }
444
445 fn group_at(&self, display_ix: usize) -> Option<GroupInfo<'a>> {
447 match self.projection.line(display_ix)? {
448 DisplayRow::GroupCaption { group, .. } => self.projection.groups().get(group),
449 _ => None,
450 }
451 }
452
453 fn display_ix(&self, key: &R::Key) -> Option<usize> {
455 self.snapshot
456 .row_id(key)
457 .and_then(|id| self.projection.display_ix(id))
458 }
459
460 pub(super) fn caption_ix(&self, path: &GroupPath) -> Option<usize> {
463 let group = self.projection.groups().find(path)?;
464 self.projection.group_line(group.id())
465 }
466
467 fn line_of<C: Copy>(&self, cell: &FocusedCell<R::Key, C>) -> Option<usize> {
469 match (cell.row(), cell.group()) {
470 (Some(key), _) => self.display_ix(key),
471 (None, Some(path)) => self.caption_ix(path),
472 (None, None) => None,
473 }
474 }
475
476 fn is_focusable(&self, display_ix: usize) -> bool {
479 self.key_at(display_ix).is_some() || self.group_at(display_ix).is_some()
480 }
481
482 fn takes_selection<C: Copy>(&self, cell: &FocusedCell<R::Key, C>) -> bool {
488 let Some(key) = cell.row() else {
489 return false;
490 };
491 let Some((id, row)) = self
492 .snapshot
493 .row_id(key)
494 .and_then(|id| Some((id, self.snapshot.row(id)?)))
495 else {
496 return true;
497 };
498 self.policy.is_selectable(&SelectableLine::Row {
499 row: &**row,
500 branch: self.projection.branch(id),
501 })
502 }
503
504 fn cell_at(
506 &self,
507 display_ix: usize,
508 column: R::Column,
509 ) -> Option<FocusedCell<R::Key, R::Column>> {
510 if let Some(key) = self.key_at(display_ix) {
511 return Some(FocusedCell::new(key.clone(), column));
512 }
513 let group = self.group_at(display_ix)?;
514 Some(FocusedCell::caption(group.path().clone(), column))
515 }
516
517 fn select_lines(
520 &self,
521 lines: impl Iterator<Item = usize>,
522 item: ColumnSet,
523 map: &mut FxHashMap<R::Key, ColumnSet>,
524 ) {
525 let (projection, snapshot, policy) = (self.projection, self.snapshot, self.policy);
526 let groups = projection.groups();
527 for display_ix in lines {
528 match projection.line(display_ix) {
529 Some(DisplayRow::Data { row: id, .. }) => {
530 if let Some(row) = snapshot.row(id)
531 && policy.is_selectable(&SelectableLine::Row {
532 row: &**row,
533 branch: projection.branch(id),
534 })
535 {
536 map.insert(row.key().clone(), item);
537 }
538 }
539 Some(DisplayRow::GroupCaption { group, .. }) => {
540 let Some(group) = groups.get(group) else {
541 continue;
542 };
543 if policy.is_selectable(&SelectableLine::Caption { group }) {
544 for row in group.rows().iter().filter_map(|id| snapshot.row(*id)) {
545 map.insert(row.key().clone(), item);
546 }
547 }
548 }
549 _ => {}
550 }
551 }
552 }
553
554 fn first_focusable(&self) -> Option<usize> {
556 (0..self.len()).find(|ix| self.is_focusable(*ix))
557 }
558
559 fn last_focusable(&self) -> Option<usize> {
560 (0..self.len()).rev().find(|ix| self.is_focusable(*ix))
561 }
562
563 fn nearest_live(&self, display_ix: usize) -> Option<usize> {
566 let display_ix = display_ix.min(self.len().checked_sub(1)?);
567 (display_ix..self.len())
568 .find(|ix| self.key_at(*ix).is_some())
569 .or_else(|| (0..display_ix).rev().find(|ix| self.key_at(*ix).is_some()))
570 }
571
572 fn focusable_toward(&self, origin: usize, target: usize) -> usize {
577 let focusable = |ix: &usize| self.is_focusable(*ix);
578 if target > origin {
579 (target..self.len())
580 .find(focusable)
581 .or_else(|| (origin + 1..target).rev().find(focusable))
582 .unwrap_or(origin)
583 } else if target < origin {
584 (0..=target)
585 .rev()
586 .find(focusable)
587 .or_else(|| (target + 1..origin).find(focusable))
588 .unwrap_or(origin)
589 } else {
590 origin
591 }
592 }
593
594 fn scope_line(
598 &self,
599 target: &LineTarget<R::Key>,
600 ) -> Option<(SelectableLine<'a, R>, std::ops::Range<usize>)> {
601 let (projection, snapshot) = (self.projection, self.snapshot);
602 match target {
603 LineTarget::Row(key) => {
604 let id = snapshot.row_id(key)?;
605 let row = snapshot.row(id)?;
606 let branch = projection.branch(id);
607 let under = match (branch, projection.order_ix(id)) {
608 (Some(branch), Some(ix)) => ix + 1..ix + 1 + branch.descendant_count(),
609 _ => 0..0,
610 };
611 Some((
612 SelectableLine::Row {
613 row: &**row,
614 branch,
615 },
616 under,
617 ))
618 }
619 LineTarget::Group(path) => {
620 let group = projection.groups().find(path)?;
621 let start = group
622 .rows()
623 .first()
624 .and_then(|id| projection.order_ix(*id))
625 .unwrap_or(0);
626 Some((
627 SelectableLine::Caption { group },
628 start..start + group.rows().len(),
629 ))
630 }
631 }
632 }
633}
634
635pub(super) trait ColumnView {
639 fn view_column_ix(&self, column: ColumnIx) -> Option<ViewColumnIx>;
640 fn column_at(&self, view: ViewColumnIx) -> Option<ColumnIx>;
641 fn visible_count(&self) -> usize;
642 fn visible_set(&self) -> ColumnSet;
643 fn visible_neighbour(&self, column: ColumnIx) -> Option<ColumnIx>;
646}
647
648impl<C: GridColumns> ColumnView for ColumnLayout<C> {
649 fn view_column_ix(&self, column: ColumnIx) -> Option<ViewColumnIx> {
650 ColumnLayout::view_column_ix(self, column)
651 }
652
653 fn column_at(&self, view: ViewColumnIx) -> Option<ColumnIx> {
654 ColumnLayout::column_at(self, view)
655 }
656
657 fn visible_count(&self) -> usize {
658 ColumnLayout::visible_set(self).len()
659 }
660
661 fn visible_set(&self) -> ColumnSet {
662 ColumnLayout::visible_set(self)
663 }
664
665 fn visible_neighbour(&self, column: ColumnIx) -> Option<ColumnIx> {
666 let order = self.order();
667 let position = order.iter().position(|ix| *ix == column)?;
668 order[position + 1..]
669 .iter()
670 .chain(order[..position].iter().rev())
671 .copied()
672 .find(|ix| self.is_visible(*ix))
673 }
674}
675
676fn first_column(columns: &impl ColumnView) -> Option<ColumnIx> {
677 columns.column_at(ViewColumnIx::new(0))
678}
679
680fn last_column(columns: &impl ColumnView) -> Option<ColumnIx> {
681 let last = columns.visible_count().checked_sub(1)?;
682 columns.column_at(ViewColumnIx::new(last as u16))
683}
684
685fn step_column(column: ColumnIx, step: isize, columns: &impl ColumnView) -> ColumnIx {
687 let Some(view) = columns.view_column_ix(column) else {
688 return column;
689 };
690 let last = columns.visible_count().saturating_sub(1);
691 let target = view.get().saturating_add_signed(step).min(last);
692 columns
693 .column_at(ViewColumnIx::new(target as u16))
694 .unwrap_or(column)
695}
696
697fn difference(columns: ColumnSet, drop: ColumnSet) -> ColumnSet {
699 (0..128u16)
700 .map(ColumnIx::new)
701 .filter(|ix| columns.contains(*ix) && !drop.contains(*ix))
702 .fold(ColumnSet::EMPTY, |mut set, ix| {
703 set.insert(ix);
704 set
705 })
706}
707
708pub(super) fn successor<T>(
713 previous: &Projection,
714 id: Option<RowId>,
715 accept: impl Fn(RowId) -> Option<T>,
716) -> Option<T> {
717 let accept_at = |display_ix: usize| previous.row(display_ix).and_then(&accept);
718 match id.and_then(|id| previous.display_ix(id)) {
719 Some(ix) => (ix + 1..previous.len())
720 .find_map(accept_at)
721 .or_else(|| (0..ix).rev().find_map(accept_at)),
722 None => (0..previous.len()).find_map(accept_at),
723 }
724}
725
726#[derive(Clone, Copy, Debug, PartialEq, Eq)]
728pub(super) enum Navigation {
729 Up,
730 Down,
731 PrevColumn,
732 NextColumn,
733 PageUp,
734 PageDown,
735 First,
737 Last,
739 FirstRow,
740 LastRow,
741 FirstCell,
742 LastCell,
743}
744
745pub(super) struct ChangeMark<K, C> {
749 revision: u64,
750 focus: Option<FocusedCell<K, C>>,
751}
752
753struct Drag<K, C> {
756 target: FocusedCell<K, C>,
757 base: Option<GridSelection<K>>,
760}
761
762pub(super) struct SelectionPainter<'a, K> {
766 selection: Option<&'a GridSelection<K>>,
767 range: Option<(RangeInclusive<usize>, ColumnSet)>,
768}
769
770impl<K: Eq + Hash> SelectionPainter<'_, K> {
771 pub(super) fn columns(&self, key: &K, display_ix: usize) -> ColumnSet {
773 let mut columns = self
774 .selection
775 .map_or(ColumnSet::EMPTY, |selection| selection.columns(key));
776 if let Some((rows, range)) = &self.range
777 && rows.contains(&display_ix)
778 {
779 columns.union_with(*range);
780 }
781 columns
782 }
783}
784
785pub(super) struct SelectionModel<K, C> {
787 mode: SelectionMode,
788 all: ColumnSet,
790 selection: GridSelection<K>,
791 revision: u64,
794 focus: Option<FocusedCell<K, C>>,
795 anchor: Option<FocusedCell<K, C>>,
797 drag: Option<Drag<K, C>>,
798}
799
800impl<K: Clone + Eq + Hash, C: GridColumns> SelectionModel<K, C> {
801 pub(super) fn new(mode: SelectionMode) -> Self {
802 Self {
803 mode,
804 all: ColumnSet::first(C::all().len()),
805 selection: GridSelection::default(),
806 revision: 0,
807 focus: None,
808 anchor: None,
809 drag: None,
810 }
811 }
812
813 fn set_selection(&mut self, selection: GridSelection<K>) {
815 if !self.selection.same_as(&selection) {
816 self.selection = selection;
817 self.revision += 1;
818 }
819 }
820
821 pub(super) fn mode(&self) -> SelectionMode {
822 self.mode
823 }
824
825 pub(super) fn selection(&self) -> &GridSelection<K> {
826 &self.selection
827 }
828
829 pub(super) fn focus(&self) -> Option<&FocusedCell<K, C>> {
830 self.focus.as_ref()
831 }
832
833 pub(super) fn is_dragging(&self) -> bool {
834 self.drag.is_some()
835 }
836
837 pub(super) fn mark(&self) -> ChangeMark<K, C> {
838 ChangeMark {
839 revision: self.revision,
840 focus: self.focus.clone(),
841 }
842 }
843
844 pub(super) fn changes_since(&self, mark: &ChangeMark<K, C>) -> (bool, bool) {
846 (self.revision != mark.revision, self.focus != mark.focus)
847 }
848
849 fn item(&self, column: ColumnIx) -> ColumnSet {
852 if self.mode.selects_cells() {
853 let mut cell = ColumnSet::EMPTY;
854 cell.insert(column);
855 cell
856 } else {
857 self.all
858 }
859 }
860
861 fn row_item(&self, columns: &impl ColumnView) -> ColumnSet {
864 match self.mode {
865 SelectionMode::RowSingle | SelectionMode::RowSet => self.all,
866 SelectionMode::CellSet => columns.visible_set(),
867 SelectionMode::CellSingle => self
868 .focus_column(columns)
869 .map_or(ColumnSet::EMPTY, |column| self.item(column)),
870 }
871 }
872
873 fn focus_column(&self, columns: &impl ColumnView) -> Option<ColumnIx> {
875 self.focus
876 .as_ref()
877 .map(|focus| focus.column().ix())
878 .filter(|column| columns.view_column_ix(*column).is_some())
879 .or_else(|| first_column(columns))
880 }
881
882 fn only(&self, target: &FocusedCell<K, C>) -> GridSelection<K> {
883 target.row().map_or_else(GridSelection::default, |key| {
884 GridSelection::from_rows([(key.clone(), self.item(target.column().ix()))])
885 })
886 }
887
888 pub(super) fn is_selected(&self, target: &FocusedCell<K, C>) -> bool {
890 target
891 .row()
892 .is_some_and(|key| self.selection.contains_cell(key, target.column().ix()))
893 }
894
895 pub(super) fn holds_more_than(&self, target: &FocusedCell<K, C>) -> bool {
898 target.row().is_some_and(|key| {
899 self.selection.len() > 1
900 || self.selection.columns(key) != self.item(target.column().ix())
901 })
902 }
903
904 pub(super) fn apply<R>(
911 &mut self,
912 edit: SelectionEdit,
913 target: FocusedCell<K, C>,
914 rows: ShownRows<'_, R>,
915 columns: &impl ColumnView,
916 ) where
917 R: GridRow<Key = K, Column = C>,
918 {
919 let selectable = rows.takes_selection(&target);
920 let edit = match edit {
921 SelectionEdit::Extend | SelectionEdit::AddRange if self.mode.is_single() => {
922 SelectionEdit::Replace
923 }
924 edit => edit,
925 };
926 let edit = match edit {
927 SelectionEdit::Replace | SelectionEdit::Toggle if !selectable => SelectionEdit::Keep,
928 edit => edit,
929 };
930 match edit {
931 SelectionEdit::Replace => self.replace(&target),
932 SelectionEdit::Extend | SelectionEdit::AddRange => {
933 let range = self
934 .anchor
935 .as_ref()
936 .and_then(|anchor| self.range(anchor, &target, rows, columns));
937 match range {
938 Some(range) if edit == SelectionEdit::AddRange => {
939 if self.selection.union_with(&range) {
940 self.revision += 1;
941 }
942 }
943 Some(range) => self.set_selection(range),
944 None if !selectable => {}
948 None => self.replace(&target),
949 }
950 }
951 SelectionEdit::Toggle => {
952 self.toggle(&target);
953 self.anchor = Some(target.clone());
954 }
955 SelectionEdit::Keep => {}
956 }
957 self.focus = Some(target);
958 }
959
960 fn replace(&mut self, target: &FocusedCell<K, C>) {
961 self.set_selection(self.only(target));
962 self.anchor = Some(target.clone());
963 }
964
965 fn toggle(&mut self, target: &FocusedCell<K, C>) {
966 let Some(key) = target.row() else {
967 return;
968 };
969 let column = target.column().ix();
970 if self.mode.is_single() {
971 let toggled = if self.selection.contains_cell(key, column) {
973 GridSelection::default()
974 } else {
975 self.only(target)
976 };
977 self.set_selection(toggled);
978 return;
979 }
980 let item = self.item(column);
981 self.selection.flip(key, column, item);
982 self.revision += 1;
983 }
984
985 fn before_toggle(&self, target: &FocusedCell<K, C>) -> GridSelection<K> {
988 let mut selection = self.selection.clone();
989 if let Some(key) = target.row() {
990 let column = target.column().ix();
991 selection.flip(key, column, self.item(column));
992 }
993 selection
994 }
995
996 fn range<R>(
1001 &self,
1002 anchor: &FocusedCell<K, C>,
1003 target: &FocusedCell<K, C>,
1004 rows: ShownRows<'_, R>,
1005 columns: &impl ColumnView,
1006 ) -> Option<GridSelection<K>>
1007 where
1008 R: GridRow<Key = K, Column = C>,
1009 {
1010 let from = rows.line_of(anchor)?;
1011 let to = rows.line_of(target)?;
1012 let item = self.range_columns(anchor.column().ix(), target.column().ix(), columns);
1013 let (start, end) = (from.min(to), from.max(to));
1014 let mut map = FxHashMap::with_capacity_and_hasher(end - start + 1, Default::default());
1015 rows.select_lines(start..=end, item, &mut map);
1016 Some(GridSelection::from_map(map))
1017 }
1018
1019 fn range_columns(&self, from: ColumnIx, to: ColumnIx, columns: &impl ColumnView) -> ColumnSet {
1022 if !self.mode.selects_cells() {
1023 return self.all;
1024 }
1025 let (Some(from), Some(to)) = (columns.view_column_ix(from), columns.view_column_ix(to))
1026 else {
1027 return self.item(to);
1029 };
1030 let (start, end) = (from.get().min(to.get()), from.get().max(to.get()));
1031 (start..=end)
1032 .filter_map(|view| columns.column_at(ViewColumnIx::new(view as u16)))
1033 .fold(ColumnSet::EMPTY, |mut set, column| {
1034 set.insert(column);
1035 set
1036 })
1037 }
1038
1039 pub(super) fn navigation_target<R>(
1044 &self,
1045 navigation: Navigation,
1046 page: usize,
1047 rows: ShownRows<'_, R>,
1048 columns: &impl ColumnView,
1049 ) -> Option<FocusedCell<K, C>>
1050 where
1051 R: GridRow<Key = K, Column = C>,
1052 {
1053 let first = first_column(columns)?;
1054 let last = last_column(columns)?;
1055 let focus = self.focus.as_ref().and_then(|focus| {
1056 let display_ix = rows.line_of(focus)?;
1057 let column = Some(focus.column().ix())
1058 .filter(|column| columns.view_column_ix(*column).is_some())
1059 .unwrap_or(first);
1060 Some((display_ix, column))
1061 });
1062 let (display_ix, column) = match focus {
1063 None => {
1064 let top = rows.first_focusable()?;
1065 let bottom = rows.last_focusable().unwrap_or(top);
1066 match navigation {
1067 Navigation::Last => (top, last),
1068 Navigation::LastRow => (bottom, first),
1069 Navigation::LastCell => (bottom, last),
1070 _ => (top, first),
1071 }
1072 }
1073 Some((origin, column)) => {
1074 let end = rows.len().saturating_sub(1);
1075 let page = page.max(1);
1076 let (target, column) = match navigation {
1077 Navigation::Up => (origin.saturating_sub(1), column),
1078 Navigation::Down => ((origin + 1).min(end), column),
1079 Navigation::PageUp => (origin.saturating_sub(page), column),
1080 Navigation::PageDown => ((origin + page).min(end), column),
1081 Navigation::PrevColumn => (origin, step_column(column, -1, columns)),
1082 Navigation::NextColumn => (origin, step_column(column, 1, columns)),
1083 Navigation::First => (origin, first),
1084 Navigation::Last => (origin, last),
1085 Navigation::FirstRow => (0, column),
1086 Navigation::LastRow => (end, column),
1087 Navigation::FirstCell => (0, first),
1088 Navigation::LastCell => (end, last),
1089 };
1090 (rows.focusable_toward(origin, target), column)
1091 }
1092 };
1093 rows.cell_at(display_ix, column_of(column))
1094 }
1095
1096 pub(super) fn focus_on(&mut self, target: FocusedCell<K, C>) {
1098 self.focus = Some(target);
1099 }
1100
1101 pub(super) fn focus_caption<R>(
1106 &mut self,
1107 path: &GroupPath,
1108 rows: ShownRows<'_, R>,
1109 columns: &impl ColumnView,
1110 ) -> bool
1111 where
1112 R: GridRow<Key = K, Column = C>,
1113 {
1114 let Some(column) = self.focus_column(columns) else {
1115 return false;
1116 };
1117 if rows.caption_ix(path).is_none() {
1118 return false;
1119 }
1120 self.focus = Some(FocusedCell::caption(path.clone(), column_of(column)));
1121 true
1122 }
1123
1124 pub(super) fn toggle_focused<R>(
1127 &mut self,
1128 rows: ShownRows<'_, R>,
1129 columns: &impl ColumnView,
1130 ) -> bool
1131 where
1132 R: GridRow<Key = K, Column = C>,
1133 {
1134 let Some(focus) = self.focus.clone() else {
1135 return false;
1136 };
1137 if focus.row().and_then(|key| rows.display_ix(key)).is_none()
1140 || !rows.takes_selection(&focus)
1141 {
1142 return false;
1143 }
1144 let edit = if self.mode.is_single() {
1145 SelectionEdit::Replace
1146 } else {
1147 SelectionEdit::Toggle
1148 };
1149 self.apply(edit, focus, rows, columns);
1150 true
1151 }
1152
1153 pub(super) fn select_all<R>(
1160 &mut self,
1161 rows: ShownRows<'_, R>,
1162 columns: &impl ColumnView,
1163 ) -> bool
1164 where
1165 R: GridRow<Key = K, Column = C>,
1166 {
1167 if self.mode.is_single() {
1168 return false;
1169 }
1170 let item = if self.mode.selects_cells() {
1171 columns.visible_set()
1172 } else {
1173 self.all
1174 };
1175 let mut map = FxHashMap::with_capacity_and_hasher(rows.len(), Default::default());
1176 rows.select_lines(0..rows.len(), item, &mut map);
1177 self.set_selection(GridSelection::from_map(map));
1178 true
1179 }
1180
1181 pub(super) fn clear(&mut self) {
1182 if !self.selection.is_empty() {
1183 self.set_selection(GridSelection::default());
1184 }
1185 }
1186
1187 pub(super) fn set_mode(&mut self, mode: SelectionMode) {
1190 if mode == self.mode {
1191 return;
1192 }
1193 let unit_changed = mode.selects_cells() != self.mode.selects_cells();
1194 self.mode = mode;
1195 self.drag = None;
1196 if unit_changed {
1197 self.clear();
1198 } else if mode.is_single() {
1199 let kept = self
1200 .focus
1201 .as_ref()
1202 .filter(|focus| self.is_selected(focus))
1203 .map(|focus| self.only(focus));
1204 self.set_selection(kept.unwrap_or_default());
1205 }
1206 }
1207
1208 pub(super) fn set_rows<R>(
1211 &mut self,
1212 keys: impl IntoIterator<Item = K>,
1213 snapshot: &StoreSnapshot<R>,
1214 columns: &impl ColumnView,
1215 ) where
1216 R: GridRow<Key = K, Column = C>,
1217 {
1218 let item = self.row_item(columns);
1219 let limit = if self.mode.is_single() { 1 } else { usize::MAX };
1220 self.set_selection(GridSelection::from_rows(
1221 keys.into_iter()
1222 .filter(|key| snapshot.row_id(key).is_some())
1223 .take(limit)
1224 .map(|key| (key, item)),
1225 ));
1226 }
1227
1228 pub(super) fn set_cells<R>(
1231 &mut self,
1232 cells: impl IntoIterator<Item = (K, C)>,
1233 snapshot: &StoreSnapshot<R>,
1234 ) where
1235 R: GridRow<Key = K, Column = C>,
1236 {
1237 let mut map: FxHashMap<K, ColumnSet> = FxHashMap::default();
1238 for (key, column) in cells {
1239 if snapshot.row_id(&key).is_none() {
1240 continue;
1241 }
1242 if self.mode.is_single() && !map.is_empty() {
1243 break;
1244 }
1245 map.entry(key)
1246 .or_default()
1247 .union_with(self.item(column.ix()));
1248 }
1249 self.set_selection(GridSelection::from_map(map));
1250 }
1251
1252 pub(super) fn select_row<R>(
1256 &mut self,
1257 key: Option<K>,
1258 rows: ShownRows<'_, R>,
1259 columns: &impl ColumnView,
1260 ) where
1261 R: GridRow<Key = K, Column = C>,
1262 {
1263 let key = key.filter(|key| rows.snapshot.row_id(key).is_some());
1264 let (Some(key), Some(column)) = (key, self.focus_column(columns)) else {
1265 self.clear();
1266 return;
1267 };
1268 let target = FocusedCell::new(key, column_of(column));
1269 self.set_selection(self.only(&target));
1270 if target.row().and_then(|key| rows.display_ix(key)).is_some() {
1271 self.focus = Some(target.clone());
1272 self.anchor = Some(target);
1273 }
1274 }
1275
1276 pub(super) fn set_focus<R>(
1280 &mut self,
1281 key: &K,
1282 column: Option<C>,
1283 rows: ShownRows<'_, R>,
1284 columns: &impl ColumnView,
1285 ) -> bool
1286 where
1287 R: GridRow<Key = K, Column = C>,
1288 {
1289 if rows.display_ix(key).is_none() {
1290 return false;
1291 }
1292 let column = column
1293 .map(|column| column.ix())
1294 .filter(|column| columns.view_column_ix(*column).is_some())
1295 .or_else(|| self.focus_column(columns));
1296 let Some(column) = column else {
1297 return false;
1298 };
1299 let target = FocusedCell::new(key.clone(), column_of(column));
1300 self.focus = Some(target.clone());
1301 self.anchor = Some(target);
1302 true
1303 }
1304
1305 pub(super) fn remove_rows<R>(&mut self, removed: &[RowId], previous: &StoreSnapshot<R>)
1309 where
1310 R: GridRow<Key = K>,
1311 {
1312 let remove = |selection: &mut GridSelection<K>| {
1313 let mut changed = false;
1314 for id in removed {
1315 if let Some(row) = previous.row(*id)
1316 && selection.contains(row.key())
1317 {
1318 selection.rows_mut().remove(row.key());
1319 changed = true;
1320 }
1321 }
1322 changed
1323 };
1324 if !self.selection.is_empty() && remove(&mut self.selection) {
1325 self.revision += 1;
1326 }
1327 if let Some(base) = self.drag.as_mut().and_then(|drag| drag.base.as_mut()) {
1328 remove(base);
1329 }
1330 }
1331
1332 pub(super) fn retain_present<R>(&mut self, snapshot: &StoreSnapshot<R>)
1335 where
1336 R: GridRow<Key = K>,
1337 {
1338 let missing = |key: &K| snapshot.row_id(key).is_none();
1339 if self.selection.retain(missing) {
1340 self.revision += 1;
1341 }
1342 if let Some(base) = self.drag.as_mut().and_then(|drag| drag.base.as_mut()) {
1343 base.retain(missing);
1344 }
1345 for cell in [&mut self.focus, &mut self.anchor] {
1346 if cell
1347 .as_ref()
1348 .and_then(|cell| cell.row())
1349 .is_some_and(missing)
1350 {
1351 *cell = None;
1352 }
1353 }
1354 }
1355
1356 pub(super) fn follow_removed<R>(
1360 &mut self,
1361 projection: &Projection,
1362 previous: &StoreSnapshot<R>,
1363 current: &StoreSnapshot<R>,
1364 ) where
1365 R: GridRow<Key = K>,
1366 {
1367 for cell in [&mut self.focus, &mut self.anchor] {
1368 let Some(focused) = cell.as_ref() else {
1369 continue;
1370 };
1371 let Some(id) = focused.row().and_then(|key| previous.row_id(key)) else {
1372 continue;
1373 };
1374 if current.row(id).is_some() {
1375 continue;
1376 }
1377 let column = focused.column();
1378 *cell = successor(projection, Some(id), |id| {
1379 current.row(id).map(|row| row.key().clone())
1380 })
1381 .map(|key| FocusedCell::new(key, column));
1382 }
1383 }
1384
1385 pub(super) fn follow_filtered<R>(
1389 &mut self,
1390 previous: &Projection,
1391 shown: &Projection,
1392 snapshot: &StoreSnapshot<R>,
1393 ) where
1394 R: GridRow<Key = K>,
1395 {
1396 for cell in [&mut self.focus, &mut self.anchor] {
1397 let Some(focused) = cell.as_ref().filter(|cell| cell.group().is_none()) else {
1399 continue;
1400 };
1401 let id = focused.row().and_then(|key| snapshot.row_id(key));
1402 if id.is_some_and(|id| shown.contains(id)) {
1403 continue;
1404 }
1405 let column = focused.column();
1406 *cell = successor(previous, id, |id| {
1407 shown
1408 .contains(id)
1409 .then(|| snapshot.row(id))
1410 .flatten()
1411 .map(|row| row.key().clone())
1412 })
1413 .map(|key| FocusedCell::new(key, column));
1414 }
1415 }
1416
1417 pub(super) fn move_off_hidden(&mut self, columns: &impl ColumnView) {
1420 for cell in [&mut self.focus, &mut self.anchor].into_iter().flatten() {
1421 let column = cell.column().ix();
1422 if columns.view_column_ix(column).is_none()
1423 && let Some(neighbour) = columns.visible_neighbour(column)
1424 {
1425 cell.column = column_of(neighbour);
1426 }
1427 }
1428 }
1429
1430 pub(super) fn begin_drag(
1434 &mut self,
1435 adds: bool,
1436 base: GridSelection<K>,
1437 target: FocusedCell<K, C>,
1438 ) {
1439 self.drag = Some(Drag {
1440 target,
1441 base: adds.then_some(base),
1442 });
1443 }
1444
1445 pub(super) fn drag_to(&mut self, target: FocusedCell<K, C>) -> bool {
1447 match &mut self.drag {
1448 Some(drag) if drag.target != target => {
1449 drag.target = target;
1450 true
1451 }
1452 _ => false,
1453 }
1454 }
1455
1456 pub(super) fn drag_target(&self) -> Option<&FocusedCell<K, C>> {
1457 self.drag.as_ref().map(|drag| &drag.target)
1458 }
1459
1460 pub(super) fn finish_drag<R>(&mut self, rows: ShownRows<'_, R>, columns: &impl ColumnView)
1463 where
1464 R: GridRow<Key = K, Column = C>,
1465 {
1466 let Some(drag) = self.drag.take() else {
1467 return;
1468 };
1469 if drag
1470 .target
1471 .row()
1472 .and_then(|key| rows.display_ix(key))
1473 .is_none()
1474 {
1475 return;
1477 }
1478 let edit = match drag.base {
1479 Some(base) => {
1480 self.set_selection(base);
1481 SelectionEdit::AddRange
1482 }
1483 None => SelectionEdit::Extend,
1484 };
1485 self.apply(edit, drag.target, rows, columns);
1486 }
1487
1488 pub(super) fn painter<'a, R>(
1490 &'a self,
1491 rows: ShownRows<'_, R>,
1492 columns: &impl ColumnView,
1493 ) -> SelectionPainter<'a, K>
1494 where
1495 R: GridRow<Key = K, Column = C>,
1496 {
1497 let Some(drag) = &self.drag else {
1498 return SelectionPainter {
1499 selection: Some(&self.selection),
1500 range: None,
1501 };
1502 };
1503 let target = drag.target.row().and_then(|key| rows.display_ix(key));
1504 let anchor = self
1505 .anchor
1506 .as_ref()
1507 .filter(|_| !self.mode.is_single())
1508 .and_then(|anchor| Some((rows.display_ix(anchor.row()?)?, anchor.column().ix())));
1509 let range = target.map(|to| {
1510 let to_column = drag.target.column().ix();
1511 let (from, from_column) = anchor.unwrap_or((to, to_column));
1512 (
1513 from.min(to)..=from.max(to),
1514 self.range_columns(from_column, to_column, columns),
1515 )
1516 });
1517 SelectionPainter {
1518 selection: drag.base.as_ref().filter(|_| !self.mode.is_single()),
1519 range,
1520 }
1521 }
1522}
1523
1524pub(super) struct Press<K, C> {
1526 mark: ChangeMark<K, C>,
1529 base: Option<GridSelection<K>>,
1533 target: FocusedCell<K, C>,
1534 origin: Point<Pixels>,
1535 deferred: bool,
1538 adds: bool,
1540 moved: bool,
1542 focus_only: bool,
1544 drag_candidate: Option<Modifiers>,
1547 drag_tried: bool,
1550}
1551
1552impl<D: LiveGridDelegate> LiveGridState<D> {
1553 pub fn with_selection_mode(mut self, mode: SelectionMode) -> Self {
1556 self.selection.set_mode(mode);
1557 self
1558 }
1559
1560 pub fn with_selection_scope(mut self, scope: impl SelectionScope<D::Row>) -> Self {
1564 self.selection_scope = Box::new(scope);
1565 self
1566 }
1567
1568 pub fn with_selection_gesture(mut self, gesture: impl SelectionGesture) -> Self {
1571 self.selection_gesture = Box::new(gesture);
1572 self
1573 }
1574
1575 pub fn with_context_menu_gesture(mut self, gesture: impl ContextMenuGesture) -> Self {
1580 self.context_menu_gesture = Box::new(gesture);
1581 self
1582 }
1583
1584 pub fn with_selection_policy(mut self, policy: impl SelectionPolicy<D::Row>) -> Self {
1589 self.selection_policy = Box::new(policy);
1590 self
1591 }
1592
1593 fn shown_rows<'a>(
1595 projection: &'a Projection,
1596 snapshot: &'a StoreSnapshot<D::Row>,
1597 policy: &'a dyn SelectionPolicy<D::Row>,
1598 ) -> ShownRows<'a, D::Row> {
1599 ShownRows::new(projection, snapshot).with_policy(policy)
1600 }
1601
1602 pub(super) fn is_context_press(
1605 &self,
1606 button: MouseButton,
1607 modifiers: Modifiers,
1608 click_count: usize,
1609 area: GridArea,
1610 ) -> bool {
1611 self.context_menu_gesture
1612 .opens_menu(&ContextMenuGestureContext::new(
1613 button,
1614 modifiers,
1615 click_count,
1616 area,
1617 ))
1618 }
1619
1620 pub fn selection_mode(&self) -> SelectionMode {
1622 self.selection.mode()
1623 }
1624
1625 pub fn selection(&self) -> &GridSelection<KeyOf<D>> {
1628 self.selection.selection()
1629 }
1630
1631 pub fn scoped_selection(&self) -> ScopedSelection<KeyOf<D>> {
1634 self.selection_scope.resolve(&SelectionScopeContext::new(
1635 self.selection.selection(),
1636 &self.projection,
1637 &self.snapshot,
1638 self.selection.mode(),
1639 ))
1640 }
1641
1642 pub fn focused_cell(&self) -> Option<&FocusedCell<KeyOf<D>, ColumnOf<D>>> {
1644 self.selection.focus()
1645 }
1646
1647 pub fn is_cell_selected(&self, key: &KeyOf<D>, column: ColumnOf<D>) -> bool {
1650 self.selection.selection().contains_cell(key, column.ix())
1651 }
1652
1653 #[cfg(any(test, feature = "test-support"))]
1656 pub fn is_focus_visible(&self) -> bool {
1657 self.focus_visible
1658 }
1659
1660 pub fn visible_keys(&self) -> Vec<KeyOf<D>> {
1663 self.painted
1664 .iter()
1665 .filter_map(|line| self.snapshot.row(line.row()?))
1666 .map(|row| row.key().clone())
1667 .collect()
1668 }
1669
1670 pub fn set_selection_mode(&mut self, mode: SelectionMode, cx: &mut Context<Self>) {
1674 self.edit_selection(SelectionOrigin::Programmatic, cx, |model, _, _| {
1675 model.set_mode(mode)
1676 });
1677 }
1678
1679 pub fn select_key(&mut self, key: Option<KeyOf<D>>, cx: &mut Context<Self>) {
1684 self.edit_selection(SelectionOrigin::Programmatic, cx, |model, rows, columns| {
1685 model.select_row(key, rows, columns)
1686 });
1687 }
1688
1689 pub fn set_selected_keys(
1693 &mut self,
1694 keys: impl IntoIterator<Item = KeyOf<D>>,
1695 cx: &mut Context<Self>,
1696 ) {
1697 self.edit_selection(SelectionOrigin::Programmatic, cx, |model, rows, columns| {
1698 model.set_rows(keys, rows.snapshot, columns)
1699 });
1700 }
1701
1702 pub fn set_selected_cells(
1706 &mut self,
1707 cells: impl IntoIterator<Item = (KeyOf<D>, ColumnOf<D>)>,
1708 cx: &mut Context<Self>,
1709 ) {
1710 self.edit_selection(SelectionOrigin::Programmatic, cx, |model, rows, _| {
1711 model.set_cells(cells, rows.snapshot)
1712 });
1713 }
1714
1715 pub fn select_all(&mut self, cx: &mut Context<Self>) {
1718 self.edit_selection(SelectionOrigin::Programmatic, cx, |model, rows, columns| {
1719 model.select_all(rows, columns)
1720 });
1721 }
1722
1723 pub fn clear_selection(&mut self, cx: &mut Context<Self>) {
1725 self.edit_selection(SelectionOrigin::Programmatic, cx, |model, _, _| {
1726 model.clear()
1727 });
1728 }
1729
1730 pub fn set_focused_cell(
1734 &mut self,
1735 key: &KeyOf<D>,
1736 column: Option<ColumnOf<D>>,
1737 cx: &mut Context<Self>,
1738 ) -> bool {
1739 let focused =
1740 self.edit_selection(SelectionOrigin::Programmatic, cx, |model, rows, columns| {
1741 model.set_focus(key, column, rows, columns)
1742 });
1743 if focused {
1744 self.reveal_focus();
1745 cx.notify();
1746 }
1747 focused
1748 }
1749
1750 pub fn set_focused_group(&mut self, path: &GroupPath, cx: &mut Context<Self>) -> bool {
1756 let focused =
1757 self.edit_selection(SelectionOrigin::Programmatic, cx, |model, rows, columns| {
1758 model.focus_caption(path, rows, columns)
1759 });
1760 if focused {
1761 self.reveal_focus();
1762 cx.notify();
1763 }
1764 focused
1765 }
1766
1767 pub(crate) fn pointer_target(&self) -> Option<&PointerTarget<KeyOf<D>>> {
1769 self.pointer_target.as_ref()
1770 }
1771
1772 pub(crate) fn command_scope(
1778 &self,
1779 target: Option<&LineTarget<KeyOf<D>>>,
1780 ) -> ScopedSelection<KeyOf<D>> {
1781 target
1782 .and_then(|target| self.line_scope(target))
1783 .unwrap_or_else(|| self.scoped_selection())
1784 }
1785
1786 fn line_scope(&self, target: &LineTarget<KeyOf<D>>) -> Option<ScopedSelection<KeyOf<D>>> {
1791 let rows = Self::shown_rows(&self.projection, &self.snapshot, &*self.selection_policy);
1792 let Some((line, under)) = rows.scope_line(target) else {
1793 return match target {
1794 LineTarget::Group(_) => Some(ScopedSelection::default()),
1795 LineTarget::Row(_) => None,
1796 };
1797 };
1798 let context = LineScopeContext::new(
1799 line,
1800 under,
1801 &self.projection,
1802 &self.snapshot,
1803 self.selection.mode(),
1804 );
1805 self.selection_policy.line_scope(&context)
1806 }
1807
1808 pub(crate) fn focus_target(&self) -> Option<LineTarget<KeyOf<D>>> {
1810 self.selection.focus().and_then(FocusedCell::line)
1811 }
1812
1813 pub(crate) fn confirm(&mut self, secondary: bool, cx: &mut Context<Self>) -> bool {
1819 let scoped = self.command_scope(self.focus_target().as_ref());
1820 if scoped.is_empty() {
1821 return false;
1822 }
1823 cx.emit(LiveGridEvent::Confirm {
1824 keys: scoped.shared_keys(),
1825 secondary,
1826 });
1827 true
1828 }
1829
1830 fn edit_selection<T>(
1833 &mut self,
1834 origin: SelectionOrigin,
1835 cx: &mut Context<Self>,
1836 edit: impl FnOnce(
1837 &mut SelectionModel<KeyOf<D>, ColumnOf<D>>,
1838 ShownRows<'_, D::Row>,
1839 &ColumnLayout<ColumnOf<D>>,
1840 ) -> T,
1841 ) -> T {
1842 let mark = self.selection.mark();
1843 let (projection, snapshot) = (self.projection.clone(), self.snapshot.clone());
1844 let result = edit(
1845 &mut self.selection,
1846 Self::shown_rows(&projection, &snapshot, &*self.selection_policy),
1847 &self.columns,
1848 );
1849 self.finish_change(mark, origin, cx);
1850 result
1851 }
1852
1853 pub(super) fn finish_change(
1857 &mut self,
1858 mark: ChangeMark<KeyOf<D>, ColumnOf<D>>,
1859 origin: SelectionOrigin,
1860 cx: &mut Context<Self>,
1861 ) -> bool {
1862 let (selection, focus) = self.selection.changes_since(&mark);
1863 if selection {
1864 cx.emit(LiveGridEvent::SelectionChange {
1865 selection: self.selection.selection().clone(),
1866 origin,
1867 });
1868 }
1869 if focus {
1870 if let Some(cell) = self.selection.focus() {
1871 self.set_current_column(cell.column().ix());
1872 }
1873 cx.emit(LiveGridEvent::FocusChange {
1874 cell: self.selection.focus().cloned(),
1875 origin,
1876 });
1877 }
1878 if selection || focus {
1879 cx.notify();
1880 }
1881 selection || focus
1882 }
1883
1884 pub(super) fn move_focus_off_hidden(
1886 &mut self,
1887 origin: SelectionOrigin,
1888 cx: &mut Context<Self>,
1889 ) {
1890 self.edit_selection(origin, cx, |model, _, columns| {
1891 model.move_off_hidden(columns)
1892 });
1893 }
1894
1895 fn page_rows(&self) -> usize {
1897 page_size(self.rows_area_height(), self.row_height)
1898 }
1899
1900 pub(super) fn navigate(
1903 &mut self,
1904 navigation: Navigation,
1905 extending: bool,
1906 cx: &mut Context<Self>,
1907 ) {
1908 if matches!(navigation, Navigation::PrevColumn | Navigation::NextColumn)
1911 && let Some(path) = self
1912 .selection
1913 .focus()
1914 .and_then(|focus| focus.group())
1915 .cloned()
1916 {
1917 self.caption_arrow(&path, navigation == Navigation::NextColumn, cx);
1918 return;
1919 }
1920 let page = self.page_rows();
1921 let (projection, snapshot) = (self.projection.clone(), self.snapshot.clone());
1922 let rows = Self::shown_rows(&projection, &snapshot, &*self.selection_policy);
1923 let Some(target) = self
1924 .selection
1925 .navigation_target(navigation, page, rows, &self.columns)
1926 else {
1927 return;
1928 };
1929 let context = SelectionGestureContext::new(
1930 GestureInput::Keyboard,
1931 Modifiers::default(),
1932 extending,
1933 self.selection.is_selected(&target),
1934 self.selection.mode(),
1935 );
1936 let edit = self.selection_gesture.edit(&context);
1937 let mark = self.selection.mark();
1938 self.selection.apply(edit, target, rows, &self.columns);
1939 match navigation {
1941 Navigation::PageDown => self.scroll_row += page,
1942 Navigation::PageUp => self.scroll_row = self.scroll_row.saturating_sub(page),
1943 _ => {}
1944 }
1945 self.clamp_scroll();
1946 self.reveal_focus();
1947 self.finish_change(mark, SelectionOrigin::User, cx);
1948 cx.notify();
1949 }
1950
1951 pub(super) fn reveal_focus(&mut self) {
1955 let Some(focus) = self.selection.focus() else {
1956 return;
1957 };
1958 let column = focus.column().ix();
1959 let is_caption = focus.group().is_some();
1960 let display_ix = ShownRows::new(&self.projection, &self.snapshot).line_of(focus);
1961 if let Some(display_ix) = display_ix {
1962 self.reveal_row(display_ix);
1963 }
1964 if !is_caption {
1965 let mut columns = ColumnSet::EMPTY;
1966 columns.insert(column);
1967 self.reveal_columns(columns);
1968 }
1969 self.clamp_scroll();
1970 }
1971
1972 pub(super) fn bind_selection_actions<E: InteractiveElement>(root: E, cx: &Context<Self>) -> E {
1974 use Navigation::*;
1975 root.on_action(cx.listener(|this, _: &SelectUp, _, cx| this.navigate(Up, false, cx)))
1976 .on_action(cx.listener(|this, _: &SelectDown, _, cx| this.navigate(Down, false, cx)))
1977 .on_action(
1978 cx.listener(|this, _: &SelectPrevColumn, _, cx| {
1979 this.navigate(PrevColumn, false, cx)
1980 }),
1981 )
1982 .on_action(
1983 cx.listener(|this, _: &SelectNextColumn, _, cx| {
1984 this.navigate(NextColumn, false, cx)
1985 }),
1986 )
1987 .on_action(
1988 cx.listener(|this, _: &SelectPageUp, _, cx| this.navigate(PageUp, false, cx)),
1989 )
1990 .on_action(
1991 cx.listener(|this, _: &SelectPageDown, _, cx| this.navigate(PageDown, false, cx)),
1992 )
1993 .on_action(cx.listener(|this, _: &SelectFirst, _, cx| this.navigate(First, false, cx)))
1994 .on_action(cx.listener(|this, _: &SelectLast, _, cx| this.navigate(Last, false, cx)))
1995 .on_action(
1996 cx.listener(|this, _: &SelectFirstRow, _, cx| this.navigate(FirstRow, false, cx)),
1997 )
1998 .on_action(
1999 cx.listener(|this, _: &SelectLastRow, _, cx| this.navigate(LastRow, false, cx)),
2000 )
2001 .on_action(
2002 cx.listener(|this, _: &SelectFirstCell, _, cx| this.navigate(FirstCell, false, cx)),
2003 )
2004 .on_action(
2005 cx.listener(|this, _: &SelectLastCell, _, cx| this.navigate(LastCell, false, cx)),
2006 )
2007 .on_action(
2008 cx.listener(|this, _: &ExtendSelectionUp, _, cx| this.navigate(Up, true, cx)),
2009 )
2010 .on_action(
2011 cx.listener(|this, _: &ExtendSelectionDown, _, cx| this.navigate(Down, true, cx)),
2012 )
2013 .on_action(cx.listener(|this, _: &ExtendSelectionPrevColumn, _, cx| {
2014 this.navigate(PrevColumn, true, cx)
2015 }))
2016 .on_action(cx.listener(|this, _: &ExtendSelectionNextColumn, _, cx| {
2017 this.navigate(NextColumn, true, cx)
2018 }))
2019 .on_action(
2020 cx.listener(|this, _: &ExtendSelectionPageUp, _, cx| {
2021 this.navigate(PageUp, true, cx)
2022 }),
2023 )
2024 .on_action(cx.listener(|this, _: &ExtendSelectionPageDown, _, cx| {
2025 this.navigate(PageDown, true, cx)
2026 }))
2027 .on_action(
2028 cx.listener(|this, _: &ExtendSelectionToFirst, _, cx| {
2029 this.navigate(First, true, cx)
2030 }),
2031 )
2032 .on_action(
2033 cx.listener(|this, _: &ExtendSelectionToLast, _, cx| this.navigate(Last, true, cx)),
2034 )
2035 .on_action(cx.listener(|this, _: &ExtendSelectionToFirstRow, _, cx| {
2036 this.navigate(FirstRow, true, cx)
2037 }))
2038 .on_action(cx.listener(|this, _: &ExtendSelectionToLastRow, _, cx| {
2039 this.navigate(LastRow, true, cx)
2040 }))
2041 .on_action(cx.listener(|this, _: &ExtendSelectionToFirstCell, _, cx| {
2042 this.navigate(FirstCell, true, cx)
2043 }))
2044 .on_action(cx.listener(|this, _: &ExtendSelectionToLastCell, _, cx| {
2045 this.navigate(LastCell, true, cx)
2046 }))
2047 .on_action(cx.listener(Self::on_select_all))
2048 .on_action(cx.listener(Self::on_toggle_selection))
2049 .on_action(cx.listener(Self::on_confirm))
2050 .on_action(cx.listener(Self::on_cancel))
2051 }
2052
2053 fn on_select_all(&mut self, _: &SelectAll, _: &mut gpui_kit::Window, cx: &mut Context<Self>) {
2055 let handled = self.edit_selection(SelectionOrigin::User, cx, |model, rows, columns| {
2056 model.select_all(rows, columns)
2057 });
2058 if !handled {
2059 cx.propagate();
2060 }
2061 }
2062
2063 fn on_toggle_selection(
2065 &mut self,
2066 _: &ToggleSelection,
2067 _: &mut gpui_kit::Window,
2068 cx: &mut Context<Self>,
2069 ) {
2070 let handled = self.edit_selection(SelectionOrigin::User, cx, |model, rows, columns| {
2071 model.toggle_focused(rows, columns)
2072 });
2073 if !handled {
2074 cx.propagate();
2075 }
2076 }
2077
2078 fn on_confirm(&mut self, action: &Confirm, _: &mut gpui_kit::Window, cx: &mut Context<Self>) {
2081 if !self.confirm(action.secondary, cx) {
2082 cx.propagate();
2083 }
2084 }
2085
2086 fn on_cancel(&mut self, _: &Cancel, window: &mut gpui_kit::Window, cx: &mut Context<Self>) {
2090 if self.cancel_row_drag(window, cx) {
2091 return;
2092 }
2093 if self.selection.selection().is_empty() {
2094 cx.propagate();
2095 return;
2096 }
2097 self.edit_selection(SelectionOrigin::User, cx, |model, _, _| model.clear());
2098 }
2099
2100 fn row_at(&self, position: Point<Pixels>) -> Option<KeyOf<D>> {
2102 self.line_at(position)
2103 .and_then(|line| self.painted.get(line)?.row())
2104 .and_then(|id| self.snapshot.row(id))
2105 .map(|row| row.key().clone())
2106 }
2107
2108 fn column_toward(&self, position: Point<Pixels>) -> Option<ColumnIx> {
2111 self.column_at_position(position).or_else(|| {
2112 if position.x < self.body_bounds.origin.x {
2113 first_column(&self.columns)
2114 } else {
2115 last_column(&self.columns)
2116 }
2117 })
2118 }
2119
2120 pub(crate) fn on_body_mouse_down(
2128 &mut self,
2129 event: &MouseDownEvent,
2130 window: &mut gpui_kit::Window,
2131 cx: &mut Context<Self>,
2132 ) {
2133 let context = self.is_context_press(
2134 event.button,
2135 event.modifiers,
2136 event.click_count,
2137 GridArea::Body,
2138 );
2139 if self.is_menu_open() && !context {
2143 self.press_series = None;
2144 return;
2145 }
2146 let (pinned, row) = match self.pinned_row_at(event.position) {
2148 Some((edge, key)) => (Some(edge), Some(key)),
2149 None => (None, self.row_at(event.position)),
2150 };
2151 let caption = pinned
2153 .is_none()
2154 .then(|| self.caption_hit(event.position))
2155 .flatten();
2156 let column = match &caption {
2157 Some((_, column)) => *column,
2158 None => row.as_ref().and(self.column_toward(event.position)),
2159 };
2160 self.pointer_target = Some(PointerTarget {
2161 row,
2162 group: caption.map(|(path, _)| path),
2163 column,
2164 button: event.button,
2165 position: event.position,
2166 pinned,
2167 });
2168 if context {
2169 self.context_press(event.modifiers, cx);
2170 self.open_body_menu(window, cx);
2171 return;
2172 }
2173 let disclosure = pinned
2175 .is_none()
2176 .then(|| self.disclosure_hit(event.position))
2177 .flatten();
2178 if let Some((line, area)) = &disclosure {
2179 self.press_disclosure(line, *area, event, cx);
2180 }
2181 let chevron = disclosure
2182 .as_ref()
2183 .and_then(|(line, area)| (*area == ExpandArea::Chevron).then_some(line));
2184 if chevron.is_none()
2187 && let Some(target) = self.pressed_target()
2188 {
2189 self.begin_click(target, event);
2190 }
2191 if pinned.is_some() {
2192 self.finish_press(cx);
2196 self.press_series = None;
2197 } else if event.button == MouseButton::Left {
2198 let group = self
2199 .pointer_target
2200 .as_ref()
2201 .and_then(|pointer| pointer.group())
2202 .cloned();
2203 if let Some(group) = group {
2204 if let Some(column) = self.focus_column_or_first() {
2206 self.focus_press(FocusedCell::caption(group, column), cx);
2207 }
2208 } else if chevron.is_some() {
2209 self.finish_press(cx);
2213 self.press_series = None;
2214 } else {
2215 self.primary_press(event.modifiers, event.click_count, window, cx);
2216 }
2217 }
2218 }
2219
2220 fn focus_column_or_first(&self) -> Option<ColumnOf<D>> {
2222 self.selection
2223 .focus()
2224 .map(|focus| focus.column().ix())
2225 .filter(|column| self.columns.view_column_ix(*column).is_some())
2226 .or_else(|| first_column(&self.columns))
2227 .map(column_of)
2228 }
2229
2230 pub(super) fn focus_press(
2234 &mut self,
2235 target: FocusedCell<KeyOf<D>, ColumnOf<D>>,
2236 cx: &mut Context<Self>,
2237 ) {
2238 self.finish_press(cx);
2239 self.press_series = None;
2240 let mark = self.selection.mark();
2241 self.selection.focus_on(target.clone());
2242 let origin = self
2243 .pointer_target
2244 .as_ref()
2245 .map_or(Point::default(), PointerTarget::position);
2246 self.press = Some(Press {
2247 mark,
2248 base: None,
2249 target,
2250 origin,
2251 deferred: false,
2252 adds: false,
2253 moved: false,
2254 focus_only: true,
2255 drag_candidate: None,
2256 drag_tried: false,
2257 });
2258 cx.notify();
2259 }
2260
2261 fn pressed_cell(&self) -> Option<FocusedCell<KeyOf<D>, ColumnOf<D>>> {
2264 let pointer = self
2265 .pointer_target
2266 .as_ref()
2267 .filter(|pointer| pointer.pinned.is_none())?;
2268 let key = pointer.row()?.clone();
2269 Some(FocusedCell::new(key, column_of(pointer.column()?)))
2270 }
2271
2272 pub(super) fn primary_press(
2277 &mut self,
2278 modifiers: Modifiers,
2279 click_count: usize,
2280 window: &mut gpui_kit::Window,
2281 cx: &mut Context<Self>,
2282 ) {
2283 self.finish_press(cx);
2285 let pressed = self.pressed_cell();
2286 if click_count >= 2 {
2287 if self.start_edit_by_press(click_count, window, cx) {
2291 return;
2292 }
2293 if self.links.series_on_link {
2296 return;
2297 }
2298 if let Some(key) = self.press_series.clone() {
2299 self.confirm_row(&key, cx);
2300 }
2301 return;
2302 }
2303 self.press_series = pressed.as_ref().and_then(|cell| cell.row().cloned());
2304 self.links.series_on_link = self.is_pointer_on_link();
2305 let Some(target) = pressed else {
2307 return;
2308 };
2309 let selected = self.selection.is_selected(&target);
2310 let drag_candidate = target
2312 .row()
2313 .is_some_and(|key| {
2314 self.is_row_drag_press(key, target.column(), selected, modifiers, click_count)
2315 })
2316 .then_some(modifiers);
2317 let context = SelectionGestureContext::new(
2318 GestureInput::Pointer,
2319 modifiers,
2320 false,
2321 selected,
2322 self.selection.mode(),
2323 );
2324 let edit = self.selection_gesture.edit(&context);
2325 let mark = self.selection.mark();
2326 let base = (edit == SelectionEdit::AddRange).then(|| self.selection.selection().clone());
2327 let deferred =
2328 edit == SelectionEdit::Replace && selected && self.selection.holds_more_than(&target);
2329 if deferred {
2330 self.selection.focus_on(target.clone());
2331 } else {
2332 let (projection, snapshot) = (self.projection.clone(), self.snapshot.clone());
2333 self.selection.apply(
2334 edit,
2335 target.clone(),
2336 Self::shown_rows(&projection, &snapshot, &*self.selection_policy),
2337 &self.columns,
2338 );
2339 }
2340 let origin = self
2341 .pointer_target
2342 .as_ref()
2343 .map_or(Point::default(), PointerTarget::position);
2344 self.press = Some(Press {
2345 mark,
2346 base,
2347 target,
2348 origin,
2349 deferred,
2350 adds: matches!(edit, SelectionEdit::Toggle | SelectionEdit::AddRange),
2351 moved: false,
2352 focus_only: false,
2353 drag_candidate,
2354 drag_tried: false,
2355 });
2356 cx.notify();
2357 }
2358
2359 pub(super) fn context_press(&mut self, modifiers: Modifiers, cx: &mut Context<Self>) {
2364 self.finish_press(cx);
2365 if let Some(group) = self
2366 .pointer_target
2367 .as_ref()
2368 .and_then(|pointer| pointer.group())
2369 .cloned()
2370 {
2371 self.edit_selection(SelectionOrigin::User, cx, |model, rows, columns| {
2372 model.focus_caption(&group, rows, columns)
2373 });
2374 return;
2375 }
2376 let Some(target) = self.pressed_cell() else {
2377 return;
2378 };
2379 let context = SelectionGestureContext::new(
2380 GestureInput::ContextPointer,
2381 modifiers,
2382 false,
2383 self.selection.is_selected(&target),
2384 self.selection.mode(),
2385 );
2386 let edit = self.selection_gesture.edit(&context);
2387 self.edit_selection(SelectionOrigin::User, cx, |model, rows, columns| {
2388 model.apply(edit, target, rows, columns)
2389 });
2390 }
2391
2392 fn confirm_row(&mut self, key: &KeyOf<D>, cx: &mut Context<Self>) {
2397 let scoped = match self.line_scope(&LineTarget::Row(key.clone())) {
2398 Some(scope) => Some(scope).filter(|scope| !scope.is_empty()),
2399 None => Some(self.scoped_selection()).filter(|scope| scope.keys().contains(key)),
2400 };
2401 if let Some(scoped) = scoped {
2402 cx.emit(LiveGridEvent::Confirm {
2403 keys: scoped.shared_keys(),
2404 secondary: false,
2405 });
2406 }
2407 }
2408
2409 pub(crate) fn on_body_drag_move(&mut self, position: Point<Pixels>, cx: &mut Context<Self>) {
2412 let Some(press) = &mut self.press else {
2413 return;
2414 };
2415 if press.focus_only {
2417 return;
2418 }
2419 if !self.selection.is_dragging() {
2420 if !is_past_drag_threshold(press.origin, position) {
2421 return;
2422 }
2423 press.moved = true;
2424 if let Some(modifiers) = press.drag_candidate {
2428 let distance = (position - press.origin).magnitude();
2429 let threshold = f64::from(f32::from(self.drag.threshold()));
2430 if !press.drag_tried && distance >= threshold {
2431 press.drag_tried = true;
2432 if let Some(pressed) = press.target.row().cloned() {
2433 self.arm_row_drag(&pressed, modifiers, cx);
2434 }
2435 }
2436 return;
2437 }
2438 if press.deferred {
2439 return;
2442 }
2443 let target = press.target.clone();
2444 let adds = press.adds;
2445 let base = match press.base.take() {
2446 Some(base) => base,
2447 None if adds => self.selection.before_toggle(&target),
2448 None => GridSelection::default(),
2449 };
2450 self.selection.begin_drag(adds, base, target);
2451 }
2452 self.drag_scroll.last_drag_position = Some(position);
2453 self.update_drag_target(position, cx);
2454 let delta = AutoScroll::compute_delta(position.y, self.rows_bounds());
2457 self.drag_scroll.set(delta, cx, |delta, state, cx| {
2458 state.auto_scroll_rows(delta, cx)
2459 });
2460 }
2461
2462 fn update_drag_target(&mut self, position: Point<Pixels>, cx: &mut Context<Self>) {
2465 let Some(target) = self.drag_target_at(position) else {
2466 return;
2467 };
2468 if self.selection.drag_to(target) {
2469 cx.notify();
2470 }
2471 }
2472
2473 fn drag_target_at(
2474 &self,
2475 position: Point<Pixels>,
2476 ) -> Option<FocusedCell<KeyOf<D>, ColumnOf<D>>> {
2477 let rows = ShownRows::new(&self.projection, &self.snapshot);
2478 let lines = ((self.rows_area_height() + self.scroll_offset) / self.row_height).ceil();
2479 let lines = (lines as usize).max(1);
2480 let y = position.y - self.rows_bounds().origin.y + self.scroll_offset;
2481 let line = if y < px(0.) {
2482 0
2483 } else {
2484 ((y / self.row_height).floor() as usize).min(lines - 1)
2485 };
2486 let display_ix = rows.nearest_live(self.scroll_row + line)?;
2487 let key = rows.key_at(display_ix)?.clone();
2488 let column = self
2489 .column_at_position(position)
2490 .or_else(|| {
2491 self.selection
2492 .drag_target()
2493 .map(|target| target.column().ix())
2494 })
2495 .or_else(|| self.column_toward(position))?;
2496 Some(FocusedCell::new(key, column_of(column)))
2497 }
2498
2499 fn auto_scroll_rows(&mut self, delta: Pixels, cx: &mut Context<Self>) {
2502 if !self.selection.is_dragging() {
2503 self.drag_scroll.stop();
2504 return;
2505 }
2506 let before = (self.scroll_row, self.scroll_offset);
2507 self.scroll_rows_by(delta);
2508 if (self.scroll_row, self.scroll_offset) == before {
2509 return;
2510 }
2511 if let Some(position) = self.drag_scroll.last_drag_position {
2512 self.update_drag_target(position, cx);
2513 }
2514 cx.notify();
2515 }
2516
2517 pub(crate) fn on_body_mouse_up(&mut self, cx: &mut Context<Self>) {
2519 self.finish_press(cx);
2520 }
2521
2522 fn finish_press(&mut self, cx: &mut Context<Self>) {
2526 let Some(press) = self.press.take() else {
2527 return;
2528 };
2529 self.drag_scroll.stop();
2530 self.unarm_row_drag();
2532 let (projection, snapshot) = (self.projection.clone(), self.snapshot.clone());
2533 let rows = Self::shown_rows(&projection, &snapshot, &*self.selection_policy);
2534 if self.selection.is_dragging() {
2535 self.selection.finish_drag(rows, &self.columns);
2536 } else if press.deferred
2537 && !press.moved
2538 && press
2539 .target
2540 .row()
2541 .and_then(|key| rows.display_ix(key))
2542 .is_some()
2543 {
2544 self.selection
2545 .apply(SelectionEdit::Replace, press.target, rows, &self.columns);
2546 }
2547 self.finish_change(press.mark, SelectionOrigin::User, cx);
2548 cx.notify();
2549 }
2550}
2551
2552#[cfg(test)]
2553mod tests {
2554 use std::{
2555 collections::{HashMap, HashSet},
2556 sync::Arc,
2557 time::Instant,
2558 };
2559
2560 use super::*;
2561 use crate::{
2562 engine::{ProjectionEngine as _, Rebuild, SortDirection, ViewSpec},
2563 highlight::HighlightPolicy,
2564 model::CellValue,
2565 policy::{ProjectionScope, StandardSelectionGesture},
2566 store::{Ingest, Op},
2567 };
2568
2569 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2570 struct Col(u16);
2571
2572 const COLUMNS: [Col; 4] = [Col(0), Col(1), Col(2), Col(3)];
2573
2574 impl GridColumns for Col {
2575 fn all() -> &'static [Self] {
2576 &COLUMNS
2577 }
2578
2579 fn id(self) -> &'static str {
2580 ["c0", "c1", "c2", "c3"][self.0 as usize]
2581 }
2582
2583 fn ix(self) -> ColumnIx {
2584 ColumnIx::new(self.0)
2585 }
2586 }
2587
2588 #[derive(Debug)]
2589 struct Row {
2590 id: u32,
2591 price: i64,
2592 }
2593
2594 impl GridRow for Row {
2595 type Key = u32;
2596 type Column = Col;
2597
2598 fn key(&self) -> &u32 {
2599 &self.id
2600 }
2601
2602 fn value(&self, column: Col) -> CellValue<'_> {
2603 match column.0 {
2604 0 => CellValue::Int(self.id.into()),
2605 1 => CellValue::Int(self.price),
2606 _ => CellValue::Empty,
2607 }
2608 }
2609 }
2610
2611 fn row(id: u32, price: i64) -> Op<Row> {
2612 Op::Upsert(Arc::new(Row { id, price }))
2613 }
2614
2615 struct Visible(Vec<ColumnIx>);
2617
2618 impl ColumnView for Visible {
2619 fn view_column_ix(&self, column: ColumnIx) -> Option<ViewColumnIx> {
2620 let position = self.0.iter().position(|ix| *ix == column)?;
2621 Some(ViewColumnIx::new(position as u16))
2622 }
2623
2624 fn column_at(&self, view: ViewColumnIx) -> Option<ColumnIx> {
2625 self.0.get(view.get()).copied()
2626 }
2627
2628 fn visible_count(&self) -> usize {
2629 self.0.len()
2630 }
2631
2632 fn visible_set(&self) -> ColumnSet {
2633 set(self.0.iter().copied())
2634 }
2635
2636 fn visible_neighbour(&self, column: ColumnIx) -> Option<ColumnIx> {
2637 let right = self.0.iter().copied().filter(|ix| *ix > column).min();
2639 right.or_else(|| self.0.iter().copied().filter(|ix| *ix < column).max())
2640 }
2641 }
2642
2643 fn set(columns: impl IntoIterator<Item = ColumnIx>) -> ColumnSet {
2644 columns.into_iter().fold(ColumnSet::EMPTY, |mut set, ix| {
2645 set.insert(ix);
2646 set
2647 })
2648 }
2649
2650 fn cols(columns: &[u16]) -> ColumnSet {
2651 set(columns.iter().copied().map(ColumnIx::new))
2652 }
2653
2654 fn all_visible() -> Visible {
2655 Visible((0..4).map(ColumnIx::new).collect())
2656 }
2657
2658 struct World {
2660 ingest: Ingest<Row>,
2661 snapshot: Arc<StoreSnapshot<Row>>,
2662 projection: Projection,
2663 }
2664
2665 impl World {
2666 fn new(count: u32) -> Self {
2668 let mut ingest = Ingest::new(HighlightPolicy::default(), Instant::now());
2669 ingest.apply((0..count).map(|id| row(id, id.into())), Instant::now());
2670 let snapshot = ingest.snapshot();
2671 let projection = Rebuild::default().rebuild(&snapshot, &ViewSpec::new());
2672 Self {
2673 ingest,
2674 snapshot,
2675 projection,
2676 }
2677 }
2678
2679 fn rows(&self) -> ShownRows<'_, Row> {
2680 ShownRows::new(&self.projection, &self.snapshot)
2681 }
2682
2683 fn apply(
2685 &mut self,
2686 ops: impl IntoIterator<Item = Op<Row>>,
2687 ) -> (Arc<StoreSnapshot<Row>>, crate::store::ChangeSet) {
2688 let changes = self.ingest.apply(ops, Instant::now());
2689 let previous = std::mem::replace(&mut self.snapshot, self.ingest.snapshot());
2690 (previous, changes)
2691 }
2692
2693 fn rebuild(&mut self, view: &ViewSpec<Row>) -> Projection {
2695 std::mem::replace(
2696 &mut self.projection,
2697 Rebuild::default().rebuild(&self.snapshot, view),
2698 )
2699 }
2700 }
2701
2702 fn at(key: u32, column: u16) -> FocusedCell<u32, Col> {
2703 FocusedCell::new(key, Col(column))
2704 }
2705
2706 fn keys(model: &SelectionModel<u32, Col>) -> Vec<u32> {
2707 let mut keys: Vec<u32> = model.selection().iter().copied().collect();
2708 keys.sort_unstable();
2709 keys
2710 }
2711
2712 #[test]
2713 fn test_selection_edits() {
2714 use SelectionEdit::*;
2715 let world = World::new(20);
2716 let columns = all_visible();
2717 let rows = world.rows();
2718
2719 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSet);
2721 model.apply(Replace, at(3, 1), rows, &columns);
2722 assert_eq!(keys(&model), [3]);
2723 assert_eq!(model.selection().columns(&3), ColumnSet::first(4));
2724 model.apply(Extend, at(6, 2), rows, &columns);
2725 assert_eq!(keys(&model), [3, 4, 5, 6]);
2726 assert_eq!(model.focus(), Some(&at(6, 2)));
2727 model.apply(Extend, at(1, 0), rows, &columns);
2728 assert_eq!(keys(&model), [1, 2, 3], "Extend drops the rest");
2729 model.apply(Toggle, at(9, 0), rows, &columns);
2730 assert_eq!(keys(&model), [1, 2, 3, 9]);
2731 model.apply(AddRange, at(11, 0), rows, &columns);
2732 assert_eq!(keys(&model), [1, 2, 3, 9, 10, 11], "the anchor is 9");
2733 model.apply(Toggle, at(2, 0), rows, &columns);
2734 assert_eq!(keys(&model), [1, 3, 9, 10, 11]);
2735 model.apply(Keep, at(15, 3), rows, &columns);
2736 assert_eq!(keys(&model), [1, 3, 9, 10, 11]);
2737 assert_eq!(model.focus(), Some(&at(15, 3)));
2738 model.apply(Extend, at(4, 0), rows, &columns);
2740 assert_eq!(keys(&model), [2, 3, 4]);
2741
2742 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSingle);
2744 model.apply(Replace, at(3, 0), rows, &columns);
2745 model.apply(Extend, at(6, 0), rows, &columns);
2746 assert_eq!(keys(&model), [6]);
2747 model.apply(AddRange, at(8, 0), rows, &columns);
2748 assert_eq!(keys(&model), [8]);
2749 model.apply(Toggle, at(8, 0), rows, &columns);
2750 assert!(model.selection().is_empty());
2751 model.apply(Toggle, at(2, 0), rows, &columns);
2752 assert_eq!(keys(&model), [2]);
2753
2754 let shuffled = Visible([0, 3, 1].map(ColumnIx::new).to_vec());
2757 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::CellSet);
2758 model.apply(Replace, at(3, 0), rows, &shuffled);
2759 assert_eq!(model.selection().columns(&3), cols(&[0]));
2760 model.apply(Extend, at(5, 3), rows, &shuffled);
2761 assert_eq!(keys(&model), [3, 4, 5]);
2762 assert_eq!(model.selection().columns(&4), cols(&[0, 3]));
2763 model.apply(Extend, at(4, 1), rows, &shuffled);
2764 assert_eq!(keys(&model), [3, 4]);
2765 assert_eq!(model.selection().columns(&3), cols(&[0, 1, 3]));
2766 assert!(!model.selection().contains_cell(&3, ColumnIx::new(2)));
2767 model.apply(Toggle, at(3, 3), rows, &shuffled);
2768 assert_eq!(model.selection().columns(&3), cols(&[0, 1]));
2769 model.apply(Toggle, at(10, 1), rows, &shuffled);
2770 model.apply(AddRange, at(11, 1), rows, &shuffled);
2771 assert_eq!(model.selection().columns(&11), cols(&[1]));
2772 assert_eq!(model.selection().columns(&4), cols(&[0, 1, 3]));
2773 model.apply(Toggle, at(11, 1), rows, &shuffled);
2775 assert!(!model.selection().contains(&11));
2776 assert!(model.holds_more_than(&at(4, 0)));
2778 assert!(model.is_selected(&at(4, 0)));
2779 assert!(!model.is_selected(&at(4, 2)));
2780
2781 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::CellSingle);
2783 model.apply(Replace, at(3, 0), rows, &shuffled);
2784 model.apply(Extend, at(5, 1), rows, &shuffled);
2785 assert_eq!(keys(&model), [5]);
2786 assert_eq!(model.selection().columns(&5), cols(&[1]));
2787 assert!(!model.holds_more_than(&at(5, 1)));
2788
2789 let mut world = World::new(20);
2791 world.apply([Op::Remove(4), Op::Remove(5)]);
2792 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSet);
2793 model.apply(Replace, at(3, 0), world.rows(), &columns);
2794 model.apply(Extend, at(7, 0), world.rows(), &columns);
2795 assert_eq!(keys(&model), [3, 6, 7]);
2796 assert!(model.select_all(world.rows(), &columns));
2797 assert_eq!(model.selection().len(), 18);
2798 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::CellSet);
2801 assert!(model.select_all(world.rows(), &shuffled));
2802 assert_eq!(model.selection().columns(&0), cols(&[0, 1, 3]));
2803 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSingle);
2804 assert!(!model.select_all(world.rows(), &columns));
2805 }
2806
2807 #[test]
2808 fn test_mode_changes_and_programmatic_selection() {
2809 let world = World::new(10);
2810 let columns = all_visible();
2811 let rows = world.rows();
2812 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSet);
2813 model.apply(SelectionEdit::Replace, at(2, 1), rows, &columns);
2814 model.apply(SelectionEdit::Extend, at(4, 1), rows, &columns);
2815 model.set_mode(SelectionMode::RowSingle);
2817 assert_eq!(keys(&model), [4]);
2818 model.set_mode(SelectionMode::CellSingle);
2820 assert!(model.selection().is_empty());
2821
2822 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSet);
2823 model.set_rows([1, 3, 99], &world.snapshot, &columns);
2824 assert_eq!(keys(&model), [1, 3], "a key the snapshot lacks is dropped");
2825 model.set_cells([(5, Col(2)), (6, Col(1))], &world.snapshot);
2826 assert_eq!(keys(&model), [5, 6]);
2827 assert_eq!(model.selection().columns(&5), ColumnSet::first(4));
2828 model.set_mode(SelectionMode::CellSet);
2829 model.set_cells([(5, Col(2)), (5, Col(1)), (99, Col(1))], &world.snapshot);
2830 assert_eq!(model.selection().columns(&5), cols(&[1, 2]));
2831 model.set_rows([7], &world.snapshot, &columns);
2832 assert_eq!(model.selection().columns(&7), ColumnSet::first(4));
2833
2834 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSingle);
2836 model.select_row(Some(8), rows, &columns);
2837 assert_eq!(keys(&model), [8]);
2838 assert_eq!(model.focus(), Some(&at(8, 0)));
2839 model.select_row(Some(99), rows, &columns);
2840 assert!(model.selection().is_empty());
2841 assert_eq!(model.focus(), Some(&at(8, 0)));
2842 assert!(model.set_focus(&3, Some(Col(2)), rows, &columns));
2843 assert_eq!(model.focus(), Some(&at(3, 2)));
2844 assert!(model.set_focus(&5, None, rows, &columns));
2845 assert_eq!(model.focus(), Some(&at(5, 2)), "None keeps the column");
2846 assert!(!model.set_focus(&99, None, rows, &columns));
2847
2848 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSingle);
2850 model.set_focus(&3, Some(Col(1)), rows, &columns);
2851 model.move_off_hidden(&Visible([0, 2, 3].map(ColumnIx::new).to_vec()));
2852 assert_eq!(model.focus(), Some(&at(3, 2)));
2853 model.set_focus(&3, Some(Col(3)), rows, &columns);
2854 model.move_off_hidden(&Visible([0, 1].map(ColumnIx::new).to_vec()));
2855 assert_eq!(model.focus(), Some(&at(3, 1)));
2856
2857 assert_eq!(SelectionMode::RowSet.selection_unit(), SelectionUnit::Row);
2858 assert_eq!(
2859 SelectionMode::CellSingle.selection_unit(),
2860 SelectionUnit::Cell
2861 );
2862 }
2863
2864 #[test]
2865 fn test_navigation_targets() {
2866 let mut world = World::new(20);
2867 let columns = Visible([0, 3, 1].map(ColumnIx::new).to_vec());
2868 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSingle);
2869 let target = |model: &SelectionModel<u32, Col>, world: &World, navigation| {
2870 model
2871 .navigation_target(navigation, 5, world.rows(), &columns)
2872 .expect("a target")
2873 };
2874 assert_eq!(target(&model, &world, Navigation::Down), at(0, 0));
2876 assert_eq!(target(&model, &world, Navigation::Last), at(0, 1));
2877 assert_eq!(target(&model, &world, Navigation::LastCell), at(19, 1));
2878 assert_eq!(target(&model, &world, Navigation::LastRow), at(19, 0));
2879
2880 model.set_focus(&10, Some(Col(3)), world.rows(), &columns);
2881 assert_eq!(target(&model, &world, Navigation::Up), at(9, 3));
2882 assert_eq!(target(&model, &world, Navigation::PageDown), at(15, 3));
2883 assert_eq!(target(&model, &world, Navigation::PageUp), at(5, 3));
2884 assert_eq!(target(&model, &world, Navigation::PrevColumn), at(10, 0));
2885 assert_eq!(target(&model, &world, Navigation::NextColumn), at(10, 1));
2886 assert_eq!(target(&model, &world, Navigation::First), at(10, 0));
2887 assert_eq!(target(&model, &world, Navigation::FirstCell), at(0, 0));
2888 assert_eq!(target(&model, &world, Navigation::LastRow), at(19, 3));
2889 model.set_focus(&19, Some(Col(1)), world.rows(), &columns);
2891 assert_eq!(target(&model, &world, Navigation::Down), at(19, 1));
2892 assert_eq!(target(&model, &world, Navigation::NextColumn), at(19, 1));
2893 assert_eq!(target(&model, &world, Navigation::PageDown), at(19, 1));
2894
2895 world.apply([Op::Remove(11), Op::Remove(12), Op::Remove(0)]);
2898 model.set_focus(&10, None, world.rows(), &columns);
2899 assert_eq!(target(&model, &world, Navigation::Down), at(13, 1));
2900 assert_eq!(target(&model, &world, Navigation::FirstRow), at(1, 1));
2901 model.set_focus(&1, None, world.rows(), &columns);
2902 assert_eq!(target(&model, &world, Navigation::Up), at(1, 1));
2903 }
2904
2905 #[test]
2906 fn test_successor_rule() {
2907 let columns = all_visible();
2908 let mut world = World::new(10);
2909 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSingle);
2910 model.apply(SelectionEdit::Replace, at(5, 2), world.rows(), &columns);
2911
2912 let (previous, _) = world.apply([Op::Remove(5)]);
2914 model.follow_removed(&world.projection, &previous, &world.snapshot);
2915 assert_eq!(model.focus(), Some(&at(6, 2)));
2916 assert_eq!(model.anchor, Some(at(6, 2)), "the anchor follows too");
2917 model.remove_rows(&[], &previous);
2919 let (previous, _) = world.apply([6, 7, 8, 9].map(Op::Remove));
2921 model.follow_removed(&world.projection, &previous, &world.snapshot);
2922 assert_eq!(model.focus(), Some(&at(4, 2)));
2923 let (previous, _) = world.apply([0, 1, 2, 3, 4].map(Op::Remove));
2925 model.follow_removed(&world.projection, &previous, &world.snapshot);
2926 assert_eq!(model.focus(), None);
2927
2928 let mut world = World::new(10);
2930 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSet);
2931 model.apply(SelectionEdit::Replace, at(5, 0), world.rows(), &columns);
2932 let even = ViewSpec::new().with_filter(ColumnSet::first(1), |row: &Row| row.id % 2 == 0);
2933 let previous = world.rebuild(&even);
2934 model.follow_filtered(&previous, &world.projection, &world.snapshot);
2935 assert_eq!(model.focus(), Some(&at(6, 0)));
2936 assert_eq!(keys(&model), [5], "a filter keeps the selection");
2937 model.set_focus(&8, None, world.rows(), &columns);
2939 let low = ViewSpec::new().with_filter(ColumnSet::first(1), |row: &Row| row.id < 5);
2940 let previous = world.rebuild(&low);
2941 model.follow_filtered(&previous, &world.projection, &world.snapshot);
2942 assert_eq!(model.focus(), Some(&at(4, 0)));
2943 let descending = ViewSpec::new().with_sort([(Col(1), SortDirection::Descending)]);
2945 let previous = world.rebuild(&descending);
2946 model.follow_filtered(&previous, &world.projection, &world.snapshot);
2947 assert_eq!(model.focus(), Some(&at(4, 0)));
2948 let none = ViewSpec::new().with_filter(ColumnSet::first(1), |_: &Row| false);
2950 let previous = world.rebuild(&none);
2951 model.follow_filtered(&previous, &world.projection, &world.snapshot);
2952 assert_eq!(model.focus(), None);
2953 }
2954
2955 #[test]
2956 fn test_removed_and_reset_keys() {
2957 let columns = all_visible();
2958 let mut world = World::new(10);
2959 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSet);
2960 model.apply(SelectionEdit::Replace, at(2, 0), world.rows(), &columns);
2961 model.apply(SelectionEdit::Extend, at(5, 0), world.rows(), &columns);
2962
2963 let (previous, changes) = world.apply([Op::Remove(3)]);
2964 model.remove_rows(changes.removed(), &previous);
2965 assert_eq!(keys(&model), [2, 4, 5]);
2966 let (previous, changes) = world.apply([Op::Remove(4), row(4, 4)]);
2968 model.remove_rows(changes.removed(), &previous);
2969 assert_eq!(keys(&model), [2, 5]);
2970 assert!(world.snapshot.get(&4).is_some());
2971
2972 let (previous, changes) = world.apply([Op::ReplaceAll(
2975 [2, 8].map(|id| Arc::new(Row { id, price: 0 })).to_vec(),
2976 )]);
2977 assert!(changes.is_reset());
2978 model.remove_rows(changes.removed(), &previous);
2979 model.retain_present(&world.snapshot);
2980 assert_eq!(keys(&model), [2]);
2981 assert_eq!(model.focus(), None);
2982 assert_eq!(model.anchor, Some(at(2, 0)));
2983 }
2984
2985 #[test]
2986 fn test_drag_painter_and_finish() {
2987 let world = World::new(20);
2988 let columns = all_visible();
2989 let mut model = SelectionModel::<u32, Col>::new(SelectionMode::RowSet);
2990 model.apply(SelectionEdit::Replace, at(2, 0), world.rows(), &columns);
2991 let base = model.selection().clone();
2992 model.begin_drag(false, base.clone(), at(2, 0));
2994 assert!(model.drag_to(at(5, 0)));
2995 assert!(!model.drag_to(at(5, 0)));
2996 let painter = model.painter(world.rows(), &columns);
2997 assert_eq!(painter.columns(&4, 4), ColumnSet::first(4));
2998 assert_eq!(painter.columns(&6, 6), ColumnSet::EMPTY);
2999 assert_eq!(keys(&model), [2], "nothing is selected before the release");
3000 model.finish_drag(world.rows(), &columns);
3001 assert_eq!(keys(&model), [2, 3, 4, 5]);
3002 assert_eq!(model.focus(), Some(&at(5, 0)));
3003
3004 model.apply(SelectionEdit::Toggle, at(10, 0), world.rows(), &columns);
3006 model.begin_drag(true, base, at(10, 0));
3007 model.drag_to(at(12, 0));
3008 let painter = model.painter(world.rows(), &columns);
3009 assert_eq!(painter.columns(&2, 2), ColumnSet::first(4));
3010 assert_eq!(painter.columns(&3, 3), ColumnSet::EMPTY, "not in the base");
3011 assert_eq!(painter.columns(&11, 11), ColumnSet::first(4));
3012 model.finish_drag(world.rows(), &columns);
3013 assert_eq!(keys(&model), [2, 10, 11, 12]);
3014 }
3015
3016 #[derive(Default)]
3022 struct Reference {
3023 store: HashMap<u32, (i64, u64)>,
3024 generation: HashMap<u32, u64>,
3025 next_seq: u64,
3026 shown: Vec<(u32, u64)>,
3027 selected: HashSet<u32>,
3028 focus: Option<u32>,
3029 anchor: Option<u32>,
3030 single: bool,
3031 }
3032
3033 impl Reference {
3034 fn generation_of(generation: &HashMap<u32, u64>, key: u32) -> u64 {
3035 generation.get(&key).copied().unwrap_or(0)
3036 }
3037
3038 fn is_live(&self, (key, generation): (u32, u64)) -> bool {
3039 self.store.contains_key(&key)
3040 && Self::generation_of(&self.generation, key) == generation
3041 }
3042
3043 fn position(&self, key: u32) -> Option<usize> {
3045 self.shown
3046 .iter()
3047 .position(|entry| entry.0 == key && self.is_live(*entry))
3048 }
3049
3050 fn live_keys(&self) -> Vec<u32> {
3051 self.shown
3052 .iter()
3053 .filter(|entry| self.is_live(**entry))
3054 .map(|entry| entry.0)
3055 .collect()
3056 }
3057
3058 fn upsert(&mut self, key: u32, price: i64) {
3059 match self.store.get_mut(&key) {
3060 Some(entry) => entry.0 = price,
3061 None => {
3062 self.store.insert(key, (price, self.next_seq));
3063 self.next_seq += 1;
3064 }
3065 }
3066 }
3067
3068 fn remove(&mut self, key: u32) -> bool {
3070 let removed = self.store.remove(&key).is_some();
3071 if removed {
3072 *self.generation.entry(key).or_default() += 1;
3073 self.selected.remove(&key);
3074 }
3075 removed
3076 }
3077
3078 fn rebuild(&self, sort: i8, filter: Option<u32>) -> Vec<(u32, u64)> {
3079 let mut rows: Vec<(u32, i64, u64)> = self
3080 .store
3081 .iter()
3082 .filter(|(key, _)| filter.is_none_or(|r| **key % 3 != r))
3083 .map(|(key, (price, seq))| (*key, *price, *seq))
3084 .collect();
3085 rows.sort_by_key(|(_, price, seq)| (i64::from(sort) * price, *seq));
3086 rows.into_iter()
3087 .map(|(key, _, _)| (key, self.generation.get(&key).copied().unwrap_or(0)))
3088 .collect()
3089 }
3090
3091 fn successor(
3093 order: &[(u32, u64)],
3094 key: u32,
3095 accept: impl Fn((u32, u64)) -> bool,
3096 ) -> Option<u32> {
3097 let position = order.iter().position(|entry| entry.0 == key);
3098 match position {
3099 Some(position) => order[position + 1..]
3100 .iter()
3101 .copied()
3102 .find(|entry| accept(*entry))
3103 .or_else(|| {
3104 order[..position]
3105 .iter()
3106 .rev()
3107 .copied()
3108 .find(|entry| accept(*entry))
3109 })
3110 .map(|entry| entry.0),
3111 None => order
3112 .iter()
3113 .copied()
3114 .find(|entry| accept(*entry))
3115 .map(|entry| entry.0),
3116 }
3117 }
3118
3119 fn range(&self, from: u32, to: u32) -> Option<Vec<u32>> {
3120 let (from, to) = (self.position(from)?, self.position(to)?);
3121 Some(
3122 self.shown[from.min(to)..=from.max(to)]
3123 .iter()
3124 .filter(|entry| self.is_live(**entry))
3125 .map(|entry| entry.0)
3126 .collect(),
3127 )
3128 }
3129
3130 fn apply(&mut self, edit: SelectionEdit, target: u32) {
3131 let edit = match edit {
3132 SelectionEdit::Extend | SelectionEdit::AddRange if self.single => {
3133 SelectionEdit::Replace
3134 }
3135 edit => edit,
3136 };
3137 let range = self.anchor.and_then(|anchor| self.range(anchor, target));
3138 match (edit, range) {
3139 (SelectionEdit::Extend, Some(range)) => self.selected = range.into_iter().collect(),
3140 (SelectionEdit::AddRange, Some(range)) => self.selected.extend(range),
3141 (SelectionEdit::Replace | SelectionEdit::Extend | SelectionEdit::AddRange, _) => {
3142 self.selected = HashSet::from([target]);
3143 self.anchor = Some(target);
3144 }
3145 (SelectionEdit::Toggle, _) => {
3146 if self.single {
3147 self.selected = if self.selected.contains(&target) {
3148 HashSet::new()
3149 } else {
3150 HashSet::from([target])
3151 };
3152 } else if !self.selected.remove(&target) {
3153 self.selected.insert(target);
3154 }
3155 self.anchor = Some(target);
3156 }
3157 _ => {}
3158 }
3159 self.focus = Some(target);
3160 }
3161
3162 fn navigate(&self, navigation: Navigation, page: usize) -> Option<u32> {
3163 let live: Vec<usize> = (0..self.shown.len())
3164 .filter(|ix| self.is_live(self.shown[*ix]))
3165 .collect();
3166 let Some(origin) = self.focus.and_then(|key| self.position(key)) else {
3167 let ix = match navigation {
3168 Navigation::LastRow | Navigation::LastCell => live.last()?,
3169 _ => live.first()?,
3170 };
3171 return Some(self.shown[*ix].0);
3172 };
3173 let end = self.shown.len() - 1;
3174 let target = match navigation {
3175 Navigation::Up => origin.saturating_sub(1),
3176 Navigation::Down => (origin + 1).min(end),
3177 Navigation::PageUp => origin.saturating_sub(page),
3178 Navigation::PageDown => (origin + page).min(end),
3179 Navigation::FirstRow | Navigation::FirstCell => 0,
3180 Navigation::LastRow | Navigation::LastCell => end,
3181 _ => origin,
3182 };
3183 let ix = if target > origin {
3184 live.iter().copied().find(|ix| *ix >= target).or_else(|| {
3185 live.iter()
3186 .copied()
3187 .rev()
3188 .find(|ix| *ix > origin && *ix < target)
3189 })
3190 } else if target < origin {
3191 live.iter()
3192 .copied()
3193 .rev()
3194 .find(|ix| *ix <= target)
3195 .or_else(|| live.iter().copied().find(|ix| *ix > target && *ix < origin))
3196 } else {
3197 None
3198 };
3199 Some(self.shown[ix.unwrap_or(origin)].0)
3200 }
3201 }
3202
3203 fn view(sort: i8, filter: Option<u32>) -> ViewSpec<Row> {
3204 let view = match sort {
3205 1 => ViewSpec::new().with_sort([(Col(1), SortDirection::Ascending)]),
3206 -1 => ViewSpec::new().with_sort([(Col(1), SortDirection::Descending)]),
3207 _ => ViewSpec::new(),
3208 };
3209 match filter {
3210 Some(r) => view.with_filter(ColumnSet::first(1), move |row: &Row| row.id % 3 != r),
3211 None => view,
3212 }
3213 }
3214
3215 #[test]
3216 fn test_selection_matches_reference_model() {
3217 const PAGE: usize = 3;
3218 let columns = Visible(vec![ColumnIx::new(0)]);
3219 let gesture = StandardSelectionGesture;
3220 for seed in 0..60 {
3221 let mut rng = fastrand::Rng::with_seed(seed);
3222 let mode = if rng.bool() {
3223 SelectionMode::RowSet
3224 } else {
3225 SelectionMode::RowSingle
3226 };
3227 let mut world = World::new(0);
3228 let mut model = SelectionModel::<u32, Col>::new(mode);
3229 let mut reference = Reference {
3230 single: mode == SelectionMode::RowSingle,
3231 ..Reference::default()
3232 };
3233 let initial: Vec<Op<Row>> = (0..20)
3234 .map(|id| {
3235 let price = rng.i64(0..50);
3236 reference.upsert(id, price);
3237 row(id, price)
3238 })
3239 .collect();
3240 world.apply(initial);
3241 let (mut sort, mut filter) = (0i8, None);
3242 world.rebuild(&view(sort, filter));
3243 reference.shown = reference.rebuild(sort, filter);
3244
3245 for step in 0..40 {
3246 let context = format!("seed {seed}, step {step}");
3247 let mut ops = Vec::new();
3250 let mut removed = HashSet::new();
3251 let reset = rng.u8(..20) == 0;
3252 if reset {
3253 let count = rng.u32(0..30);
3254 let mut rows: Vec<Arc<Row>> = Vec::new();
3255 for id in 0..count {
3256 if rng.bool() {
3257 rows.push(Arc::new(Row {
3258 id,
3259 price: rng.i64(0..50),
3260 }));
3261 }
3262 }
3263 for key in reference.store.keys() {
3266 *reference.generation.entry(*key).or_default() += 1;
3267 }
3268 reference.store.clear();
3269 for row in &rows {
3270 reference.upsert(row.id, row.price);
3271 }
3272 ops.push(Op::ReplaceAll(rows));
3273 } else {
3274 for _ in 0..rng.usize(0..4) {
3275 let key = rng.u32(0..40);
3276 match rng.u8(..4) {
3277 0 => {
3278 if reference.remove(key) {
3279 removed.insert(key);
3280 }
3281 ops.push(Op::Remove(key));
3282 }
3283 1 => {
3284 if reference.remove(key) {
3285 removed.insert(key);
3286 }
3287 ops.push(Op::Remove(key));
3288 let price = rng.i64(0..50);
3289 reference.upsert(key, price);
3290 ops.push(row(key, price));
3291 }
3292 _ => {
3293 let price = rng.i64(0..50);
3294 reference.upsert(key, price);
3295 ops.push(row(key, price));
3296 }
3297 }
3298 }
3299 }
3300 let (previous, changes) = world.apply(ops);
3301 model.remove_rows(changes.removed(), &previous);
3302 if changes.is_reset() {
3303 model.retain_present(&world.snapshot);
3304 let store = reference.store.clone();
3305 reference.selected.retain(|key| store.contains_key(key));
3306 reference.focus = reference.focus.filter(|key| store.contains_key(key));
3307 reference.anchor = reference.anchor.filter(|key| store.contains_key(key));
3308 } else if !changes.removed().is_empty() {
3309 model.follow_removed(&world.projection, &previous, &world.snapshot);
3310 let moved = [reference.focus, reference.anchor].map(|cell| {
3313 let key = cell?;
3314 if !removed.contains(&key) {
3315 return Some(key);
3316 }
3317 Reference::successor(&reference.shown, key, |entry| {
3318 reference.is_live(entry)
3319 })
3320 });
3321 [reference.focus, reference.anchor] = moved;
3322 }
3323
3324 if reset || rng.u8(..3) == 0 {
3328 if rng.bool() {
3329 sort = rng.i8(-1..=1);
3330 filter = rng.bool().then(|| rng.u32(0..3));
3331 }
3332 let previous = world.rebuild(&view(sort, filter));
3333 model.follow_filtered(&previous, &world.projection, &world.snapshot);
3334 let shown = reference.rebuild(sort, filter);
3335 let old = std::mem::replace(&mut reference.shown, shown);
3336 let new: HashSet<u32> = reference.shown.iter().map(|entry| entry.0).collect();
3337 let moved = [reference.focus, reference.anchor].map(|cell| {
3338 let key = cell?;
3339 if new.contains(&key) {
3340 return Some(key);
3341 }
3342 Reference::successor(&old, key, |entry| {
3343 new.contains(&entry.0) && reference.is_live(entry)
3344 })
3345 });
3346 [reference.focus, reference.anchor] = moved;
3347 }
3348
3349 let live = reference.live_keys();
3351 let rows = world.rows();
3352 match rng.u8(..6) {
3353 0 | 1 if !live.is_empty() => {
3354 let key = live[rng.usize(..live.len())];
3355 let mut modifiers = Modifiers::default();
3356 modifiers.shift = rng.bool();
3357 modifiers.platform = rng.bool();
3358 modifiers.control = modifiers.platform;
3359 let target = at(key, 0);
3360 let edit = gesture.edit(&SelectionGestureContext::new(
3361 GestureInput::Pointer,
3362 modifiers,
3363 false,
3364 model.is_selected(&target),
3365 mode,
3366 ));
3367 model.apply(edit, target, rows, &columns);
3368 reference.apply(edit, key);
3369 }
3370 2 => {
3371 let navigation = [
3372 Navigation::Up,
3373 Navigation::Down,
3374 Navigation::PageUp,
3375 Navigation::PageDown,
3376 Navigation::FirstRow,
3377 Navigation::LastRow,
3378 ][rng.usize(..6)];
3379 let extending = rng.bool();
3380 let target = model.navigation_target(navigation, PAGE, rows, &columns);
3381 let expected = reference.navigate(navigation, PAGE);
3382 assert_eq!(
3383 target.as_ref().and_then(|cell| cell.row().copied()),
3384 expected,
3385 "{context}"
3386 );
3387 if let Some(target) = target {
3388 let edit = gesture.edit(&SelectionGestureContext::new(
3389 GestureInput::Keyboard,
3390 Modifiers::default(),
3391 extending,
3392 model.is_selected(&target),
3393 mode,
3394 ));
3395 let key = *target.row().expect("a row");
3396 model.apply(edit, target, rows, &columns);
3397 reference.apply(edit, key);
3398 }
3399 }
3400 3 => {
3401 let toggled = model.toggle_focused(rows, &columns);
3402 let focus = reference
3403 .focus
3404 .filter(|key| reference.position(*key).is_some());
3405 assert_eq!(toggled, focus.is_some(), "{context}");
3406 if let Some(key) = focus {
3407 let edit = if reference.single {
3408 SelectionEdit::Replace
3409 } else {
3410 SelectionEdit::Toggle
3411 };
3412 reference.apply(edit, key);
3413 }
3414 }
3415 4 => {
3416 if model.select_all(rows, &columns) {
3417 reference.selected = live.iter().copied().collect();
3418 }
3419 }
3420 _ => {
3421 model.clear();
3422 reference.selected.clear();
3423 }
3424 }
3425
3426 let selected: HashSet<u32> = model.selection().iter().copied().collect();
3429 assert_eq!(selected, reference.selected, "{context}");
3430 let scoped = ProjectionScope.resolve(&SelectionScopeContext::new(
3432 model.selection(),
3433 &world.projection,
3434 &world.snapshot,
3435 mode,
3436 ));
3437 let expected: Vec<u32> = reference
3438 .live_keys()
3439 .into_iter()
3440 .filter(|key| reference.selected.contains(key))
3441 .collect();
3442 assert_eq!(scoped.keys(), expected, "{context}");
3443 let focus = model.focus().and_then(|cell| cell.row().copied());
3445 assert_eq!(focus, reference.focus, "{context}");
3446 if let Some(key) = focus {
3447 assert!(world.rows().display_ix(&key).is_some(), "{context}");
3448 }
3449 assert_eq!(
3450 model.anchor.as_ref().and_then(|cell| cell.row().copied()),
3451 reference.anchor,
3452 "{context}"
3453 );
3454 }
3455 }
3456 }
3457}