ferroui_base/rendering/composition/server/
composition_property.rs1use super::IServerObject;
2use crate::rendering::composition::expressions::ExpressionVariant;
3use std::any::{Any, TypeId};
4use std::sync::atomic::{AtomicI32, Ordering};
5
6static NEXT_ID: AtomicI32 = AtomicI32::new(1);
7
8pub struct CompositionProperty {
15 id: i32,
16 name: &'static str,
17 owner: TypeId,
18 owner_name: &'static str,
19 get_variant: Option<fn(&dyn IServerObject) -> ExpressionVariant>,
20 set_variant: Option<fn(&dyn IServerObject, ExpressionVariant)>,
21}
22
23impl CompositionProperty {
24 fn new(
25 id: i32,
26 name: &'static str,
27 owner: TypeId,
28 owner_name: &'static str,
29 get_variant: Option<fn(&dyn IServerObject) -> ExpressionVariant>,
30 set_variant: Option<fn(&dyn IServerObject, ExpressionVariant)>,
31 ) -> Self {
32 Self { id, name, owner, owner_name, get_variant, set_variant }
33 }
34
35 pub fn id(&self) -> i32 {
36 self.id
37 }
38
39 pub fn name(&self) -> &'static str {
40 self.name
41 }
42
43 pub fn owner(&self) -> TypeId {
46 self.owner
47 }
48
49 pub fn owner_name(&self) -> &'static str {
51 self.owner_name
52 }
53
54 pub fn get_variant(&self) -> Option<fn(&dyn IServerObject) -> ExpressionVariant> {
57 self.get_variant
58 }
59
60 pub fn set_variant(&self) -> Option<fn(&dyn IServerObject, ExpressionVariant)> {
66 self.set_variant
67 }
68
69 pub fn register<TOwner: Any, TField>(
72 name: &'static str,
73 owner_name: &'static str,
74 get_field: fn(&dyn IServerObject) -> TField,
75 set_field: fn(&dyn IServerObject, TField),
76 get_variant: Option<fn(&dyn IServerObject) -> ExpressionVariant>,
77 set_variant: Option<fn(&dyn IServerObject, ExpressionVariant)>,
78 ) -> CompositionPropertyOf<TField> {
79 let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
80 CompositionPropertyOf {
81 base: CompositionProperty::new(id, name, TypeId::of::<TOwner>(), owner_name, get_variant, set_variant),
82 get_field,
83 set_field,
84 }
85 }
86}
87
88static DYNAMIC_PROPERTIES: std::sync::OnceLock<
90 std::sync::Mutex<std::collections::HashMap<String, &'static CompositionProperty>>,
91> = std::sync::OnceLock::new();
92
93impl CompositionProperty {
94 pub fn dynamic(name: &str) -> &'static CompositionProperty {
108 let mut properties = DYNAMIC_PROPERTIES
109 .get_or_init(Default::default)
110 .lock()
111 .unwrap_or_else(std::sync::PoisonError::into_inner);
112 if let Some(property) = properties.get(name) {
113 return property;
114 }
115 let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
116 let name: &'static str = Box::leak(name.to_owned().into_boxed_str());
117 let property: &'static CompositionProperty = Box::leak(Box::new(CompositionProperty::new(
118 id,
119 name,
120 TypeId::of::<DynamicPropertyOwner>(),
121 "CompositionPropertySet",
122 None,
123 None,
124 )));
125 properties.insert(name.to_owned(), property);
126 property
127 }
128
129 pub fn is_dynamic(&self) -> bool {
132 self.owner == TypeId::of::<DynamicPropertyOwner>()
133 }
134}
135
136struct DynamicPropertyOwner;
138
139impl PartialEq for CompositionProperty {
140 fn eq(&self, other: &Self) -> bool {
141 self.id == other.id
142 }
143}
144
145impl Eq for CompositionProperty {}
146
147impl std::hash::Hash for CompositionProperty {
148 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
149 self.id.hash(state);
150 }
151}
152
153impl std::fmt::Debug for CompositionProperty {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 write!(f, "{}.{}#{}", self.owner_name, self.name, self.id)
156 }
157}
158
159pub struct CompositionPropertyOf<T> {
162 base: CompositionProperty,
163 get_field: fn(&dyn IServerObject) -> T,
164 set_field: fn(&dyn IServerObject, T),
165}
166
167impl<T> CompositionPropertyOf<T> {
168 pub fn base(&self) -> &CompositionProperty {
170 &self.base
171 }
172
173 pub fn get_field(&self, obj: &dyn IServerObject) -> T {
175 (self.get_field)(obj)
176 }
177
178 pub fn set_field(&self, obj: &dyn IServerObject, value: T) {
180 (self.set_field)(obj, value)
181 }
182}
183
184impl<T> std::ops::Deref for CompositionPropertyOf<T> {
185 type Target = CompositionProperty;
186
187 fn deref(&self) -> &CompositionProperty {
188 &self.base
189 }
190}
191
192pub fn props_of<P: Any>(obj: &dyn IServerObject) -> &P {
197 match obj.get_props(TypeId::of::<P>()).and_then(|props| props.downcast_ref::<P>()) {
198 Some(props) => props,
199 None => panic!("the server object does not have the properties of {}", std::any::type_name::<P>()),
200 }
201}