Skip to main content

gpui_kit_grid/state/
selection.rs

1//! Key-based selection: the selection mode, the selected rows or cells, the
2//! focused cell and the anchor. All of them are held by row key and column,
3//! so they survive re-sorting, filtering and the update stream.
4//!
5//! [`SelectionModel`] works on the projection on screen and its snapshot and
6//! needs no window, so it is tested without one. Every branch on
7//! [`SelectionMode`] lives in this module: painting, Actions and the pointer
8//! ask the model (`columns(key)`, an edit) instead of the mode.
9//!
10//! Below the model, the [`LiveGridState`] side: builders and readers, the
11//! keyboard Actions, pointer presses and drags, programmatic changes, and
12//! their events (one `SelectionChange`, then one `FocusChange`, per action).
13
14use 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
50/// A pointer press turns into a drag past this distance on either axis
51/// (GPUI's drag threshold).
52const DRAG_THRESHOLD: Pixels = px(2.);
53
54/// Returns true if the pointer at `position` is past the drag threshold from
55/// the press at `origin`: the press is a drag, not a click.
56pub(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/// What a click or a key press selects.
61#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
62pub enum SelectionMode {
63    /// One row at a time (the default).
64    #[default]
65    RowSingle,
66    /// Any set of rows.
67    RowSet,
68    /// One cell at a time.
69    CellSingle,
70    /// Any set of cells.
71    CellSet,
72}
73
74impl SelectionMode {
75    fn selects_cells(self) -> bool {
76        matches!(self, Self::CellSingle | Self::CellSet)
77    }
78
79    /// One row or one cell at a time; otherwise the grid is
80    /// multiselectable for accessibility.
81    pub(super) fn is_single(self) -> bool {
82        matches!(self, Self::RowSingle | Self::CellSingle)
83    }
84
85    /// What one selected item is for accessibility: rows carry `selected`
86    /// in the row modes, cells in the cell modes.
87    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/// The selected rows or cells, by key. Cloning is O(1): the set is shared
97/// and copied on write.
98#[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        // A selection may hold every row: print its rows only when few.
114        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    /// A selection of these rows and their columns; rows with no columns
131    /// are left out.
132    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    /// Rows with a selected row or at least one selected cell.
141    pub fn len(&self) -> usize {
142        self.rows.len()
143    }
144
145    /// Returns true if nothing is selected.
146    pub fn is_empty(&self) -> bool {
147        self.rows.is_empty()
148    }
149
150    /// Returns true if the row is selected, or one of its cells is.
151    pub fn contains(&self, key: &K) -> bool {
152        self.rows.contains_key(key)
153    }
154
155    /// Returns true if the cell is selected; in the row modes, if its row is.
156    pub fn contains_cell(&self, key: &K, column: ColumnIx) -> bool {
157        self.columns(key).contains(column)
158    }
159
160    /// The selected columns of a row: every schema column in the row modes,
161    /// `ColumnSet::EMPTY` if the row has nothing selected.
162    pub fn columns(&self, key: &K) -> ColumnSet {
163        self.rows.get(key).copied().unwrap_or(ColumnSet::EMPTY)
164    }
165
166    /// The selected rows in no particular order.
167    pub fn iter(&self) -> impl Iterator<Item = &K> + '_ {
168        self.rows.keys()
169    }
170
171    /// Returns true if both select the same rows and cells.
172    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    /// The rows to change; copied first if another clone shares them.
184    fn rows_mut(&mut self) -> &mut FxHashMap<K, ColumnSet> {
185        Arc::make_mut(&mut self.rows)
186    }
187
188    /// Adds every row and cell of `other`; returns true if any was new.
189    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    /// Drops the keys `missing` says are gone; returns true if any was.
205    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    /// Flips one item of the row `key`: `item` is its columns (a row's or
214    /// a cell's), `column` the one that tells whether it is selected.
215    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/// The focused cell: the keyboard target inside the grid, a cell of a data
234/// row or a group caption.
235#[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    /// The caption of the group at `path`; `column` is kept for the way
252    /// back to the data rows.
253    pub(crate) fn caption(path: GroupPath, column: C) -> Self {
254        Self {
255            row: None,
256            group: Some(path),
257            column,
258        }
259    }
260
261    /// The row; `None` on a group caption.
262    pub fn row(&self) -> Option<&K> {
263        self.row.as_ref()
264    }
265
266    /// The group of a focused caption; `None` on a row.
267    pub fn group(&self) -> Option<&GroupPath> {
268        self.group.as_ref()
269    }
270
271    /// The column. On a caption, the column the focus keeps for the data
272    /// rows: arrows up and down onto a row land in it.
273    pub fn column(&self) -> C {
274        self.column
275    }
276}
277
278/// The line a command acts on, by identity: a data row by key, a group
279/// caption by path.
280#[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    /// The focused line as a command's target.
288    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/// What a command acts on: the selection within its scope, in display order
298/// ([`crate::SelectionScope`]). Collect one from `(key, columns)` pairs.
299#[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    /// The rows, in display order.
326    pub fn keys(&self) -> &[K] {
327        &self.keys
328    }
329
330    /// The selected columns of `keys()[ix]`: every schema column in the row
331    /// modes; `ColumnSet::EMPTY` past the end.
332    pub fn columns(&self, ix: usize) -> ColumnSet {
333        self.columns.get(ix).copied().unwrap_or(ColumnSet::EMPTY)
334    }
335
336    /// The number of rows.
337    pub fn len(&self) -> usize {
338        self.keys.len()
339    }
340
341    /// Returns true if there are no rows.
342    pub fn is_empty(&self) -> bool {
343        self.keys.is_empty()
344    }
345
346    /// The keys without a copy, for an event.
347    pub(crate) fn shared_keys(&self) -> Arc<[K]> {
348        self.keys.clone()
349    }
350}
351
352/// What the last mouse-down on the body hit, captured at that moment: a
353/// re-sort before the release, or before a menu is built, cannot retarget
354/// it.
355#[derive(Clone, Debug)]
356pub(crate) struct PointerTarget<K> {
357    /// `None`: the empty area below the rows, or a group caption. The key
358    /// of a pinned row is of its band's key space (`pinned`).
359    row: Option<K>,
360    /// The group of a pressed caption.
361    group: Option<GroupPath>,
362    /// The cell's column; on a caption, the aggregate cell's, if any.
363    column: Option<ColumnIx>,
364    button: MouseButton,
365    position: Point<Pixels>,
366    /// The band of a pinned row: the press selects nothing, and its menu
367    /// targets `GridTarget::Pinned`.
368    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    /// The group of a pressed caption.
377    pub(crate) fn group(&self) -> Option<&GroupPath> {
378        self.group.as_ref()
379    }
380
381    /// The band of a pinned row; `None` for a data row or no row.
382    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
400/// The lines on screen: the projection shown, the snapshot it is drawn
401/// from and the [`SelectionPolicy`] that says which lines take the
402/// selection. Positions are display indices. A data row removed since the
403/// projection was built (a tombstone) has no key; it and a group footer
404/// take neither the focus nor the selection. A group caption takes the
405/// focus, and the selection only if the policy says so.
406pub(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    /// The lines with the default policy, [`FocusCaptions`].
422    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    /// The key of the live row at a display position.
440    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    /// The group whose caption is at a display position.
446    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    /// The display position of a live row on screen.
454    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    /// The display position of the caption of the group at `path`, if
461    /// shown.
462    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    /// The display position of a focused row or caption, if shown.
468    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    /// Returns true if the line at a display position takes the focus: a
477    /// live data row or a group caption.
478    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    /// Returns true if a press or a key on the line of `cell` may select
483    /// it: a data row the policy selects (with `FocusBranches`, not a tree
484    /// row with children). A caption never: a
485    /// selectable one joins a range through its group's rows. A row the
486    /// snapshot lacks is left to the edit.
487    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    /// The focus on the line at a display position, in `column`.
505    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    /// Adds the selectable lines of `lines` to `map`, each with `item`: a
518    /// live data row its key, a selectable caption its group's live rows.
519    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    /// The first and the last line that take the focus.
555    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    /// The live row nearest to `display_ix`: it, else the next one, else the
564    /// one before.
565    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    /// Where a move from `origin` to `target` lands: on a focusable
573    /// `target`, else on the first focusable line past it in the same
574    /// direction, else on the one nearest to it on the way back; `origin`
575    /// if there is none.
576    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    /// The line [`SelectionPolicy::line_scope`] is asked about for a
595    /// command over `target`, and the rows under it (a range of
596    /// `Projection::rows`); `None` if the line is not in the projection.
597    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
635/// The visible columns in display order, as the selection sees them: ranges
636/// of cells and navigation go by [`ViewColumnIx`], identity by
637/// [`ColumnIx`], and only the column layout converts between them.
638pub(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    /// The visible column next to a hidden `column` in display order: the
644    /// first one right of it, else the first one left of it.
645    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
685/// The visible column `step` places from `column`, stopping at the edges.
686fn 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
697/// `columns` without the columns of `drop`.
698fn 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
708/// The live row next to `id` among the data rows `previous` shows that
709/// `accept` takes: the first one after it, else the nearest one before it
710/// (the successor rule). Without `id` among them, the first one `accept`
711/// takes at all.
712pub(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/// A keyboard move of the focus.
727#[derive(Clone, Copy, Debug, PartialEq, Eq)]
728pub(super) enum Navigation {
729    Up,
730    Down,
731    PrevColumn,
732    NextColumn,
733    PageUp,
734    PageDown,
735    /// The first visible column.
736    First,
737    /// The last visible column.
738    Last,
739    FirstRow,
740    LastRow,
741    FirstCell,
742    LastCell,
743}
744
745/// The selection's revision and the focus before an action: the action's
746/// events compare against it. It holds no clone of the selection, so an
747/// edit in place during the action does not copy the set.
748pub(super) struct ChangeMark<K, C> {
749    revision: u64,
750    focus: Option<FocusedCell<K, C>>,
751}
752
753/// A drag in progress: the range from the anchor to `target` is painted and
754/// becomes the selection on release.
755struct Drag<K, C> {
756    target: FocusedCell<K, C>,
757    /// The selection before the gesture, when the drag adds to it (after a
758    /// Toggle or an AddRange press).
759    base: Option<GridSelection<K>>,
760}
761
762/// What a frame paints as selected: the selection, or during a drag the
763/// range under the pointer, alone or over the selection before the gesture.
764/// Built once per frame; one hash lookup per painted row.
765pub(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    /// The painted columns of the row with `key` at `display_ix`.
772    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
785/// The selection, the focused cell and the anchor, by key; no GPUI types.
786pub(super) struct SelectionModel<K, C> {
787    mode: SelectionMode,
788    /// Every schema column: the columns of a selected row in the row modes.
789    all: ColumnSet,
790    selection: GridSelection<K>,
791    /// Counts the changes of `selection`: every edit that changes it bumps
792    /// it, one that leaves it as it was does not.
793    revision: u64,
794    focus: Option<FocusedCell<K, C>>,
795    /// Where a range starts (Shift); set by `Replace` and `Toggle`.
796    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    /// Replaces the selection, counting a change only if it differs.
814    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    /// Whether the selection and the focus changed since `mark`.
845    pub(super) fn changes_since(&self, mark: &ChangeMark<K, C>) -> (bool, bool) {
846        (self.revision != mark.revision, self.focus != mark.focus)
847    }
848
849    /// The columns one selected item at `column` covers: the whole row in
850    /// the row modes, the cell in the cell modes.
851    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    /// The columns a programmatically selected row gets: the whole row, its
862    /// visible cells in `CellSet`, the focused column's cell in `CellSingle`.
863    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    /// The focused column if visible, else the first visible one.
874    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    /// Returns true if the target row (in the cell modes, cell) is selected.
889    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    /// Returns true if a `Replace` on the selected `target` would drop more
896    /// than the target: a press there collapses the selection on release.
897    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    /// Applies `edit` with `target`, a line on screen; the target always
905    /// gets the focus. In the single modes `Extend` and `AddRange` replace.
906    /// A group caption takes no selection, nor a line the selection policy
907    /// does not select (a tree row with children under `FocusBranches`): on
908    /// one, `Replace` and `Toggle` only move the focus, and a range from the
909    /// anchor stops at it, the anchor staying where it is.
910    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                    // No anchor on screen: start over from the target; a
945                    // caption or a line the policy does not select only
946                    // takes the focus.
947                    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            // Select the target, or clear the one selected item.
972            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    /// The selection with `target` flipped back: what it was before a
986    /// `Toggle` of `target`.
987    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    /// The selectable lines from `anchor` to `target` in the projection on
997    /// screen, tombstones skipped, captions by the selection policy; in the
998    /// cell modes, with the visible columns from the anchor's to the
999    /// target's. `None` if either is not on screen.
1000    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    /// The columns of a range: every column in the row modes, the visible
1020    /// ones from `from` to `to` by [`ViewColumnIx`] in the cell modes.
1021    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            // A hidden anchor column: the target's column alone.
1028            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    /// Where a keyboard move puts the focus, in model coordinates (display
1040    /// index, [`ViewColumnIx`]): rows off screen are reached the same way.
1041    /// No wrap at the edges. Without a focus on screen, the first move
1042    /// focuses the first cell, the `…Last…` moves the last row or column.
1043    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    /// Moves the focus to `target` alone; the selection and the anchor stay.
1097    pub(super) fn focus_on(&mut self, target: FocusedCell<K, C>) {
1098        self.focus = Some(target);
1099    }
1100
1101    /// Focuses the caption of the group at `path` if the projection on
1102    /// screen shows it; the focused column stays, or is the first visible
1103    /// one. The selection and the anchor stay. Returns false if the caption
1104    /// is not shown.
1105    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    /// Space: toggles the focused row or cell in the set modes, selects it
1125    /// in the single modes. Returns false without a focus on screen.
1126    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        // A caption takes no selection, nor a line the policy does not
1138        // select: Space goes on.
1139        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    /// Cmd/Ctrl+A: selects the keys of the rows on screen now, tombstones
1154    /// skipped; in `CellSet` their visible cells. Rows of collapsed groups
1155    /// are not on screen, and captions take the selection only as the
1156    /// policy says. Rows that arrive later are never selected by it. The
1157    /// focus and the anchor stay. Returns false in the single modes, which
1158    /// have no "select all".
1159    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    /// Rows ↔ cells clears the selection; a set mode → a single one keeps
1188    /// only the focused row or cell, if it was selected.
1189    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    /// Selects the rows of `keys` that the snapshot has (the first one in
1209    /// the single modes). The focus and the anchor stay.
1210    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    /// Selects these cells of rows the snapshot has (the first one in the
1229    /// single modes); in the row modes, their rows.
1230    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    /// Selects one row (`None` clears): the row in the row modes, its cell
1253    /// in the focused column in the cell modes. A row on screen also takes
1254    /// the focus and the anchor; a key the snapshot lacks selects nothing.
1255    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    /// Focuses a cell of a row on screen; `None`, or a hidden column, keeps
1277    /// the focused column. The anchor follows. Returns false if the row is
1278    /// not on screen.
1279    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    /// Drops the keys of rows removed from the store, read from the
1306    /// `previous` snapshot: a key removed and inserted again in one batch is
1307    /// a new row and is not selected.
1308    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    /// A new data set: keys the snapshot has stay selected, the rest go;
1333    /// the focus and the anchor stay if their keys are there, else go.
1334    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    /// The successor rule for removed rows: a focus or an anchor on a row
1357    /// removed from the store moves to the next live row of the projection
1358    /// on screen, else to the previous one, else to none. The column stays.
1359    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    /// The successor rule for a new projection: a focus or an anchor on a
1386    /// row the new projection does not show moves along the `previous`
1387    /// order to the nearest row it shows.
1388    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            // A focused caption follows its group (`follow_shown_lines`).
1398            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    /// Moves the focus and the anchor off a hidden column, to the visible
1418    /// column right of it, else left of it.
1419    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    /// Starts a drag-selection at `target`. A drag after `Replace` or
1431    /// `Extend` extends from the anchor; one after `Toggle` or `AddRange`
1432    /// (`adds`) adds the range to `base`, the selection before the gesture.
1433    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    /// Moves the drag's end; returns true if it moved.
1446    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    /// Ends a drag: its range, by the projection on screen now, becomes the
1461    /// selection (over the base when it adds), and its end the focus.
1462    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            // The row under the pointer went away: keep what the press did.
1476            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    /// What this frame paints as selected.
1489    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
1524/// A primary-button press on the body until its release.
1525pub(super) struct Press<K, C> {
1526    /// The selection and focus before the gesture: its events come once,
1527    /// on release.
1528    mark: ChangeMark<K, C>,
1529    /// The selection before an `AddRange` press, for a drag that follows.
1530    /// (Before a `Toggle` it is rebuilt when the drag starts, so a toggle
1531    /// does not copy a large selection.)
1532    base: Option<GridSelection<K>>,
1533    target: FocusedCell<K, C>,
1534    origin: Point<Pixels>,
1535    /// A `Replace` on a selected row waits for the release (deferred
1536    /// collapse), so a drag of the selection keeps it.
1537    deferred: bool,
1538    /// The press toggled or added a range: a drag adds too.
1539    adds: bool,
1540    /// The pointer went past the drag threshold.
1541    moved: bool,
1542    /// The press only moves the focus: on a caption.
1543    focus_only: bool,
1544    /// The press may drag rows, with its modifiers for `drag_payload`: it
1545    /// starts no drag-selection.
1546    drag_candidate: Option<Modifiers>,
1547    /// The pointer passed the drag gesture's threshold: the drag armed, or
1548    /// the delegate refused it.
1549    drag_tried: bool,
1550}
1551
1552impl<D: LiveGridDelegate> LiveGridState<D> {
1553    /// Sets what a click or a key press selects (default
1554    /// [`SelectionMode::RowSingle`]).
1555    pub fn with_selection_mode(mut self, mode: SelectionMode) -> Self {
1556        self.selection.set_mode(mode);
1557        self
1558    }
1559
1560    /// Sets which rows and cells commands act on (default
1561    /// [`ProjectionScope`](crate::ProjectionScope); see also
1562    /// [`StoreScope`](crate::StoreScope)).
1563    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    /// Sets how a press or a navigation command edits the selection
1569    /// (default [`StandardSelectionGesture`](crate::StandardSelectionGesture)).
1570    pub fn with_selection_gesture(mut self, gesture: impl SelectionGesture) -> Self {
1571        self.selection_gesture = Box::new(gesture);
1572        self
1573    }
1574
1575    /// Sets which press opens the context menu (default
1576    /// [`StandardContextMenuGesture`](crate::StandardContextMenuGesture)). On
1577    /// the body that press edits the selection as
1578    /// [`GestureInput::ContextPointer`](crate::GestureInput::ContextPointer).
1579    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    /// Sets which lines take the selection and what a command over a line
1585    /// acts on (default [`FocusCaptions`](crate::FocusCaptions): group
1586    /// captions take the focus, not the selection, and a command over one
1587    /// acts on its group's rows).
1588    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    /// The lines on screen, with the selection policy.
1594    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    /// Whether a press opens the context menu: the [`ContextMenuGesture`]
1603    /// decides, on the body and on the header.
1604    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    /// What a click or a key press selects.
1621    pub fn selection_mode(&self) -> SelectionMode {
1622        self.selection.mode()
1623    }
1624
1625    /// The whole selection, by key. It may hold rows hidden by a filter:
1626    /// commands use [`Self::scoped_selection`].
1627    pub fn selection(&self) -> &GridSelection<KeyOf<D>> {
1628        self.selection.selection()
1629    }
1630
1631    /// The selection within the selection scope, in display order: what a
1632    /// command acts on. O(selected rows).
1633    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    /// The focused cell: the keyboard target.
1643    pub fn focused_cell(&self) -> Option<&FocusedCell<KeyOf<D>, ColumnOf<D>>> {
1644        self.selection.focus()
1645    }
1646
1647    /// Returns true if the cell is selected; in the row modes, if its row
1648    /// is.
1649    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    /// Returns true if the last frame showed keyboard focus: the grid was
1654    /// focused and the last input came from the keyboard.
1655    #[cfg(any(test, feature = "test-support"))]
1656    pub fn is_focus_visible(&self) -> bool {
1657        self.focus_visible
1658    }
1659
1660    /// The keys of the data rows painted in the last frame, top to bottom;
1661    /// group captions are left out.
1662    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    /// Changes the selection mode (`SelectionOrigin::Programmatic`). Rows ↔
1671    /// cells clears the selection; a set mode → a single one keeps only the
1672    /// focused row or cell.
1673    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    /// Selects one row, or clears the selection with `None`; in the cell
1680    /// modes, its cell in the focused column. A row on screen also takes the
1681    /// focus. A key missing from the current snapshot selects nothing
1682    /// (`SelectionOrigin::Programmatic`).
1683    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    /// Selects these rows (the first one in the single modes; their visible
1690    /// cells in `CellSet`). Keys missing from the current snapshot are
1691    /// dropped: a row that does not exist yet is not selected in advance.
1692    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    /// Selects these cells (the first one in the single modes; their rows
1703    /// in the row modes). Keys missing from the current snapshot are
1704    /// dropped.
1705    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    /// Selects every row on screen, as Cmd/Ctrl+A: the keys now, so rows
1716    /// that arrive later are not selected. Does nothing in the single modes.
1717    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    /// Clears the selection; the focus stays.
1724    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    /// Focuses a cell and scrolls it into view (`SelectionOrigin::
1731    /// Programmatic`); the anchor follows. `None` keeps the focused column.
1732    /// Returns false if the row is not on screen.
1733    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    /// Focuses the caption of the group at `path` if the shown order paints
1751    /// it, and scrolls it into view (`SelectionOrigin::Programmatic`); the
1752    /// focused column stays. The selection and the anchor stay: a caption
1753    /// takes no selection. Returns false if the projection on screen shows
1754    /// no such caption, under a collapsed group for one.
1755    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    /// What the last mouse-down on the body hit.
1768    pub(crate) fn pointer_target(&self) -> Option<&PointerTarget<KeyOf<D>>> {
1769        self.pointer_target.as_ref()
1770    }
1771
1772    /// What a command over `target` acts on: the [`SelectionPolicy`]
1773    /// decides for the line, in the projection on screen; without a target,
1774    /// or when the policy leaves it, [`Self::scoped_selection`]. A caption
1775    /// whose group is gone acts on nothing. O(rows under the line) for a
1776    /// caption, as Cmd/Ctrl+A.
1777    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    /// The scope the [`SelectionPolicy`] gives a command over `target` of
1787    /// its own; `None` leaves the command to [`Self::scoped_selection`], as
1788    /// for a row the projection on screen does not hold. A caption whose
1789    /// group is gone has an empty scope.
1790    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    /// The focused line, as a command's target.
1809    pub(crate) fn focus_target(&self) -> Option<LineTarget<KeyOf<D>>> {
1810        self.selection.focus().and_then(FocusedCell::line)
1811    }
1812
1813    /// Emits `Confirm` for the command scope of the focused line (the
1814    /// rows of a focused caption's group, else the scoped selection);
1815    /// returns false, emitting nothing, if it is empty. Enter,
1816    /// Cmd/Ctrl+Enter, and the primary menu command, which the menu's press
1817    /// focused first.
1818    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    /// Runs `edit` on the selection model with the rows and columns on
1831    /// screen, then emits what changed.
1832    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    /// Emits `SelectionChange`, then `FocusChange`, for what changed since
1854    /// `mark`; a new focus also becomes the current column. Returns true if
1855    /// anything changed.
1856    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    /// Moves the focus off a hidden column, after the `LayoutChange` that hid it.
1885    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    /// The rows a page key moves by: the rows that fit in the row area.
1896    fn page_rows(&self) -> usize {
1897        page_size(self.rows_area_height(), self.row_height)
1898    }
1899
1900    /// A keyboard move: the [`SelectionGesture`] edits the selection with
1901    /// the target, which gets the focus and is scrolled into view.
1902    pub(super) fn navigate(
1903        &mut self,
1904        navigation: Navigation,
1905        extending: bool,
1906        cx: &mut Context<Self>,
1907    ) {
1908        // ← and → on a caption expand, collapse and move along the tree
1909        // (WAI-ARIA treegrid).
1910        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        // Page keys scroll by a page, so the focus keeps its screen line.
1940        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    /// Scrolls the focused cell into view: its line by the nearest edge,
1952    /// its column if it scrolls (frozen zones do not). A focused caption
1953    /// does not scroll sideways: it is painted at the left edge.
1954    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    /// The keyboard Actions of the selection, on the grid root.
1973    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    /// Cmd/Ctrl+A; the single modes let the key go on.
2054    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    /// Space; without a focused row the key goes on.
2064    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    /// Enter and Cmd/Ctrl+Enter; with nothing selected the key goes on, so
2079    /// a dialog's default button still answers it.
2080    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    /// Escape clears the selection (the focus and the anchor stay); with
2087    /// nothing selected the key goes on, so a dialog can close. During a
2088    /// row drag from this grid it cancels the drag instead.
2089    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    /// The key of the data row painted at a window position.
2101    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    /// The column at a window position; left of the columns the first
2109    /// visible one, right of them the last.
2110    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    /// A press on the body. The target is captured now, so a re-sort
2121    /// before the release cannot retarget it. A context press then opens
2122    /// the menu for it. On a group caption or a tree row's chevron the
2123    /// [`ExpandGesture`](crate::ExpandGesture) may expand or collapse the
2124    /// line; a caption then takes the focus, never the selection, and a
2125    /// tree row's chevron neither. Any press but one on a chevron may end
2126    /// in a click on its target.
2127    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        // The press that closes the menu changes nothing else: not the
2140        // selection, no drag, no double click. A context press opens a new
2141        // menu for its own target.
2142        if self.is_menu_open() && !context {
2143            self.press_series = None;
2144            return;
2145        }
2146        // A pinned row first: the bands are over the data rows' area.
2147        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        // A group caption: its path and the aggregate cell under the press.
2152        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        // Then the expand gesture, on a caption or a tree row's chevron.
2174        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        // Any other press may end in a `Click` on its target; a chevron is
2185        // a control, as a resize handle is.
2186        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            // A pinned row is not selected or focused: the press only
2193            // focuses the grid (the root does that), and a double click on
2194            // it does nothing.
2195            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                // A caption takes the focus, not the selection.
2205                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                // A tree row's chevron is a control, as Finder's disclosure
2210                // triangle: the focus, the selection and the anchor stay; no
2211                // drag, no double click.
2212                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    /// The focused column, if visible, else the first visible one.
2221    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    /// A primary press that only moves the focus to `target`, a caption:
2231    /// no selection, no drag, no double click. Its `FocusChange` comes on
2232    /// the release.
2233    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    /// The data row and cell of the last press, as a target; a pinned row
2262    /// is none.
2263    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    /// A primary press on a data cell (or the empty area below the rows):
2273    /// the selection gesture, a drag-selection, and the second press of a
2274    /// series; the host of a cell's element reaches it for a press the
2275    /// element did not stop.
2276    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        // A release this grid never saw ends the last gesture first.
2284        self.finish_press(cx);
2285        let pressed = self.pressed_cell();
2286        if click_count >= 2 {
2287            // A double click on a cell with an editor edits it; otherwise it
2288            // confirms the row of the series' first press, whatever moved
2289            // under the pointer since.
2290            if self.start_edit_by_press(click_count, window, cx) {
2291                return;
2292            }
2293            // A double click on a link's text: its first click opened the
2294            // link, which is the cell's command.
2295            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        // The empty area below the rows: nothing to select.
2306        let Some(target) = pressed else {
2307            return;
2308        };
2309        let selected = self.selection.is_selected(&target);
2310        // A press the drag gesture takes may drag rows.
2311        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    /// The menu gesture: an unselected row is selected first (Windows
2360    /// convention), a selected one keeps the selection; the row gets the
2361    /// focus. A caption gets the focus and the selection stays. The events
2362    /// come at once, before the menu is built.
2363    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    /// The second press of a series: `Confirm` for the row of the first
2393    /// one. Its scope of its own, if the selection policy gives it one and
2394    /// it is not empty (a tree row with children under `FocusBranches`: its
2395    /// subtree's leaves); else the scoped selection, if the row is in it.
2396    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    /// The pointer moved with the primary button down: past the threshold a
2410    /// press becomes a drag-selection, and the range follows the pointer.
2411    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        // A press on a caption starts no drag-selection.
2416        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            // A press that may drag rows starts no drag-selection; past the
2425            // gesture's threshold it arms the drag, or, refused, does
2426            // nothing more.
2427            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                // Dragging a selection drags its rows, so it does not start
2440                // a new selection.
2441                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        // Above or below the data rows (over a pinned band too), the rows
2455        // scroll (`AutoScroll`, as a header drag scrolls the columns).
2456        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    /// The drag's end: the row under `position`, held within the rows on
2463    /// screen, and the column under it.
2464    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    /// One auto-scroll step of a drag-selection; the range follows the last
2500    /// pointer position.
2501    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    /// The primary button went up anywhere in the window.
2518    pub(crate) fn on_body_mouse_up(&mut self, cx: &mut Context<Self>) {
2519        self.finish_press(cx);
2520    }
2521
2522    /// Ends the press: a drag fixes its range, a deferred collapse applies
2523    /// `Replace` unless the pointer moved; then one `SelectionChange` and one
2524    /// `FocusChange` for the whole gesture.
2525    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        // The row drag, if any, ends with the press.
2531        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    /// Visible columns in display order; the others are hidden.
2616    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            // Schema order stands in for the display order here.
2638            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    /// A store, its snapshot, and the projection on screen.
2659    struct World {
2660        ingest: Ingest<Row>,
2661        snapshot: Arc<StoreSnapshot<Row>>,
2662        projection: Projection,
2663    }
2664
2665    impl World {
2666        /// Rows `0..count`, `price = id`, shown in insertion order.
2667        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        /// Applies `ops`; the projection on screen stays (tombstones).
2684        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        /// Rebuilds the projection; returns the one it replaced.
2694        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        // RowSet: every edit, the anchor, and every column of a row.
2720        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        // Keep left the anchor at 2.
2739        model.apply(Extend, at(4, 0), rows, &columns);
2740        assert_eq!(keys(&model), [2, 3, 4]);
2741
2742        // RowSingle: Extend and AddRange replace; Toggle selects or clears.
2743        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        // CellSet: ranges of cells by `ViewColumnIx`, hidden columns left
2755        // out; columns visible in the order c0, c3, c1 with c2 hidden.
2756        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        // Toggling a row's last cell drops the row.
2774        model.apply(Toggle, at(11, 1), rows, &shuffled);
2775        assert!(!model.selection().contains(&11));
2776        // `Replace` on a selected cell of a larger selection collapses.
2777        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        // CellSingle: one cell.
2782        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        // Rows removed after the projection was built are not in a range.
2790        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        // Select all in the cell modes takes the visible cells; none in the
2799        // single modes.
2800        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        // A set mode to a single one keeps the focused row.
2816        model.set_mode(SelectionMode::RowSingle);
2817        assert_eq!(keys(&model), [4]);
2818        // Rows to cells clears.
2819        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        // `select_row` moves the focus and the anchor to a row on screen.
2835        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        // A hidden focus column moves right, else left.
2849        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        // Without a focus: the first cell, or the last row or column.
2875        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        // No wrap at the edges.
2890        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        // Tombstones: the move goes on past them in the same direction, and
2896        // stays where none is left.
2897        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        // Removed: the next row by the old order, the column kept.
2913        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        // The selection does not follow the focus in a single mode.
2918        model.remove_rows(&[], &previous);
2919        // Removed with the rows after it: the one before.
2920        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        // Everything removed: no focus.
2924        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        // Filtered: the nearest row of the old order the new one shows.
2929        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        // The last shown row filtered out: the one before it.
2938        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        // A focus the new order still shows stays, even if it moved.
2944        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        // Nothing left to show: no focus.
2949        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        // Removed and inserted again in one batch: a new row, not selected.
2967        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        // A new data set keeps only the keys it has; the focus (on 5) goes
2973        // with its key.
2974        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        // An extending drag paints only its range, by display position.
2993        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        // An adding drag paints and selects over the selection before it.
3005        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    // -----------------------------------------------------------------------
3017    // The model against a naive reference: `Vec` order, `HashSet` keys.
3018
3019    /// The naive store and screen: keys with price and insertion sequence;
3020    /// the screen's rows with the generation they were shown at.
3021    #[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        /// Positions of the live rows on screen, keyed.
3044        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        /// Returns true if the key was in the store.
3069        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        /// The successor of `key` along `order` among rows `accept` takes.
3092        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                // Data: updates, inserts, removals, a removal and insert of
3248                // one key in one batch, and now and then a new data set.
3249                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                    // Every row of the old set is gone, even under a key the
3264                    // new set has again.
3265                    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                    // Removed in this batch, even if inserted again: the next
3311                    // row on screen that is still live.
3312                    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                // Now and then a new order: a sort, a filter, or the same view.
3325                // A new data set is shown at once, as the grid does: until
3326                // then the focus kept by key is not on screen.
3327                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                // A gesture on the rows on screen.
3350                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                // 1, 3, 4: the selection is the reference's: no key selected
3427                // ahead of its gesture, none back after a removal.
3428                let selected: HashSet<u32> = model.selection().iter().copied().collect();
3429                assert_eq!(selected, reference.selected, "{context}");
3430                // 2: the scope is the selection within the screen, in order.
3431                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                // 5, 6: the focus is the reference's, on a live row on screen.
3444                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}