Skip to main content

ferroui_base/
ferro_property_metadata.rs

1use crate::data::BindingMode;
2use crate::{FerroObject, TypeInfo};
3use std::cell::RefCell;
4use std::rc::Rc;
5
6/// Metadata common to all property kinds.
7#[derive(Clone, Debug)]
8pub struct FerroPropertyMetadata {
9    default_binding_mode: BindingMode,
10    enable_data_validation: Option<bool>,
11    is_read_only: bool,
12}
13
14impl FerroPropertyMetadata {
15    pub fn new(default_binding_mode: BindingMode, enable_data_validation: Option<bool>) -> Self {
16        Self { default_binding_mode, enable_data_validation, is_read_only: false }
17    }
18
19    /// The default binding mode for the property.
20    pub fn default_binding_mode(&self) -> BindingMode {
21        if self.default_binding_mode == BindingMode::Default {
22            BindingMode::OneWay
23        } else {
24            self.default_binding_mode
25        }
26    }
27
28    /// Whether the property is interested in data validation.
29    ///
30    /// Data validation is validation performed at the target of a binding, for
31    /// example in a view model. To enable it on a binding, the property must
32    /// also enable it here.
33    pub fn enable_data_validation(&self) -> Option<bool> {
34        self.enable_data_validation
35    }
36
37    /// Whether the metadata has been frozen.
38    pub fn is_read_only(&self) -> bool {
39        self.is_read_only
40    }
41
42    /// Merges unset members from the base metadata.
43    pub fn merge(&mut self, base: &FerroPropertyMetadata) {
44        assert!(!self.is_read_only, "The metadata is read-only.");
45        if self.default_binding_mode == BindingMode::Default {
46            self.default_binding_mode = base.default_binding_mode();
47        }
48        if self.enable_data_validation.is_none() {
49            self.enable_data_validation = base.enable_data_validation;
50        }
51    }
52
53    /// Makes the metadata read-only.
54    pub fn freeze(&mut self) {
55        self.is_read_only = true;
56    }
57}
58
59/// The coercion callback of a styled property.
60pub type CoerceValueCallback<T> = Rc<dyn Fn(&FerroObject, T) -> T>;
61
62/// Metadata for styled properties.
63pub struct StyledPropertyMetadata<T> {
64    base: FerroPropertyMetadata,
65    default_value: Option<T>,
66    coerce: Option<CoerceValueCallback<T>>,
67}
68
69impl<T: Clone> Clone for StyledPropertyMetadata<T> {
70    fn clone(&self) -> Self {
71        Self { base: self.base.clone(), default_value: self.default_value.clone(), coerce: self.coerce.clone() }
72    }
73}
74
75impl<T> std::ops::Deref for StyledPropertyMetadata<T> {
76    type Target = FerroPropertyMetadata;
77    fn deref(&self) -> &FerroPropertyMetadata {
78        &self.base
79    }
80}
81
82impl<T: Clone + 'static> StyledPropertyMetadata<T> {
83    /// Creates metadata. A `default_value` of `None` inherits the default
84    /// value of the base metadata when merged.
85    pub fn new(default_value: Option<T>) -> Self {
86        Self {
87            base: FerroPropertyMetadata::new(BindingMode::Default, Some(false)),
88            default_value,
89            coerce: None,
90        }
91    }
92
93    pub fn with_default_binding_mode(mut self, mode: BindingMode) -> Self {
94        self.base.default_binding_mode = mode;
95        self
96    }
97
98    pub fn with_coerce(mut self, coerce: impl Fn(&FerroObject, T) -> T + 'static) -> Self {
99        self.coerce = Some(Rc::new(coerce));
100        self
101    }
102
103    pub fn with_enable_data_validation(mut self, enable: bool) -> Self {
104        self.base.enable_data_validation = Some(enable);
105        self
106    }
107
108    /// The default value of the property. Panics if the metadata has not been
109    /// merged with a base that provides one.
110    #[inline]
111    pub fn default_value(&self) -> &T {
112        self.default_value.as_ref().expect("metadata has no default value")
113    }
114
115    pub(crate) fn has_default_value(&self) -> bool {
116        self.default_value.is_some()
117    }
118
119    /// The value coercion callback, if any.
120    #[inline]
121    pub fn coerce_value(&self) -> Option<&CoerceValueCallback<T>> {
122        self.coerce.as_ref()
123    }
124}
125
126/// Metadata for direct properties.
127#[derive(Clone)]
128pub struct DirectPropertyMetadata<T> {
129    base: FerroPropertyMetadata,
130    unset_value: Option<T>,
131}
132
133impl<T> std::ops::Deref for DirectPropertyMetadata<T> {
134    type Target = FerroPropertyMetadata;
135    fn deref(&self) -> &FerroPropertyMetadata {
136        &self.base
137    }
138}
139
140impl<T: Clone + 'static> DirectPropertyMetadata<T> {
141    pub fn new(unset_value: Option<T>) -> Self {
142        Self { base: FerroPropertyMetadata::new(BindingMode::Default, None), unset_value }
143    }
144
145    pub fn with_default_binding_mode(mut self, mode: BindingMode) -> Self {
146        self.base.default_binding_mode = mode;
147        self
148    }
149
150    pub fn with_enable_data_validation(mut self, enable: bool) -> Self {
151        self.base.enable_data_validation = Some(enable);
152        self
153    }
154
155    /// The value to use when the property is set to the unset value.
156    #[inline]
157    pub fn unset_value(&self) -> &T {
158        self.unset_value.as_ref().expect("metadata has no unset value")
159    }
160}
161
162/// Behaviour shared by the typed metadata kinds so that [`MetadataTable`] can
163/// manage them generically.
164pub trait PropertyMetadata: Sized + 'static {
165    fn base(&self) -> &FerroPropertyMetadata;
166    fn merge(&mut self, base: &Self);
167    fn freeze(&mut self);
168    /// Returns metadata that is safe to use for any host type: a copy without
169    /// host-specific callbacks.
170    fn generate_type_safe_metadata(this: &Rc<Self>) -> Rc<Self>;
171}
172
173impl<T: Clone + 'static> PropertyMetadata for StyledPropertyMetadata<T> {
174    fn base(&self) -> &FerroPropertyMetadata {
175        &self.base
176    }
177
178    fn merge(&mut self, base: &Self) {
179        self.base.merge(&base.base);
180        if self.default_value.is_none() {
181            self.default_value = base.default_value.clone();
182        }
183        if self.coerce.is_none() {
184            self.coerce = base.coerce.clone();
185        }
186    }
187
188    fn freeze(&mut self) {
189        self.base.freeze();
190    }
191
192    fn generate_type_safe_metadata(this: &Rc<Self>) -> Rc<Self> {
193        if this.is_read_only() && this.coerce.is_none() {
194            return this.clone();
195        }
196        let mut copy = StyledPropertyMetadata::new(this.default_value.clone())
197            .with_default_binding_mode(this.default_binding_mode())
198            .with_enable_data_validation(this.enable_data_validation().unwrap_or(false));
199        copy.freeze();
200        Rc::new(copy)
201    }
202}
203
204impl<T: Clone + 'static> PropertyMetadata for DirectPropertyMetadata<T> {
205    fn base(&self) -> &FerroPropertyMetadata {
206        &self.base
207    }
208
209    fn merge(&mut self, base: &Self) {
210        self.base.merge(&base.base);
211        if self.unset_value.is_none() {
212            self.unset_value = base.unset_value.clone();
213        }
214    }
215
216    fn freeze(&mut self) {
217        self.base.freeze();
218    }
219
220    fn generate_type_safe_metadata(this: &Rc<Self>) -> Rc<Self> {
221        this.clone()
222    }
223}
224
225/// Per-host-type metadata storage of a property, with the lookup rules of the
226/// property system: the metadata registered for the nearest base class of the
227/// queried type wins, falling back to the type-safe default metadata.
228pub(crate) struct MetadataTable<M> {
229    default_metadata: Rc<M>,
230    inner: RefCell<TableInner<M>>,
231}
232
233struct TableInner<M> {
234    /// Fast path while only the registration metadata exists.
235    single: Option<(&'static TypeInfo, Rc<M>)>,
236    metadata: Vec<(&'static TypeInfo, Rc<M>)>,
237    cache: Vec<(&'static TypeInfo, Rc<M>)>,
238}
239
240impl<M: PropertyMetadata> MetadataTable<M> {
241    pub fn new(host_type: &'static TypeInfo, mut metadata: M) -> Self {
242        metadata.freeze();
243        let metadata = Rc::new(metadata);
244        let default_metadata = M::generate_type_safe_metadata(&metadata);
245        Self {
246            default_metadata,
247            inner: RefCell::new(TableInner {
248                single: Some((host_type, metadata.clone())),
249                metadata: vec![(host_type, metadata)],
250                cache: Vec::new(),
251            }),
252        }
253    }
254
255    /// A table for a property that shares its identity with `source` (added
256    /// owner) and optionally carries metadata for the new owner.
257    pub fn for_added_owner(source: &Self, owner_type: &'static TypeInfo, metadata: Option<M>) -> Self {
258        let mut entries = Vec::new();
259        if let Some(m) = metadata {
260            entries.push((owner_type, Rc::new(m)));
261        }
262        Self {
263            default_metadata: source.default_metadata.clone(),
264            inner: RefCell::new(TableInner { single: None, metadata: entries, cache: Vec::new() }),
265        }
266    }
267
268    #[inline]
269    pub fn default_metadata(&self) -> &Rc<M> {
270        &self.default_metadata
271    }
272
273    pub fn get(&self, type_: &'static TypeInfo) -> Rc<M> {
274        let mut inner = self.inner.borrow_mut();
275        if let Some((host, single)) = &inner.single {
276            if Rc::ptr_eq(single, &self.default_metadata) {
277                return self.default_metadata.clone();
278            }
279            return if host.is_assignable_from(type_) { single.clone() } else { self.default_metadata.clone() };
280        }
281        if let Some((_, m)) = inner.cache.iter().find(|(t, _)| std::ptr::eq(*t, type_)) {
282            return m.clone();
283        }
284        let mut current = Some(type_);
285        let mut result = None;
286        while let Some(t) = current {
287            if let Some((_, m)) = inner.metadata.iter().find(|(mt, _)| std::ptr::eq(*mt, t)) {
288                result = Some(m.clone());
289                break;
290            }
291            current = t.base_type();
292        }
293        let result = result.unwrap_or_else(|| self.default_metadata.clone());
294        inner.cache.push((type_, result.clone()));
295        result
296    }
297
298    pub fn override_metadata(&self, property_name: &str, type_: &'static TypeInfo, mut metadata: M) {
299        let already_set = self.inner.borrow().metadata.iter().any(|(t, _)| std::ptr::eq(*t, type_));
300        if already_set {
301            panic!("Metadata is already set for {property_name} on {type_}.");
302        }
303        let base = self.get(type_);
304        metadata.merge(&base);
305        metadata.freeze();
306        let mut inner = self.inner.borrow_mut();
307        inner.metadata.push((type_, Rc::new(metadata)));
308        inner.cache.clear();
309        inner.single = None;
310    }
311
312    pub fn unregister(&self, type_: &'static TypeInfo) {
313        let mut inner = self.inner.borrow_mut();
314        inner.metadata.retain(|(t, _)| !std::ptr::eq(*t, type_));
315        inner.cache.retain(|(t, _)| !std::ptr::eq(*t, type_));
316        inner.single = None;
317    }
318}