Skip to main content

gpui_kit_grid/state/
tree.rs

1//! The tree on screen: turning a tree on and off, the column that shows it,
2//! and what the keys and the menu ask of a tree row.
3//!
4//! The rest is shared with group captions: the indent and the chevron, the
5//! press on a chevron and the keys (`state/disclosure.rs`), the selection
6//! of tree rows ([`SelectionPolicy`](crate::SelectionPolicy),
7//! `state/selection.rs`).
8
9use std::mem;
10
11use gpui_kit::Context;
12
13use super::{
14    ColumnOf, KeyOf, LiveGridDelegate, LiveGridState,
15    events::{ExpandTarget, GroupOrigin},
16    menu::GridTarget,
17};
18use crate::{
19    engine::TreeSpec,
20    model::{ColumnIx, GridColumns as _, GridRow as _},
21};
22
23impl<D: LiveGridDelegate> LiveGridState<D> {
24    /// Shows the rows as a tree by `GridRow::parent_key` (`Some`) or flat
25    /// (`None`), and rebuilds at once. The new order is shown from the top,
26    /// unless the focused line (a row or a group caption) is on screen and
27    /// still shown: it keeps its screen line. A tree replaces the grouping,
28    /// as [`Self::set_group_by`] replaces a tree. Emits `GroupChange` with
29    /// `GroupOrigin::Programmatic`, after `OrderHeldChange { held: false }`
30    /// if a held order was dropped; a `ProjectionChange` follows.
31    ///
32    /// A [`TreeSpec`] has no equality (its rules are closures), so a tree
33    /// always rebuilds and emits; `None` on a view that is no tree does
34    /// nothing. The expansion of tree rows is kept by key: turning the tree
35    /// off and on again brings it back.
36    pub fn set_tree(&mut self, tree: Option<TreeSpec<D::Row>>, cx: &mut Context<Self>) {
37        self.apply_tree(tree, GroupOrigin::Programmatic, cx);
38    }
39
40    /// The one path of every tree change but `set_view`, which compares the
41    /// whole view: `set_tree` and a restored layout. Returns false, doing
42    /// nothing, for `None` on a view that is no tree; otherwise puts the
43    /// tree into the view (a grouping goes) or makes the view flat, and
44    /// rebuilds at once, as a user action, shown as any new view
45    /// (`view_changed`): `OrderHeldChange { held: false }` if a held order
46    /// is dropped, then `GroupChange { origin }`.
47    pub(crate) fn apply_tree(
48        &mut self,
49        tree: Option<TreeSpec<D::Row>>,
50        origin: GroupOrigin,
51        cx: &mut Context<Self>,
52    ) -> bool {
53        let view = mem::take(&mut self.view);
54        let is_tree = view.tree().is_some();
55        self.view = match tree {
56            Some(tree) => view.with_tree(tree),
57            None if is_tree => view.with_group_by(std::iter::empty()),
58            None => view,
59        };
60        if !is_tree && self.view.tree().is_none() {
61            return false;
62        }
63        self.view_changed(None, Some(origin), cx);
64        true
65    }
66
67    /// The column that shows the tree now: [`TreeSpec::column`] if it is
68    /// visible, else the first visible column in display order (also while
69    /// a tree projection is still shown for a view that is no tree any
70    /// more). Read from the column layout every frame, so hiding or moving
71    /// columns never loses the tree.
72    pub(crate) fn tree_column(&self) -> Option<ColumnIx> {
73        self.view
74            .tree()
75            .and_then(|tree| tree.column())
76            .map(|column| column.ix())
77            .filter(|ix| self.columns.is_visible(*ix))
78            .or_else(|| self.columns.visible().next())
79    }
80
81    /// Returns true if the shown projection is a tree and the row with
82    /// `key` has shown children in it.
83    pub(super) fn is_tree_branch(&self, key: &KeyOf<D>) -> bool {
84        self.projection.is_tree()
85            && self
86                .snapshot
87                .row_id(key)
88                .is_some_and(|id| self.projection.branch(id).is_some())
89    }
90
91    /// The parent of a tree row in the shown projection: its nearest
92    /// ancestor the view passes (a filter may hide the rows between).
93    /// `None` for a root, and outside a tree. O(depth), once per key press.
94    pub(super) fn tree_parent(&self, key: &KeyOf<D>) -> Option<KeyOf<D>> {
95        let (projection, snapshot) = (&self.projection, &self.snapshot);
96        let tree = projection.tree_data()?;
97        let id = snapshot.row_id(key)?;
98        // A root: no parent, an orphan, or a row of a cycle.
99        if tree.depth(projection.order_ix(id)?) == 0 {
100            return None;
101        }
102        let mut row = snapshot.row(id)?;
103        // Up the parent keys to the nearest row the projection holds; the
104        // bound ends a cycle of rows it does not.
105        for _ in 0..=u16::MAX {
106            let parent = snapshot.row_id(row.parent_key()?)?;
107            row = snapshot.row(parent)?;
108            if projection.contains(parent) {
109                return Some(row.key().clone());
110            }
111        }
112        None
113    }
114
115    /// Whether the line of a menu target is expanded, if it expands: a
116    /// group caption, or a tree row with children.
117    pub(super) fn target_expansion(
118        &self,
119        target: &GridTarget<KeyOf<D>, ColumnOf<D>>,
120    ) -> Option<bool> {
121        match target {
122            GridTarget::GroupCaption { group, .. } => {
123                Some(self.is_expanded(&ExpandTarget::Group(group.clone())))
124            }
125            GridTarget::Cell { key, .. } if self.is_tree_branch(key) => {
126                Some(self.is_expanded(&ExpandTarget::Row(key.clone())))
127            }
128            _ => None,
129        }
130    }
131}