ferroui_base/
ferro_property_metadata.rs1use crate::data::BindingMode;
2use crate::{FerroObject, TypeInfo};
3use rustc_hash::FxHashMap;
4use std::cell::RefCell;
5use std::rc::Rc;
6
7#[derive(Clone, Debug)]
9pub struct FerroPropertyMetadata {
10 default_binding_mode: BindingMode,
11 enable_data_validation: Option<bool>,
12 is_read_only: bool,
13}
14
15impl FerroPropertyMetadata {
16 pub fn new(default_binding_mode: BindingMode, enable_data_validation: Option<bool>) -> Self {
17 Self { default_binding_mode, enable_data_validation, is_read_only: false }
18 }
19
20 pub fn default_binding_mode(&self) -> BindingMode {
22 if self.default_binding_mode == BindingMode::Default {
23 BindingMode::OneWay
24 } else {
25 self.default_binding_mode
26 }
27 }
28
29 pub fn enable_data_validation(&self) -> Option<bool> {
35 self.enable_data_validation
36 }
37
38 pub fn is_read_only(&self) -> bool {
40 self.is_read_only
41 }
42
43 pub fn merge(&mut self, base: &FerroPropertyMetadata) {
45 assert!(!self.is_read_only, "The metadata is read-only.");
46 if self.default_binding_mode == BindingMode::Default {
47 self.default_binding_mode = base.default_binding_mode();
48 }
49 if self.enable_data_validation.is_none() {
50 self.enable_data_validation = base.enable_data_validation;
51 }
52 }
53
54 pub fn freeze(&mut self) {
56 self.is_read_only = true;
57 }
58}
59
60pub type CoerceValueCallback<T> = Rc<dyn Fn(&FerroObject, T) -> T>;
62
63pub struct StyledPropertyMetadata<T> {
65 base: FerroPropertyMetadata,
66 default_value: Option<T>,
67 coerce: Option<CoerceValueCallback<T>>,
68}
69
70impl<T: Clone> Clone for StyledPropertyMetadata<T> {
71 fn clone(&self) -> Self {
72 Self { base: self.base.clone(), default_value: self.default_value.clone(), coerce: self.coerce.clone() }
73 }
74}
75
76impl<T> std::ops::Deref for StyledPropertyMetadata<T> {
77 type Target = FerroPropertyMetadata;
78 fn deref(&self) -> &FerroPropertyMetadata {
79 &self.base
80 }
81}
82
83impl<T: Clone + 'static> StyledPropertyMetadata<T> {
84 pub fn new(default_value: Option<T>) -> Self {
87 Self {
88 base: FerroPropertyMetadata::new(BindingMode::Default, Some(false)),
89 default_value,
90 coerce: None,
91 }
92 }
93
94 pub fn with_default_binding_mode(mut self, mode: BindingMode) -> Self {
95 self.base.default_binding_mode = mode;
96 self
97 }
98
99 pub fn with_coerce(mut self, coerce: impl Fn(&FerroObject, T) -> T + 'static) -> Self {
100 self.coerce = Some(Rc::new(coerce));
101 self
102 }
103
104 pub fn with_enable_data_validation(mut self, enable: bool) -> Self {
105 self.base.enable_data_validation = Some(enable);
106 self
107 }
108
109 #[inline]
112 pub fn default_value(&self) -> &T {
113 self.default_value.as_ref().expect("metadata has no default value")
114 }
115
116 pub(crate) fn has_default_value(&self) -> bool {
117 self.default_value.is_some()
118 }
119
120 #[inline]
122 pub fn coerce_value(&self) -> Option<&CoerceValueCallback<T>> {
123 self.coerce.as_ref()
124 }
125}
126
127#[derive(Clone)]
129pub struct DirectPropertyMetadata<T> {
130 base: FerroPropertyMetadata,
131 unset_value: Option<T>,
132}
133
134impl<T> std::ops::Deref for DirectPropertyMetadata<T> {
135 type Target = FerroPropertyMetadata;
136 fn deref(&self) -> &FerroPropertyMetadata {
137 &self.base
138 }
139}
140
141impl<T: Clone + 'static> DirectPropertyMetadata<T> {
142 pub fn new(unset_value: Option<T>) -> Self {
143 Self { base: FerroPropertyMetadata::new(BindingMode::Default, None), unset_value }
144 }
145
146 pub fn with_default_binding_mode(mut self, mode: BindingMode) -> Self {
147 self.base.default_binding_mode = mode;
148 self
149 }
150
151 pub fn with_enable_data_validation(mut self, enable: bool) -> Self {
152 self.base.enable_data_validation = Some(enable);
153 self
154 }
155
156 #[inline]
158 pub fn unset_value(&self) -> &T {
159 self.unset_value.as_ref().expect("metadata has no unset value")
160 }
161}
162
163pub trait PropertyMetadata: Sized + 'static {
166 fn base(&self) -> &FerroPropertyMetadata;
167 fn merge(&mut self, base: &Self);
168 fn freeze(&mut self);
169 fn generate_type_safe_metadata(this: &Rc<Self>) -> Rc<Self>;
172}
173
174impl<T: Clone + 'static> PropertyMetadata for StyledPropertyMetadata<T> {
175 fn base(&self) -> &FerroPropertyMetadata {
176 &self.base
177 }
178
179 fn merge(&mut self, base: &Self) {
180 self.base.merge(&base.base);
181 if self.default_value.is_none() {
182 self.default_value = base.default_value.clone();
183 }
184 if self.coerce.is_none() {
185 self.coerce = base.coerce.clone();
186 }
187 }
188
189 fn freeze(&mut self) {
190 self.base.freeze();
191 }
192
193 fn generate_type_safe_metadata(this: &Rc<Self>) -> Rc<Self> {
194 if this.is_read_only() && this.coerce.is_none() {
195 return this.clone();
196 }
197 let mut copy = StyledPropertyMetadata::new(this.default_value.clone())
198 .with_default_binding_mode(this.default_binding_mode())
199 .with_enable_data_validation(this.enable_data_validation().unwrap_or(false));
200 copy.freeze();
201 Rc::new(copy)
202 }
203}
204
205impl<T: Clone + 'static> PropertyMetadata for DirectPropertyMetadata<T> {
206 fn base(&self) -> &FerroPropertyMetadata {
207 &self.base
208 }
209
210 fn merge(&mut self, base: &Self) {
211 self.base.merge(&base.base);
212 if self.unset_value.is_none() {
213 self.unset_value = base.unset_value.clone();
214 }
215 }
216
217 fn freeze(&mut self) {
218 self.base.freeze();
219 }
220
221 fn generate_type_safe_metadata(this: &Rc<Self>) -> Rc<Self> {
222 this.clone()
223 }
224}
225
226pub(crate) struct MetadataTable<M> {
230 default_metadata: Rc<M>,
231 inner: RefCell<TableInner<M>>,
232}
233
234struct TableInner<M> {
235 single: Option<(&'static TypeInfo, Rc<M>)>,
237 metadata: Vec<(&'static TypeInfo, Rc<M>)>,
238 cache: FxHashMap<usize, Rc<M>>,
244}
245
246impl<M: PropertyMetadata> MetadataTable<M> {
247 pub fn new(host_type: &'static TypeInfo, mut metadata: M) -> Self {
248 metadata.freeze();
249 let metadata = Rc::new(metadata);
250 let default_metadata = M::generate_type_safe_metadata(&metadata);
251 Self {
252 default_metadata,
253 inner: RefCell::new(TableInner {
254 single: Some((host_type, metadata.clone())),
255 metadata: vec![(host_type, metadata)],
256 cache: FxHashMap::default(),
257 }),
258 }
259 }
260
261 pub fn for_added_owner(source: &Self, owner_type: &'static TypeInfo, metadata: Option<M>) -> Self {
264 let mut entries = Vec::new();
265 if let Some(m) = metadata {
266 entries.push((owner_type, Rc::new(m)));
267 }
268 Self {
269 default_metadata: source.default_metadata.clone(),
270 inner: RefCell::new(TableInner { single: None, metadata: entries, cache: FxHashMap::default() }),
271 }
272 }
273
274 #[inline]
275 pub fn default_metadata(&self) -> &Rc<M> {
276 &self.default_metadata
277 }
278
279 pub fn get(&self, type_: &'static TypeInfo) -> Rc<M> {
280 let mut inner = self.inner.borrow_mut();
281 if let Some((host, single)) = &inner.single {
282 if Rc::ptr_eq(single, &self.default_metadata) {
283 return self.default_metadata.clone();
284 }
285 return if host.is_assignable_from(type_) { single.clone() } else { self.default_metadata.clone() };
286 }
287 let key = type_ as *const TypeInfo as usize;
288 if let Some(m) = inner.cache.get(&key) {
289 return m.clone();
290 }
291 let mut current = Some(type_);
292 let mut result = None;
293 while let Some(t) = current {
294 if let Some((_, m)) = inner.metadata.iter().find(|(mt, _)| std::ptr::eq(*mt, t)) {
295 result = Some(m.clone());
296 break;
297 }
298 current = t.base_type();
299 }
300 let result = result.unwrap_or_else(|| self.default_metadata.clone());
301 inner.cache.insert(key, result.clone());
302 result
303 }
304
305 pub fn override_metadata(&self, property_name: &str, type_: &'static TypeInfo, mut metadata: M) {
306 let already_set = self.inner.borrow().metadata.iter().any(|(t, _)| std::ptr::eq(*t, type_));
307 if already_set {
308 panic!("Metadata is already set for {property_name} on {type_}.");
309 }
310 let base = self.get(type_);
311 metadata.merge(&base);
312 metadata.freeze();
313 let mut inner = self.inner.borrow_mut();
314 inner.metadata.push((type_, Rc::new(metadata)));
315 inner.cache.clear();
316 inner.single = None;
317 }
318
319 pub fn unregister(&self, type_: &'static TypeInfo) {
320 let mut inner = self.inner.borrow_mut();
321 inner.metadata.retain(|(t, _)| !std::ptr::eq(*t, type_));
322 inner.cache.remove(&(type_ as *const TypeInfo as usize));
323 inner.single = None;
324 }
325}