dioxuscut_core/timeline/context.rs
1//! Timeline context — provides frame position and video config to all
2//! descendant Dioxus components via context injection.
3//!
4//! Equivalent to Remotion's `TimelineContext` + `SequenceContext`.
5
6use crate::types::VideoConfig;
7
8/// The current playback position in the composition, relative to the
9/// **component's own origin** (i.e., already offset by any parent `Sequence`).
10///
11/// This is the value returned by `use_current_frame()`.
12#[derive(Debug, Clone, PartialEq)]
13pub struct TimelineContext {
14 /// Frame number relative to the component root (0-indexed).
15 pub frame: u32,
16 /// Absolute frame in the root composition (for internal use).
17 pub(crate) absolute_frame: u32,
18}
19
20impl TimelineContext {
21 /// Create a new context starting at `frame`.
22 pub fn new(frame: u32) -> Self {
23 Self {
24 frame,
25 absolute_frame: frame,
26 }
27 }
28
29 /// Create a child context offset from its parent's local timeline.
30 ///
31 /// The root absolute frame is preserved so nested sequences do not
32 /// accidentally reinterpret a relative `from` value as a root offset.
33 pub fn offset_from(parent: &Self, offset: u32) -> Self {
34 let frame = parent.frame.saturating_sub(offset);
35 Self {
36 frame,
37 absolute_frame: parent.absolute_frame,
38 }
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn nested_offsets_use_the_parent_local_frame() {
48 let root = TimelineContext::new(45);
49 let parent = TimelineContext::offset_from(&root, 30);
50 let child = TimelineContext::offset_from(&parent, 10);
51
52 assert_eq!(parent.frame, 15);
53 assert_eq!(child.frame, 5);
54 assert_eq!(child.absolute_frame, 45);
55 }
56}
57
58/// Context that carries the `VideoConfig` (width, height, fps, duration).
59///
60/// This is the value returned by `use_video_config()`.
61#[derive(Debug, Clone, PartialEq)]
62pub struct VideoConfigContext(pub VideoConfig);