Skip to main content

gpui_ce_motion/
orchestration.rs

1//! Retained variant inheritance and frame-boundary parent/child sequencing.
2use crate::{
3    Completion, CompletionStatus, Frame, Motion, MotionState, Target, Transition, Variants,
4};
5
6use std::{fmt, time::Duration};
7
8/// Timing relationship between an element and its direct children.
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
10pub enum ChildrenOrder {
11    /// Parent and children begin together.
12    #[default]
13    Together,
14    /// Children begin after the parent's animation completes.
15    BeforeChildren,
16    /// Parent begins after all child subtrees complete.
17    AfterChildren,
18}
19
20/// Explicit application-owned defaults. Pass this value through render helpers;
21/// it does not install a global provider or override the OS reduced-motion policy.
22#[derive(Clone, Copy, Debug, Default)]
23pub struct MotionConfig {
24    transition: Transition,
25    stagger: Duration,
26}
27
28impl MotionConfig {
29    /// Chooses the transition for descriptions created with `motion`.
30    pub fn transition(mut self, transition: Transition) -> Self {
31        self.transition = transition;
32
33        self
34    }
35
36    /// Chooses the interval between eligible direct children.
37    pub fn stagger(mut self, interval: Duration) -> Self {
38        self.stagger = interval;
39
40        self
41    }
42
43    /// Creates a declaration using these defaults. Per-value overrides still apply.
44    pub fn motion(self, target: Target) -> Motion {
45        Motion::new(target).transition(self.transition)
46    }
47}
48
49/// Invalid tree identity or a variant missing from an affected node.
50#[derive(Clone, Copy, Debug, PartialEq, Eq)]
51pub enum TreeError {
52    /// Duplicate key, missing parent, or unknown requested node.
53    Identity,
54    /// A participating node does not define the requested variant.
55    Variant,
56    /// Nesting exceeds the bounded 256-level preparation limit.
57    Depth,
58}
59
60impl fmt::Display for TreeError {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        write!(f, "invalid motion tree operation: {self:?}")
63    }
64}
65
66impl std::error::Error for TreeError {}
67
68/// One prepared node. Add parents before children, making cycles impossible.
69#[derive(Debug)]
70pub struct MotionNode<K, V> {
71    key: K,
72    parent: Option<K>,
73    initial: Target,
74    variants: Variants<V>,
75    order: ChildrenOrder,
76    exit_order: Option<ChildrenOrder>,
77    inherit: bool,
78}
79
80impl<K, V> MotionNode<K, V> {
81    /// Creates a node with an explicit initial presentation and variant table.
82    pub fn new(key: K, initial: Target, variants: Variants<V>) -> Self {
83        Self {
84            key,
85            parent: None,
86            initial,
87            variants,
88            order: ChildrenOrder::Together,
89            exit_order: None,
90            inherit: true,
91        }
92    }
93
94    /// Associates a previously added parent.
95    pub fn parent(mut self, parent: K) -> Self {
96        self.parent = Some(parent);
97
98        self
99    }
100
101    /// Sequences this node relative to its children.
102    pub fn when(mut self, order: ChildrenOrder) -> Self {
103        self.order = order;
104
105        self
106    }
107
108    /// Uses a separate parent/child timing policy when this node exits.
109    /// For example, enter before children and leave after their exits finish.
110    pub fn exit_when(mut self, order: ChildrenOrder) -> Self {
111        self.exit_order = Some(order);
112
113        self
114    }
115
116    /// Controls ancestor variant propagation. Explicit `set_node_variant` still works.
117    pub fn inherit(mut self, inherit: bool) -> Self {
118        self.inherit = inherit;
119
120        self
121    }
122}
123
124#[derive(Debug)]
125struct Node<K, V> {
126    declaration: MotionNode<K, V>,
127    parent: Option<usize>,
128    children: Vec<usize>,
129    rank: usize,
130    state: MotionState,
131    current: Motion,
132    desired: Motion,
133    pending: Option<Motion>,
134    clock: Duration,
135    eligible: Option<Duration>,
136    running: bool,
137    done: bool,
138    frame: Frame,
139}
140
141/// An explicitly retained tree of independently interruptible motion states.
142/// Variant changes validate before mutation and capture the old presentation at
143/// the change timestamp. Gated nodes hold their local clock and velocity until
144/// eligible. Sequencing starts at the next sampled frame boundary, not through a
145/// timer. Preparation allocates; normal sampling does not. Host owns focus policy.
146#[derive(Debug)]
147pub struct MotionTree<K, V> {
148    nodes: Vec<Node<K, V>>,
149    config: MotionConfig,
150    last: Duration,
151    active: bool,
152    observers: crate::completion::CompletionSource,
153}
154
155impl<K, V> Default for MotionTree<K, V> {
156    fn default() -> Self {
157        Self {
158            nodes: Vec::new(),
159            config: MotionConfig::default(),
160            last: Duration::ZERO,
161            active: false,
162            observers: Default::default(),
163        }
164    }
165}
166
167impl<K: PartialEq, V: PartialEq> MotionTree<K, V> {
168    /// Applies explicit defaults to this tree. Each node's `Motion` keeps its own transition.
169    pub fn config(mut self, config: MotionConfig) -> Self {
170        self.config = config;
171
172        self
173    }
174
175    /// Adds a unique node. Parents must already exist; invalid additions are transactional.
176    pub fn add(&mut self, declaration: MotionNode<K, V>) -> Result<(), TreeError> {
177        if self
178            .nodes
179            .iter()
180            .any(|node| node.declaration.key == declaration.key)
181        {
182            return Err(TreeError::Identity);
183        }
184
185        let parent = declaration
186            .parent
187            .as_ref()
188            .map(|key| self.index(key))
189            .transpose()?;
190
191        let mut ancestor = parent;
192        let mut depth = 0;
193
194        while let Some(index) = ancestor {
195            depth += 1;
196            ancestor = self.nodes[index].parent;
197        }
198
199        if depth >= 256 {
200            return Err(TreeError::Depth);
201        }
202
203        let rank = parent.map_or(0, |index| self.nodes[index].children.len());
204        let current = Motion::new(declaration.initial).transition(Transition::Instant);
205        let mut state = MotionState::default();
206        let frame = state.sample(current, Duration::ZERO, false);
207        let index = self.nodes.len();
208
209        self.nodes.push(Node {
210            declaration,
211            parent,
212            rank,
213            children: Vec::new(),
214            state,
215            current,
216            desired: current,
217            pending: None,
218            clock: Duration::ZERO,
219            eligible: None,
220            running: false,
221            done: true,
222            frame,
223        });
224
225        if let Some(parent) = parent {
226            self.nodes[parent].children.push(index);
227        }
228
229        Ok(())
230    }
231
232    fn index(&self, key: &K) -> Result<usize, TreeError> {
233        self.nodes
234            .iter()
235            .position(|node| node.declaration.key == *key)
236            .ok_or(TreeError::Identity)
237    }
238
239    fn inherits(&self, mut index: usize, root: Option<usize>) -> bool {
240        loop {
241            if Some(index) == root {
242                return true;
243            }
244
245            if !self.nodes[index].declaration.inherit {
246                return false;
247            }
248
249            match self.nodes[index].parent {
250                Some(parent) => index = parent,
251                None => return root.is_none(),
252            }
253        }
254    }
255
256    /// Propagates a variant through inheriting roots/descendants. A non-inheriting
257    /// node blocks propagation through its subtree. New variants interrupt smoothly.
258    pub fn set_variant(
259        &mut self,
260        variant: &V,
261        now: Duration,
262        reduced: bool,
263    ) -> Result<(), TreeError> {
264        self.change(None, variant, now, reduced)
265    }
266
267    /// Explicitly changes one subtree, including its root even when inheritance is disabled.
268    pub fn set_node_variant(
269        &mut self,
270        key: &K,
271        variant: &V,
272        now: Duration,
273        reduced: bool,
274    ) -> Result<(), TreeError> {
275        let root = self.index(key)?;
276
277        self.change(Some(root), variant, now, reduced)
278    }
279
280    /// Retargets one node from application-computed data while retaining its
281    /// parent/child timing policy. Other nodes keep their current declarations.
282    /// Returns `Identity` without mutation for an unknown key.
283    pub fn set_motion(
284        &mut self,
285        key: &K,
286        motion: Motion,
287        now: Duration,
288        reduced: bool,
289    ) -> Result<(), TreeError> {
290        let index = self.index(key)?;
291
292        if self.nodes[index].desired == motion {
293            return Ok(());
294        }
295
296        self.sample(now, reduced);
297        self.observers.resolve(CompletionStatus::Cancelled);
298        let node = &mut self.nodes[index];
299
300        node.desired = motion;
301        node.pending = Some(motion);
302        node.running = false;
303        node.done = false;
304        node.eligible = None;
305        self.active = true;
306
307        Ok(())
308    }
309
310    fn change(
311        &mut self,
312        root: Option<usize>,
313        variant: &V,
314        now: Duration,
315        reduced: bool,
316    ) -> Result<(), TreeError> {
317        for (index, node) in self.nodes.iter().enumerate() {
318            if self.inherits(index, root) && node.declaration.variants.get(variant).is_none() {
319                return Err(TreeError::Variant);
320            }
321        }
322
323        if !self.nodes.iter().enumerate().any(|(index, node)| {
324            self.inherits(index, root)
325                && node.declaration.variants.get(variant) != Some(node.desired)
326        }) {
327            return Ok(());
328        }
329
330        self.sample(now, reduced);
331        self.observers.resolve(CompletionStatus::Cancelled);
332
333        for index in 0..self.nodes.len() {
334            if self.inherits(index, root)
335                && self.nodes[index].declaration.variants.get(variant)
336                    != Some(self.nodes[index].desired)
337            {
338                let node = &mut self.nodes[index];
339
340                node.pending = node.declaration.variants.get(variant);
341                node.desired = node.pending.expect("validated variant");
342                node.running = false;
343                node.done = false;
344                node.eligible = None;
345            }
346        }
347
348        self.active = self.nodes.iter().any(|node| !node.done);
349
350        Ok(())
351    }
352
353    fn own(&mut self, index: usize, allowed: bool, delta: Duration, now: Duration, reduced: bool) {
354        let delay = self
355            .config
356            .stagger
357            .saturating_mul(self.nodes[index].rank.min(u32::MAX as usize) as u32);
358
359        let node = &mut self.nodes[index];
360
361        if node.done {
362            return;
363        }
364
365        if !allowed && !reduced {
366            return;
367        }
368
369        let due = *node.eligible.get_or_insert(now.saturating_add(delay));
370
371        if now < due && !reduced {
372            return;
373        }
374
375        if let Some(motion) = node.pending.take() {
376            node.current = motion;
377        } else if node.running {
378            node.clock = node.clock.saturating_add(delta);
379        }
380
381        node.frame = node.state.sample(node.current, node.clock, reduced);
382        node.running = true;
383        node.done = !node.frame.is_animating();
384    }
385
386    fn subtree(
387        &mut self,
388        index: usize,
389        allowed: bool,
390        delta: Duration,
391        now: Duration,
392        reduced: bool,
393    ) -> bool {
394        let node = &self.nodes[index];
395
396        let order = if node.desired.is_present() {
397            node.declaration.order
398        } else {
399            node.declaration
400                .exit_order
401                .unwrap_or(node.declaration.order)
402        };
403
404        if order != ChildrenOrder::AfterChildren {
405            self.own(index, allowed, delta, now, reduced);
406        }
407
408        let children_allowed =
409            allowed && (order != ChildrenOrder::BeforeChildren || self.nodes[index].done);
410
411        let mut children_done = true;
412
413        for i in 0..self.nodes[index].children.len() {
414            let child = self.nodes[index].children[i];
415
416            children_done &= self.subtree(child, children_allowed || reduced, delta, now, reduced);
417        }
418
419        if order == ChildrenOrder::AfterChildren {
420            self.own(index, allowed && children_done, delta, now, reduced);
421        }
422
423        self.nodes[index].done && children_done
424    }
425
426    /// Advances one monotonic frame. Reduced motion bypasses gates and delays.
427    pub fn sample(&mut self, now: Duration, reduced: bool) {
428        let now = now.max(self.last);
429        let delta = now.saturating_sub(self.last);
430        let mut done = true;
431
432        for index in 0..self.nodes.len() {
433            if self.nodes[index].parent.is_none() {
434                done &= self.subtree(index, true, delta, now, reduced);
435            }
436        }
437
438        if done && self.active {
439            self.observers.resolve(CompletionStatus::Completed);
440        }
441
442        self.active = !done;
443        self.last = now;
444    }
445
446    /// Current node presentation, for ordinary native `Frame` application.
447    pub fn get(&self, key: &K) -> Option<Frame> {
448        self.index(key).ok().map(|index| self.nodes[index].frame)
449    }
450
451    /// Keeps an exiting root mounted until its entire nested subtree exits.
452    /// Once false, the host may release that view/entity and restore focus.
453    pub fn is_mounted(&self, key: &K) -> bool {
454        let Ok(root) = self.index(key) else {
455            return false;
456        };
457
458        self.nodes.iter().enumerate().any(|(mut index, node)| {
459            if !node.frame.is_mounted() {
460                return false;
461            }
462
463            loop {
464                if index == root {
465                    return true;
466                }
467
468                match self.nodes[index].parent {
469                    Some(parent) => index = parent,
470                    None => return false,
471                }
472            }
473        })
474    }
475
476    /// Whether a trajectory, dependent child, or stagger delay is pending.
477    pub fn is_animating(&self) -> bool {
478        self.active
479    }
480
481    /// Observes the current change; replacing it or dropping the tree cancels.
482    pub fn completion(&mut self) -> Completion {
483        self.observers.subscribe(!self.active)
484    }
485}