1use crate::controls::INameScope;
2use crate::data::converters::IValueConverter;
3use crate::data::core::expression_nodes::ExpressionNode;
4use crate::data::core::parsers::ExpressionNodeFactory;
5use crate::data::compiled_binding_path::TypedPropertyElement;
6use crate::data::core::plugins::property_value_type;
7use crate::data::core::{BindingExpression, BindingExpressionOptions, TargetTypeConverter};
8use crate::data::{
9 BindingBase, BindingExpressionBase, BindingMode, BindingPriority, CompiledBindingPath, UpdateSourceTrigger,
10};
11use crate::utilities::CultureInfo;
12use crate::{BoxedValue, FerroObject, FerroProperty, Ref, StyledElement, WeakRef};
13use std::rc::{Rc, Weak};
14
15pub(crate) fn resolve_defaults_from_metadata(
18 mode: BindingMode,
19 update_source_trigger: UpdateSourceTrigger,
20 target: &FerroObject,
21 target_property: Option<&'static FerroProperty>,
22) -> (BindingMode, UpdateSourceTrigger) {
23 let trigger = if update_source_trigger == UpdateSourceTrigger::Default {
24 UpdateSourceTrigger::PropertyChanged
25 } else {
26 update_source_trigger
27 };
28 let mode = if mode == BindingMode::Default {
29 match target_property {
30 Some(p) => p.get_metadata_for(target).default_binding_mode(),
31 None => BindingMode::OneWay,
32 }
33 } else {
34 mode
35 };
36 (mode, trigger)
37}
38
39pub(crate) fn create_data_context_node(target_property: Option<&'static FerroProperty>) -> Rc<dyn ExpressionNode> {
41 ExpressionNodeFactory::create_data_context(target_property)
42}
43
44
45macro_rules! binding_properties {
50 (
51 $(#[$struct_meta:meta])*
52 pub struct $name:ident {
53 $(
54 $(#[$meta:meta])*
55 $field:ident / $set:ident / $with:ident : $ty:ty = $default:expr,
56 )*
57 }
58 ) => {
59 $(#[$struct_meta])*
60 pub struct $name {
61 $($field: ::std::cell::RefCell<$ty>,)*
62 }
63
64 impl Default for $name {
65 fn default() -> Self {
66 Self { $($field: ::std::cell::RefCell::new($default),)* }
67 }
68 }
69
70 impl PartialEq for $name {
73 #[inline]
74 fn eq(&self, other: &Self) -> bool {
75 ::std::ptr::eq(self, other)
76 }
77 }
78
79 impl $name {
80 #[cfg(all(debug_assertions, feature = "hot-reload"))]
83 pub fn snapshot_for_reload(&self) -> ::std::rc::Rc<Self> {
84 ::std::rc::Rc::new(Self {
85 $($field: ::std::cell::RefCell::new(self.$field()),)*
86 })
87 }
88
89 $(
90 $(#[$meta])*
91 pub fn $field(&self) -> $ty {
92 self.$field.borrow().clone()
93 }
94
95 pub fn $set(&self, value: $ty) {
96 *self.$field.borrow_mut() = value;
97 }
98
99 pub fn $with(self: ::std::rc::Rc<Self>, value: $ty) -> ::std::rc::Rc<Self> {
101 self.$set(value);
102 self
103 }
104 )*
105 }
106 };
107}
108pub(crate) use binding_properties;
109
110pub(crate) fn is_unset(value: &Option<BoxedValue>) -> bool {
112 value.as_ref().is_some_and(|v| v.is::<crate::UnsetValueType>())
113}
114
115pub(crate) fn explicit_source(source: Option<BoxedValue>) -> Option<Option<BoxedValue>> {
118 if is_unset(&source) {
119 None
120 } else {
121 Some(source)
122 }
123}
124
125binding_properties! {
126 pub struct CompiledBinding {
137 converter / set_converter / with_converter_value: Option<Rc<dyn IValueConverter>> = None,
139 converter_culture / set_converter_culture / with_converter_culture: Option<CultureInfo> = None,
142 converter_parameter / set_converter_parameter / with_converter_parameter: Option<BoxedValue> = None,
144 delay / set_delay / with_delay: i32 = 0,
147 fallback_value / set_fallback_value / with_fallback_value: Option<BoxedValue> = Some(FerroProperty::unset_value()),
150 mode / set_mode / with_mode: BindingMode = BindingMode::Default,
152 path / set_path / with_path: Option<CompiledBindingPath> = None,
154 priority / set_priority / with_priority: BindingPriority = BindingPriority::LocalValue,
156 source / set_source / with_source: Option<BoxedValue> = Some(FerroProperty::unset_value()),
159 string_format / set_string_format / with_string_format: Option<String> = None,
161 target_null_value / set_target_null_value / with_target_null_value: Option<BoxedValue> = Some(FerroProperty::unset_value()),
164 update_source_trigger / set_update_source_trigger / with_update_source_trigger: UpdateSourceTrigger = UpdateSourceTrigger::Default,
167 default_anchor / set_default_anchor / with_default_anchor: Option<WeakRef<FerroObject>> = None,
169 name_scope / set_name_scope / with_name_scope: Option<Weak<dyn INameScope>> = None,
171 }
172}
173
174impl CompiledBinding {
175 pub fn empty() -> Rc<Self> {
177 Rc::new(Self::default())
178 }
179
180 pub fn new(path: CompiledBindingPath) -> Rc<Self> {
182 Self::empty().with_path(Some(path))
183 }
184
185 pub fn with_converter(self: Rc<Self>, converter: Rc<dyn IValueConverter>) -> Rc<Self> {
188 self.with_converter_value(Some(converter))
189 }
190}
191
192impl CompiledBinding {
193 fn typed_element_for(
196 &self,
197 target: &FerroObject,
198 target_property: Option<&'static FerroProperty>,
199 ) -> Option<Rc<dyn TypedPropertyElement>> {
200 let typed = self.path.borrow().as_ref()?.single_typed_element()?.clone();
202
203 if !is_unset(&self.source.borrow()) {
205 return None;
206 }
207
208 if self.converter.borrow().is_some()
211 || !is_unset(&self.fallback_value.borrow())
212 || self.string_format.borrow().is_some()
213 || !is_unset(&self.target_null_value.borrow())
214 || !matches!(
215 self.update_source_trigger(),
216 UpdateSourceTrigger::Default | UpdateSourceTrigger::PropertyChanged
217 )
218 || self.delay() != 0
219 {
220 return None;
221 }
222
223 let target_property = target_property?;
225 let target_type = property_value_type(target_property);
226 if !crate::data::core::ValueTypes::is_assignable(typed.value_type(), target_type) {
227 return None;
228 }
229
230 if !target.is::<StyledElement>() {
233 return None;
234 }
235
236 if target_property.id() == StyledElement::data_context_property().id() {
239 return None;
240 }
241
242 if target_property.get_metadata_for(target).enable_data_validation() == Some(true) {
244 return None;
245 }
246
247 let (mode, _) =
251 resolve_defaults_from_metadata(self.mode(), self.update_source_trigger(), target, Some(target_property));
252 if matches!(mode, BindingMode::TwoWay | BindingMode::OneWayToSource)
253 && (!typed.can_set() || !crate::data::core::ValueTypes::is_assignable(target_type, typed.value_type()))
254 {
255 return None;
256 }
257
258 Some(typed)
259 }
260}
261
262impl BindingBase for CompiledBinding {
263 fn as_any(&self) -> Option<&dyn std::any::Any> {
264 Some(self)
265 }
266
267 fn create_instance(
268 &self,
269 target: &FerroObject,
270 target_property: Option<&'static FerroProperty>,
271 anchor: Option<&Ref<FerroObject>>,
272 ) -> Rc<dyn BindingExpressionBase> {
273 if let Some(typed) = self.typed_element_for(target, target_property) {
274 let (mode, _) =
277 resolve_defaults_from_metadata(self.mode(), self.update_source_trigger(), target, target_property);
278 return typed.create_expression(mode, self.priority());
279 }
280
281 let enable_data_validation =
282 target_property.is_some_and(|p| p.get_metadata_for(target).enable_data_validation() == Some(true));
283 let mut nodes: Vec<Rc<dyn ExpressionNode>> = Vec::new();
284 let path = self.path();
285 let is_rooted = path.as_ref().is_some_and(|p| p.build_expression(&mut nodes));
286 let explicit_source = explicit_source(self.source());
287
288 if explicit_source.is_none() && !is_rooted {
291 nodes.insert(0, create_data_context_node(target_property));
292 }
293
294 let target_ref = target.to_ref();
298 let default_anchor = self.default_anchor().as_ref().and_then(WeakRef::upgrade);
299 let anchor = anchor.or(default_anchor.as_ref());
300 let source = match nodes.first().and_then(|n| n.as_source_node()) {
301 Some(source_node) => source_node.select_source(explicit_source.as_ref(), &target_ref, anchor),
302 None => match &explicit_source {
303 Some(source) => source.clone(),
304 None => Some(Rc::new(target_ref.clone()) as BoxedValue),
305 },
306 };
307
308 let (mode, trigger) =
309 resolve_defaults_from_metadata(self.mode(), self.update_source_trigger(), target, target_property);
310
311 BindingExpression::new(
312 source,
313 nodes,
314 BindingExpressionOptions {
315 delay: self.delay(),
316 fallback_value: self.fallback_value(),
317 converter: self.converter(),
318 converter_culture: self.converter_culture(),
319 converter_parameter: self.converter_parameter(),
320 enable_data_validation,
321 mode,
322 priority: self.priority(),
323 string_format: self.string_format(),
324 target_null_value: self.target_null_value(),
325 target_property,
326 target_type_converter: Some(TargetTypeConverter::get_default_converter()),
327 update_source_trigger: trigger,
328 },
329 )
330 }
331}