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