Skip to main content

fission_core/ui/widgets/
video.rs

1use crate::lowering::{LoweringContext, NodeBuilder};
2use crate::ui::traits::Lower;
3use fission_ir::{
4    op::{EmbedKind, LayoutOp, Op},
5    NodeId, WidgetNodeId,
6};
7use serde::{Deserialize, Serialize};
8
9/// A platform-native video player widget.
10///
11/// The video is rendered by the platform's native player and embedded into the
12/// Fission layout as an opaque surface. Use [`BuildCtx::video_controls`] to
13/// create play/pause/seek action envelopes.
14///
15/// # Example
16///
17/// ```rust,ignore
18/// Video {
19///     source: "https://example.com/clip.mp4".into(),
20///     width: Some(640.0),
21///     height: Some(360.0),
22///     autoplay: true,
23///     loop_playback: false,
24///     ..Default::default()
25/// }
26/// .build(ctx, view);
27/// ```
28#[derive(Debug, Default, Clone, Serialize, Deserialize)]
29pub struct Video {
30    /// Stable widget identity (auto-derived from `source` if `None`).
31    pub id: Option<WidgetNodeId>,
32    /// URL or asset path to the video file.
33    pub source: String,
34    /// Fixed width in layout points.
35    pub width: Option<f32>,
36    /// Fixed height in layout points.
37    pub height: Option<f32>,
38    /// Whether to start playing immediately.
39    pub autoplay: bool,
40    /// Whether to loop playback when the video ends.
41    pub loop_playback: bool,
42}
43
44impl Video {
45    pub fn into_node(self) -> crate::ui::Node {
46        crate::ui::Node::Video(self)
47    }
48}
49
50impl Lower for Video {
51    fn lower(&self, cx: &mut LoweringContext) -> NodeId {
52        let widget_id = self
53            .id
54            .unwrap_or_else(|| WidgetNodeId::explicit(&self.source));
55        let layout_id = cx.widget_node_id(widget_id);
56
57        let embed_id = NodeBuilder::new(
58            cx.next_node_id(),
59            Op::Layout(LayoutOp::Embed {
60                kind: EmbedKind::Video,
61                widget_id,
62                width: self.width,
63                height: self.height,
64            }),
65        )
66        .build(cx);
67
68        let mut layout_builder = NodeBuilder::new(
69            layout_id,
70            Op::Layout(LayoutOp::Box {
71                width: self.width,
72                height: self.height,
73                min_width: None,
74                max_width: None,
75                min_height: None,
76                max_height: None,
77                padding: [0.0; 4],
78                flex_grow: 0.0,
79                flex_shrink: 0.0,
80                aspect_ratio: None,
81            }),
82        );
83        layout_builder.add_child(embed_id);
84        layout_builder.build(cx)
85    }
86}