1use crate::{
9 env::VideoState,
10 registry::{AnimationPropertyId, VideoRegistration},
11 ui::{Align, Button, Checkbox, Column, Container, Grid, GridItem, Image, LazyColumn, Node, Overlay, Positioned, Radio, Row, Scroll, Slider, Spacer, Switch, Text, TextInput, Video, ZStack},
12 AppState, BuildCtx, Env, RuntimeState, LayoutSnapshot, LayoutRect, LayoutSize,
13};
14use fission_i18n::I18nRegistry;
15use fission_ir::{NodeId, WidgetNodeId};
16use fission_layout::BoxConstraints;
17use fission_theme::Theme;
18
19pub struct View<'a, S: AppState> {
38 pub state: &'a S,
40 pub runtime: &'a RuntimeState,
42 pub env: &'a Env,
44 pub layout: Option<&'a LayoutSnapshot>,
46}
47
48impl<'a, S: AppState> View<'a, S> {
49 pub fn new(state: &'a S, runtime: &'a RuntimeState, env: &'a Env, layout: Option<&'a LayoutSnapshot>) -> Self {
50 Self {
51 state,
52 runtime,
53 env,
54 layout,
55 }
56 }
57
58 pub fn theme(&self) -> &Theme {
59 &self.env.theme
60 }
61 pub fn i18n(&self) -> &I18nRegistry {
62 &self.env.i18n
63 }
64
65 pub fn get_rect(&self, id: WidgetNodeId) -> Option<LayoutRect> {
66 let node_id: NodeId = id.into();
67 self.layout.and_then(|l| l.get_node_rect(node_id))
68 }
69
70 pub fn get_constraints(&self, id: WidgetNodeId) -> Option<BoxConstraints> {
71 let node_id: NodeId = id.into();
72 self.layout.and_then(|l| l.get_node_constraints(node_id))
73 }
74
75 pub fn viewport_size(&self) -> LayoutSize {
76 self.env.viewport_size
77 }
78
79 pub fn select<T: Selector<S>>(&self) -> T::Output {
80 T::select(self)
81 }
82
83 pub fn animation_value(&self, widget_id: WidgetNodeId, property: &AnimationPropertyId) -> f32 {
84 self.runtime
85 .animation
86 .values
87 .get(&(widget_id, property.clone()))
88 .copied()
89 .unwrap_or_else(|| property.default_value())
90 }
91
92 pub fn video_state(&self, widget_id: WidgetNodeId) -> Option<&VideoState> {
93 self.runtime.video.states.get(&widget_id)
94 }
95}
96
97pub trait Selector<S: AppState> {
117 type Output;
119 fn select(view: &View<S>) -> Self::Output;
121}
122
123pub trait Widget<S: AppState> {
146 fn build(&self, ctx: &mut BuildCtx<S>, view: &View<S>) -> Node;
151}
152
153impl<S: AppState> Widget<S> for Node {
155 fn build(&self, _ctx: &mut BuildCtx<S>, _view: &View<S>) -> Node {
156 self.clone()
157 }
158}
159
160macro_rules! impl_widget_for_primitive {
161 ($t:ty, $v:ident) => {
162 impl<S: AppState> Widget<S> for $t {
163 fn build(&self, _ctx: &mut BuildCtx<S>, _view: &View<S>) -> Node {
164 Node::$v(self.clone())
165 }
166 }
167 };
168}
169
170impl_widget_for_primitive!(Row, Row);
171impl_widget_for_primitive!(Column, Column);
172impl_widget_for_primitive!(Align, Align);
173impl_widget_for_primitive!(Text, Text);
174impl_widget_for_primitive!(Button, Button);
175impl_widget_for_primitive!(TextInput, TextInput);
176impl_widget_for_primitive!(Scroll, Scroll);
177impl_widget_for_primitive!(Image, Image);
178impl_widget_for_primitive!(ZStack, ZStack);
179impl_widget_for_primitive!(Overlay, Overlay);
180impl_widget_for_primitive!(Container, Container);
181impl_widget_for_primitive!(Grid, Grid);
182impl_widget_for_primitive!(GridItem, GridItem);
183impl_widget_for_primitive!(Checkbox, Checkbox);
184impl_widget_for_primitive!(Switch, Switch);
185impl_widget_for_primitive!(Radio, Radio);
186impl_widget_for_primitive!(Positioned, Positioned);
187impl_widget_for_primitive!(Spacer, Spacer);
188impl_widget_for_primitive!(Slider, Slider);
189impl_widget_for_primitive!(LazyColumn, LazyColumn);
190
191impl<S: AppState> Widget<S> for Video {
192 fn build(&self, ctx: &mut BuildCtx<S>, _view: &View<S>) -> Node {
193 let mut video = self.clone();
194 let id = video
195 .id
196 .unwrap_or_else(|| WidgetNodeId::explicit(&video.source));
197 video.id = Some(id);
198
199 ctx.register_video(VideoRegistration {
200 node_id: id,
201 source: video.source.clone(),
202 autoplay: video.autoplay,
203 loop_playback: video.loop_playback,
204 });
205
206 Node::Video(video)
207 }
208}