Skip to main content

ferroui_base/styling/
setter.rs

1use super::{
2    DirectPropertySetterBindingInstance, DirectPropertySetterInstance, IStyleInstance, ISetterInstance,
3    ISetterValue, ITemplate, PropertySetterTemplateInstance, SetterBase, SetterInstance, SetterInstanceKind,
4    StyleInstance,
5};
6use crate::data::core::SinkRef;
7use crate::data::{BindingBase, BindingPriority};
8use crate::property_store::{IValueEntry, ValueFrame};
9use crate::reactive::IObservable;
10use crate::{AnyValue, BoxedValue, FerroProperty, PropertyValue, StyledElement, StyledProperty};
11use std::any::Any;
12use std::cell::{Cell, OnceCell, RefCell};
13use std::fmt;
14use std::rc::Rc;
15
16/// The value of a [`Setter`].
17#[derive(Clone)]
18pub enum SetterValue {
19    /// A plain value, which must hold exactly the value type of the setter's
20    /// property.
21    Value(BoxedValue),
22    /// A source of values: the setter binds the property to it on every
23    /// control the style is applied to.
24    ///
25    /// This is the seam for binding descriptions: a binding class will
26    /// create its per-control source where this variant uses the same
27    /// observable for every control.
28    Binding(Rc<dyn IObservable<BoxedValue>>),
29    /// A binding description: the setter instantiates the binding on every
30    /// control the style is applied to.
31    BindingBase(Rc<dyn BindingBase>),
32    /// A template: the setter sets the property to the object the template
33    /// builds, building it lazily once per control.
34    Template(Rc<dyn ITemplate>),
35}
36
37/// A setter for a [`Style`](super::Style).
38///
39/// A setter describes what will take place when the requirements for a style
40/// are met: it sets a property to a value, binds it to a source of values or
41/// sets it to the result of a template.
42pub struct Setter {
43    property: Cell<Option<&'static FerroProperty>>,
44    value: RefCell<Option<SetterValue>>,
45    direct: OnceCell<Rc<DirectPropertySetterInstance>>,
46}
47
48
49/// Compares by identity (reference equality), as the reference type this
50/// mirrors.
51impl PartialEq for Setter {
52    #[inline]
53    fn eq(&self, other: &Self) -> bool {
54        std::ptr::eq(self, other)
55    }
56}
57
58impl Default for Setter {
59    fn default() -> Self {
60        Self { property: Cell::new(None), value: RefCell::new(None), direct: OnceCell::new() }
61    }
62}
63
64impl Setter {
65    /// Creates a setter without a property or value.
66    pub fn empty() -> Rc<Self> {
67        Rc::new(Self::default())
68    }
69
70    /// Creates a setter that sets a styled property to a value.
71    pub fn new<T: PropertyValue>(property: &'static StyledProperty<T>, value: T) -> Rc<Self> {
72        Self::with_value(property, SetterValue::Value(Rc::new(value)))
73    }
74
75    /// Creates a setter that sets a property to an untyped value, which must
76    /// hold exactly the property's value type.
77    pub fn new_untyped(property: &'static FerroProperty, value: BoxedValue) -> Rc<Self> {
78        Self::with_value(property, SetterValue::Value(value))
79    }
80
81    /// Creates a setter that binds a property to a source of values.
82    pub fn new_binding(property: &'static FerroProperty, source: Rc<dyn IObservable<BoxedValue>>) -> Rc<Self> {
83        Self::with_value(property, SetterValue::Binding(source))
84    }
85
86    /// Creates a setter that binds a property with a binding (a compiled,
87    /// string-path, template or multi binding).
88    pub fn new_binding_base(property: &'static FerroProperty, binding: Rc<dyn BindingBase>) -> Rc<Self> {
89        Self::with_value(property, SetterValue::BindingBase(binding))
90    }
91
92    /// Creates a setter that sets a property to the object built by a
93    /// template.
94    pub fn new_template(property: &'static FerroProperty, template: Rc<dyn ITemplate>) -> Rc<Self> {
95        Self::with_value(property, SetterValue::Template(template))
96    }
97
98    /// Creates a setter for a property with the given kind of value.
99    pub fn with_value(property: &'static FerroProperty, value: SetterValue) -> Rc<Self> {
100        let setter = Self::default();
101        setter.property.set(Some(property));
102        *setter.value.borrow_mut() = Some(value);
103        Rc::new(setter)
104    }
105
106    /// The property to set.
107    pub fn property(&self) -> Option<&'static FerroProperty> {
108        self.property.get()
109    }
110
111    pub fn set_property(&self, value: Option<&'static FerroProperty>) {
112        self.property.set(value)
113    }
114
115    /// The property value.
116    pub fn value(&self) -> Option<SetterValue> {
117        self.value.borrow().clone()
118    }
119
120    pub fn set_value(&self, value: Option<SetterValue>) {
121        *self.value.borrow_mut() = value;
122    }
123
124    /// Sets a plain value that wants to be notified that it has become a
125    /// setter value. `boxed` is the value as it will be set on the property.
126    pub fn set_setter_value(&self, value: &dyn ISetterValue, boxed: BoxedValue) {
127        value.initialize(self);
128        *self.value.borrow_mut() = Some(SetterValue::Value(boxed));
129    }
130
131    fn ensure_property(&self) -> &'static FerroProperty {
132        match self.property.get() {
133            Some(property) => property,
134            None => panic!("Setter.Property must be set."),
135        }
136    }
137
138    fn plain_value(&self) -> Option<BoxedValue> {
139        match &*self.value.borrow() {
140            Some(SetterValue::Value(value)) => Some(value.clone()),
141            // A setter without a value sets null, where the property takes it.
142            None => self.property.get().and_then(Self::null_value),
143            _ => None,
144        }
145    }
146
147    /// The null of the value type of `property` (`None` of an optional
148    /// type), if the type has one: what a setter whose value is null sets.
149    fn null_value(property: &'static FerroProperty) -> Option<BoxedValue> {
150        crate::data::core::ValueTypes::null_value(crate::data::core::ValueType::new(
151            property.property_type(),
152            property.property_type_name(),
153        ))
154    }
155
156    fn set_binding(
157        &self,
158        property: &'static FerroProperty,
159        instance: &Rc<StyleInstance>,
160        target: &StyledElement,
161        source: Rc<dyn IObservable<BoxedValue>>,
162    ) -> SetterInstance {
163        if !property.is_direct() {
164            let frame: Rc<dyn ValueFrame> = instance.clone();
165            let entry = property.routes().route_create_binding_entry(target, Rc::downgrade(&frame), source);
166            SetterInstance { kind: SetterInstanceKind::Entry(entry), is_setter: false }
167        } else {
168            target.bind_property_untyped(property, source, BindingPriority::LocalValue);
169            SetterInstance {
170                kind: SetterInstanceKind::Other(Rc::new(DirectPropertySetterBindingInstance)),
171                is_setter: false,
172            }
173        }
174    }
175
176    fn set_binding_base(
177        &self,
178        property: &'static FerroProperty,
179        instance: &Rc<StyleInstance>,
180        target: &StyledElement,
181        binding: Rc<dyn BindingBase>,
182    ) -> SetterInstance {
183        if !property.is_direct() {
184            // The expression is an entry of the style instance's frame: it is
185            // started when the frame's values are evaluated and stopped when
186            // the style is deactivated or detached.
187            let expression = binding.create_instance(target, Some(property), None);
188            let owner: &crate::FerroObject = target;
189            let frame: &dyn ValueFrame = &**instance;
190            expression.attach(SinkRef::Store(owner.to_weak()), None, owner, property, frame.base().priority());
191            let entry: Rc<dyn IValueEntry> = expression;
192            SetterInstance { kind: SetterInstanceKind::Entry(entry), is_setter: false }
193        } else {
194            target.bind_binding(property, &*binding);
195            SetterInstance {
196                kind: SetterInstanceKind::Other(Rc::new(DirectPropertySetterBindingInstance)),
197                is_setter: false,
198            }
199        }
200    }
201
202    fn set_direct_value(&self, property: &'static FerroProperty, target: &StyledElement, value: &BoxedValue) -> SetterInstance {
203        let value: &dyn AnyValue = &**value;
204        target.set_value_untyped(property, value.as_any(), BindingPriority::LocalValue);
205        let direct: Rc<dyn ISetterInstance> =
206            self.direct.get_or_init(|| Rc::new(DirectPropertySetterInstance)).clone();
207        // The value is set on the control itself, so the style instance
208        // cannot be shared: every control needs its own attach.
209        SetterInstance { kind: SetterInstanceKind::Other(direct), is_setter: false }
210    }
211}
212
213impl ISetterInstance for Setter {}
214
215impl SetterBase for Setter {
216    fn as_any(&self) -> Option<&dyn Any> {
217        Some(self)
218    }
219
220    fn instance(self: Rc<Self>, instance: &Rc<StyleInstance>, target: &StyledElement) -> SetterInstance {
221        let Some(property) = self.property.get() else {
222            panic!("Setter.Property must be set.");
223        };
224
225        if property.is_direct() && instance.has_activator() {
226            panic!(
227                "Cannot set direct property '{}' in '{}' because the style has an activator.",
228                property,
229                instance.source_display()
230            );
231        }
232
233        if instance.has_activator() {
234            if let Some(class_property_name) = crate::ClassBindingManager::is_classes_binding_property(property) {
235                panic!(
236                    "Cannot set Class Binding property '(Classes.{})' in '{}' because the style has an activator.",
237                    class_property_name,
238                    instance.source_display()
239                );
240            }
241        }
242
243        let value = self.value.borrow().clone();
244        match value {
245            Some(SetterValue::Binding(source)) => self.set_binding(property, instance, target, source),
246            Some(SetterValue::BindingBase(binding)) => self.set_binding_base(property, instance, target, binding),
247            Some(SetterValue::Template(template)) => SetterInstance {
248                kind: SetterInstanceKind::Entry(Rc::new(PropertySetterTemplateInstance::new(property, template))),
249                is_setter: false,
250            },
251            Some(SetterValue::Value(value)) => {
252                let inner: &dyn AnyValue = &*value;
253                if !property.is_valid_value(inner.as_any()) {
254                    panic!("Setter value '{:?}' is not a valid value for property '{}'.", inner, property);
255                }
256                if property.is_direct() {
257                    self.set_direct_value(property, target, &value)
258                } else {
259                    SetterInstance { kind: SetterInstanceKind::Entry(self), is_setter: true }
260                }
261            }
262            // Null is a value of a property whose type admits it (a reference or
263            // nullable type in the managed original); of a value type it is not.
264            None => match Self::null_value(property) {
265                Some(null) if property.is_direct() => self.set_direct_value(property, target, &null),
266                Some(_) => SetterInstance { kind: SetterInstanceKind::Entry(self), is_setter: true },
267                None => panic!("Setter value '(null)' is not a valid value for property '{}'.", property),
268            },
269        }
270    }
271}
272
273impl IValueEntry for Setter {
274    fn property(&self) -> &'static FerroProperty {
275        self.ensure_property()
276    }
277
278    fn has_value(&self) -> bool {
279        true
280    }
281
282    fn try_get_value(&self, out: &mut dyn Any) -> bool {
283        match self.plain_value() {
284            Some(value) => {
285                let value: &dyn AnyValue = &*value;
286                self.ensure_property().routes().route_copy_value(value.as_any(), out)
287            }
288            None => false,
289        }
290    }
291
292    fn get_value_boxed(&self) -> Option<BoxedValue> {
293        self.plain_value()
294    }
295
296    fn unsubscribe(&self) {}
297}
298
299impl fmt::Display for Setter {
300    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301        let property = self.property.get().map(|p| p.name().to_string()).unwrap_or_default();
302        match &*self.value.borrow() {
303            Some(SetterValue::Value(value)) => write!(f, "Setter: {property} = {:?}", &**value),
304            Some(SetterValue::Binding(_)) | Some(SetterValue::BindingBase(_)) => {
305                write!(f, "Setter: {property} = (binding)")
306            }
307            Some(SetterValue::Template(_)) => write!(f, "Setter: {property} = (template)"),
308            None => write!(f, "Setter: {property} = "),
309        }
310    }
311}