1use crate::{
9 env::VideoState, Env, GlobalState, LayoutRect, LayoutSize, LayoutSnapshot, MotionPropertyId,
10 MotionValue, RuntimeState, ViewHandle,
11};
12use fission_i18n::I18nRegistry;
13use fission_ir::WidgetId;
14use fission_layout::BoxConstraints;
15use fission_theme::Theme;
16use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
17use std::hash::{BuildHasher, Hash};
18
19pub struct View<'a, S: GlobalState> {
33 pub state: &'a S,
35 pub runtime: &'a RuntimeState,
37 pub env: &'a Env,
39 pub layout: Option<&'a LayoutSnapshot>,
41}
42
43impl<'a, S: GlobalState> View<'a, S> {
44 pub fn new(
45 state: &'a S,
46 runtime: &'a RuntimeState,
47 env: &'a Env,
48 layout: Option<&'a LayoutSnapshot>,
49 ) -> Self {
50 Self {
51 state,
52 runtime,
53 env,
54 layout,
55 }
56 }
57
58 pub fn state(&self) -> &S {
59 self.state
60 }
61
62 pub fn runtime(&self) -> &RuntimeState {
63 self.runtime
64 }
65
66 pub fn env(&self) -> &Env {
67 self.env
68 }
69
70 pub fn layout(&self) -> Option<&LayoutSnapshot> {
71 self.layout
72 }
73
74 pub fn theme(&self) -> &Theme {
75 &self.env.theme
76 }
77 pub fn i18n(&self) -> &I18nRegistry {
78 &self.env.i18n
79 }
80
81 pub fn get_rect(&self, id: WidgetId) -> Option<LayoutRect> {
82 let node_id: WidgetId = id.into();
83 self.layout.and_then(|l| l.get_node_rect(node_id))
84 }
85
86 pub fn get_constraints(&self, id: WidgetId) -> Option<BoxConstraints> {
87 let node_id: WidgetId = id.into();
88 self.layout.and_then(|l| l.get_node_constraints(node_id))
89 }
90
91 pub fn viewport_size(&self) -> LayoutSize {
92 self.env.viewport_size
93 }
94
95 pub fn select<R>(&self, selector: impl FnOnce(&S) -> R) -> R {
96 selector(self.state)
97 }
98
99 pub fn motion_value(&self, widget_id: WidgetId, property: MotionPropertyId) -> MotionValue {
100 self.runtime
101 .motion
102 .values
103 .get(&(widget_id, property.clone()))
104 .cloned()
105 .unwrap_or_else(|| property.default_value())
106 }
107
108 pub fn motion_scalar(&self, widget_id: WidgetId, property: MotionPropertyId) -> f32 {
109 self.runtime.motion.scalar_value(widget_id, property)
110 }
111
112 pub fn video_state(&self, widget_id: WidgetId) -> Option<&VideoState> {
113 self.runtime.video.states.get(&widget_id)
114 }
115}
116
117#[derive(Clone, Copy, Debug)]
123pub struct ValueView<'a, T> {
124 value: &'a T,
125}
126
127impl<'a, T> ValueView<'a, T> {
128 pub fn new(value: &'a T) -> Self {
129 Self { value }
130 }
131
132 pub fn borrow(&self) -> &'a T {
133 self.value
134 }
135
136 pub fn map<R>(&self, selector: impl FnOnce(&T) -> R) -> ComputedView<R> {
137 ComputedView::new(selector(self.value))
138 }
139}
140
141impl<T: Clone> ValueView<'_, T> {
142 pub fn get(&self) -> T {
143 self.value.clone()
144 }
145}
146
147impl<'a, T> ValueView<'a, Vec<T>> {
148 pub fn len(&self) -> usize {
149 self.value.len()
150 }
151
152 pub fn is_empty(&self) -> bool {
153 self.value.is_empty()
154 }
155
156 pub fn iter(&self) -> std::slice::Iter<'a, T> {
157 self.value.iter()
158 }
159}
160
161impl<'a, T> IntoIterator for ValueView<'a, Vec<T>> {
162 type Item = &'a T;
163 type IntoIter = std::slice::Iter<'a, T>;
164
165 fn into_iter(self) -> Self::IntoIter {
166 self.value.iter()
167 }
168}
169
170#[derive(Clone, Debug)]
172pub struct ComputedView<T> {
173 value: T,
174}
175
176impl<T> ComputedView<T> {
177 pub fn new(value: T) -> Self {
178 Self { value }
179 }
180
181 pub fn borrow(&self) -> &T {
182 &self.value
183 }
184
185 pub fn get(self) -> T {
186 self.value
187 }
188
189 pub fn map<R>(self, selector: impl FnOnce(&T) -> R) -> ComputedView<R> {
190 ComputedView::new(selector(&self.value))
191 }
192}
193
194pub trait FissionViewField {
199 type View<'a>
200 where
201 Self: 'a;
202
203 fn view_field<'a>(value: &'a Self) -> Self::View<'a>;
204}
205
206macro_rules! scalar_view_field {
207 ($($ty:ty),* $(,)?) => {
208 $(
209 impl FissionViewField for $ty {
210 type View<'a> = ValueView<'a, Self> where Self: 'a;
211
212 fn view_field<'a>(value: &'a Self) -> Self::View<'a> {
213 ValueView::new(value)
214 }
215 }
216 )*
217 };
218}
219
220scalar_view_field!(
221 bool, char, String, usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64
222);
223
224impl<T> FissionViewField for Vec<T> {
225 type View<'a>
226 = ValueView<'a, Self>
227 where
228 Self: 'a;
229
230 fn view_field<'a>(value: &'a Self) -> Self::View<'a> {
231 ValueView::new(value)
232 }
233}
234
235impl<T> FissionViewField for Option<T> {
236 type View<'a>
237 = ValueView<'a, Self>
238 where
239 Self: 'a;
240
241 fn view_field<'a>(value: &'a Self) -> Self::View<'a> {
242 ValueView::new(value)
243 }
244}
245
246impl<T, const N: usize> FissionViewField for [T; N] {
247 type View<'a>
248 = ValueView<'a, Self>
249 where
250 Self: 'a;
251
252 fn view_field<'a>(value: &'a Self) -> Self::View<'a> {
253 ValueView::new(value)
254 }
255}
256
257impl<T: Ord> FissionViewField for BTreeSet<T> {
258 type View<'a>
259 = ValueView<'a, Self>
260 where
261 Self: 'a;
262
263 fn view_field<'a>(value: &'a Self) -> Self::View<'a> {
264 ValueView::new(value)
265 }
266}
267
268impl<K: Ord, V> FissionViewField for BTreeMap<K, V> {
269 type View<'a>
270 = ValueView<'a, Self>
271 where
272 Self: 'a;
273
274 fn view_field<'a>(value: &'a Self) -> Self::View<'a> {
275 ValueView::new(value)
276 }
277}
278
279impl<T: Eq + Hash, S: BuildHasher> FissionViewField for HashSet<T, S> {
280 type View<'a>
281 = ValueView<'a, Self>
282 where
283 Self: 'a;
284
285 fn view_field<'a>(value: &'a Self) -> Self::View<'a> {
286 ValueView::new(value)
287 }
288}
289
290impl<K: Eq + Hash, V, S: BuildHasher> FissionViewField for HashMap<K, V, S> {
291 type View<'a>
292 = ValueView<'a, Self>
293 where
294 Self: 'a;
295
296 fn view_field<'a>(value: &'a Self) -> Self::View<'a> {
297 ValueView::new(value)
298 }
299}
300
301pub trait Selector<S: GlobalState> {
321 type Output;
323 fn select(view: ViewHandle<S>) -> Self::Output;
325}