pax_runtime_api/properties/
local_property.rs1use super::*;
2
3impl<T: PropertyValue> Interpolatable for LocalProperty<T> {
4 fn interpolate(&self, other: &Self, t: f64) -> Self {
5 let cp_self = self.clone();
6 let cp_other = other.clone();
7 LocalProperty::computed(
8 move || cp_self.get().interpolate(&cp_other.get(), t),
9 &[self.untyped(), other.untyped()],
10 )
11 }
12}
13#[derive(Clone)]
24pub struct LocalProperty<T> {
25 pub(super) untyped: UntypedProperty,
26 _phantom: PhantomData<T>,
27}
28
29impl<T: PropertyValue> PropertyBinding<T> for LocalProperty<T> {
30 fn from_value(value: T) -> Self {
31 Self::new(value)
32 }
33 fn local(&self) -> Self {
34 self.clone()
35 }
36 fn from_local(source: Self) -> Self {
37 source
38 }
39 fn value_for_conversion(&self) -> T {
40 self.get()
41 }
42}
43
44impl<T: PropertyValue + std::fmt::Debug> std::fmt::Debug for LocalProperty<T> {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 write!(f, "LocalProperty ({:?})", self.get())
47 }
48}
49
50impl<T: PropertyValue> LocalProperty<T> {
51 pub fn local(&self) -> Self {
53 self.clone()
54 }
55
56 pub fn new(val: T) -> Self {
58 Self::new_optional_name(val, None)
59 }
60
61 pub fn new_from_untyped(untyped: UntypedProperty) -> Self {
63 Self {
64 untyped,
65 _phantom: PhantomData {},
66 }
67 }
68
69 pub fn computed(evaluator: impl Fn() -> T + 'static, dependents: &[UntypedProperty]) -> Self {
71 Self::computed_with_config(evaluator, dependents, None, None)
72 }
73
74 pub fn computed_with_cutoff(
81 evaluator: impl Fn() -> T + 'static,
82 dependents: &[UntypedProperty],
83 cutoff: impl Fn(&T, &T) -> bool + 'static,
84 ) -> Self {
85 Self::computed_with_config(evaluator, dependents, None, Some(Rc::new(cutoff)))
86 }
87
88 pub fn new_with_name(val: T, name: &str) -> Self {
90 Self::new_optional_name(val, Some(name))
91 }
92
93 pub fn computed_with_name(
95 evaluator: impl Fn() -> T + 'static,
96 dependents: &[UntypedProperty],
97 name: &str,
98 ) -> Self {
99 Self::computed_with_config(evaluator, dependents, Some(name), None)
100 }
101
102 pub fn computed_with_cutoff_and_name(
104 evaluator: impl Fn() -> T + 'static,
105 dependents: &[UntypedProperty],
106 cutoff: impl Fn(&T, &T) -> bool + 'static,
107 name: &str,
108 ) -> Self {
109 Self::computed_with_config(evaluator, dependents, Some(name), Some(Rc::new(cutoff)))
110 }
111
112 fn new_optional_name(val: T, name: Option<&str>) -> Self {
113 Self {
114 untyped: UntypedProperty::new(val, Vec::with_capacity(0), PropertyType::Literal, name),
115 _phantom: PhantomData {},
116 }
117 }
118
119 fn computed_with_config(
120 evaluator: impl Fn() -> T + 'static,
121 dependents: &[UntypedProperty],
122 name: Option<&str>,
123 cutoff: Option<Rc<dyn Fn(&T, &T) -> bool>>,
124 ) -> Self {
125 let inbound: Vec<_> = dependents.iter().map(|v| v.get_id()).collect();
126 let start_val = T::default();
127 let evaluator = Rc::new(super::shared_graph::owner_evaluator(evaluator));
128 Self {
129 untyped: UntypedProperty::new(
130 start_val,
131 inbound,
132 PropertyType::Computed {
133 evaluator,
134 cutoff: cutoff.map(|predicate| properties_table::Cutoff {
135 predicate,
136 initialized: false,
137 }),
138 },
139 name,
140 ),
141 _phantom: PhantomData {},
142 }
143 }
144
145 pub fn ease_to<D: Into<Duration>>(&self, end_val: T, duration: D, curve: EasingCurve) {
151 self.ease_to_value(end_val, duration.into(), curve, true);
152 }
153
154 pub fn ease_to_later<D: Into<Duration>>(&self, end_val: T, duration: D, curve: EasingCurve) {
157 self.ease_to_value(end_val, duration.into(), curve, false);
158 }
159
160 fn ease_to_value(&self, end_val: T, duration: Duration, curve: EasingCurve, overwrite: bool) {
162 PROPERTY_TABLE.with(|t| {
163 t.transition(
164 self.untyped.id,
165 TransitionQueueEntry {
166 duration,
167 curve,
168 ending_value: end_val,
169 },
170 overwrite,
171 )
172 })
173 }
174
175 pub fn cancel_transitions(&self) {
181 PROPERTY_TABLE.with(|t| t.cancel_transitions::<T>(self.untyped.id));
182 }
183
184 pub fn get(&self) -> T {
188 PROPERTY_TABLE.with(|t| t.get_value(self.untyped.id))
189 }
190
191 pub fn set(&self, val: T) {
194 PROPERTY_TABLE.with(|t| t.set_value(self.untyped.id, val));
195 }
196
197 #[doc(hidden)]
202 pub fn invalidate(&self) {
203 PROPERTY_TABLE.with(|t| t.invalidate(self.untyped.id));
204 }
205
206 pub fn set_if_neq(&self, val: T) -> bool
210 where
211 T: PartialEq,
212 {
213 let should_set =
214 PROPERTY_TABLE.with(|t| t.read_value(self.untyped.id, |current: &T| current != &val));
215 if should_set {
216 self.set(val);
217 }
218 should_set
219 }
220
221 pub fn update(&self, f: impl FnOnce(&mut T)) {
225 let mut val = self.get();
228 f(&mut val);
229 self.set(val);
230 }
231
232 pub fn read<V>(&self, f: impl FnOnce(&T) -> V) -> V {
237 PROPERTY_TABLE.with(|t| t.read_value(self.untyped.id, f))
238 }
239
240 pub fn replace_with(&self, target: LocalProperty<T>) {
246 PROPERTY_TABLE.with(|t| {
247 t.replace_property_keep_outbound_connections::<T>(self.untyped.id, target.untyped.id)
249 })
250 }
251
252 pub fn untyped(&self) -> UntypedProperty {
254 self.untyped.clone()
255 }
256}
257
258impl<T: PropertyValue> Default for LocalProperty<T> {
259 fn default() -> Self {
260 LocalProperty::new(T::default())
261 }
262}
263
264impl<'de, T: PropertyValue + Deserialize<'de>> Deserialize<'de> for LocalProperty<T> {
267 fn deserialize<D>(deserializer: D) -> Result<LocalProperty<T>, D::Error>
268 where
269 D: serde::Deserializer<'de>,
270 {
271 let value = T::deserialize(deserializer)?;
272 Ok(LocalProperty::new(value))
273 }
274}
275
276impl<T: PropertyValue + Serialize> Serialize for LocalProperty<T> {
277 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
278 where
279 S: serde::Serializer,
280 {
281 self.get().serialize(serializer)
283 }
284}