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 $(
81 $(#[$meta])*
82 pub fn $field(&self) -> $ty {
83 self.$field.borrow().clone()
84 }
85
86 pub fn $set(&self, value: $ty) {
87 *self.$field.borrow_mut() = value;
88 }
89
90 pub fn $with(self: ::std::rc::Rc<Self>, value: $ty) -> ::std::rc::Rc<Self> {
92 self.$set(value);
93 self
94 }
95 )*
96 }
97 };
98}
99pub(crate) use binding_properties;
100
101pub(crate) fn is_unset(value: &Option<BoxedValue>) -> bool {
103 value.as_ref().is_some_and(|v| v.is::<crate::UnsetValueType>())
104}
105
106pub(crate) fn explicit_source(source: Option<BoxedValue>) -> Option<Option<BoxedValue>> {
109 if is_unset(&source) {
110 None
111 } else {
112 Some(source)
113 }
114}
115
116binding_properties! {
117 pub struct CompiledBinding {
128 converter / set_converter / with_converter_value: Option<Rc<dyn IValueConverter>> = None,
130 converter_culture / set_converter_culture / with_converter_culture: Option<CultureInfo> = None,
133 converter_parameter / set_converter_parameter / with_converter_parameter: Option<BoxedValue> = None,
135 delay / set_delay / with_delay: i32 = 0,
138 fallback_value / set_fallback_value / with_fallback_value: Option<BoxedValue> = Some(FerroProperty::unset_value()),
141 mode / set_mode / with_mode: BindingMode = BindingMode::Default,
143 path / set_path / with_path: Option<CompiledBindingPath> = None,
145 priority / set_priority / with_priority: BindingPriority = BindingPriority::LocalValue,
147 source / set_source / with_source: Option<BoxedValue> = Some(FerroProperty::unset_value()),
150 string_format / set_string_format / with_string_format: Option<String> = None,
152 target_null_value / set_target_null_value / with_target_null_value: Option<BoxedValue> = Some(FerroProperty::unset_value()),
155 update_source_trigger / set_update_source_trigger / with_update_source_trigger: UpdateSourceTrigger = UpdateSourceTrigger::Default,
158 default_anchor / set_default_anchor / with_default_anchor: Option<WeakRef<FerroObject>> = None,
160 name_scope / set_name_scope / with_name_scope: Option<Weak<dyn INameScope>> = None,
162 }
163}
164
165impl CompiledBinding {
166 pub fn empty() -> Rc<Self> {
168 Rc::new(Self::default())
169 }
170
171 pub fn new(path: CompiledBindingPath) -> Rc<Self> {
173 Self::empty().with_path(Some(path))
174 }
175
176 pub fn with_converter(self: Rc<Self>, converter: Rc<dyn IValueConverter>) -> Rc<Self> {
179 self.with_converter_value(Some(converter))
180 }
181}
182
183impl CompiledBinding {
184 fn typed_element_for(
187 &self,
188 target: &FerroObject,
189 target_property: Option<&'static FerroProperty>,
190 ) -> Option<Rc<dyn TypedPropertyElement>> {
191 let typed = self.path.borrow().as_ref()?.single_typed_element()?.clone();
193
194 if !is_unset(&self.source.borrow()) {
196 return None;
197 }
198
199 if self.converter.borrow().is_some()
202 || !is_unset(&self.fallback_value.borrow())
203 || self.string_format.borrow().is_some()
204 || !is_unset(&self.target_null_value.borrow())
205 || !matches!(
206 self.update_source_trigger(),
207 UpdateSourceTrigger::Default | UpdateSourceTrigger::PropertyChanged
208 )
209 || self.delay() != 0
210 {
211 return None;
212 }
213
214 let target_property = target_property?;
216 let target_type = property_value_type(target_property);
217 if !crate::data::core::ValueTypes::is_assignable(typed.value_type(), target_type) {
218 return None;
219 }
220
221 if !target.is::<StyledElement>() {
224 return None;
225 }
226
227 if target_property.id() == StyledElement::data_context_property().id() {
230 return None;
231 }
232
233 if target_property.get_metadata_for(target).enable_data_validation() == Some(true) {
235 return None;
236 }
237
238 let (mode, _) =
242 resolve_defaults_from_metadata(self.mode(), self.update_source_trigger(), target, Some(target_property));
243 if matches!(mode, BindingMode::TwoWay | BindingMode::OneWayToSource)
244 && (!typed.can_set() || !crate::data::core::ValueTypes::is_assignable(target_type, typed.value_type()))
245 {
246 return None;
247 }
248
249 Some(typed)
250 }
251}
252
253impl BindingBase for CompiledBinding {
254 fn as_any(&self) -> Option<&dyn std::any::Any> {
255 Some(self)
256 }
257
258 fn create_instance(
259 &self,
260 target: &FerroObject,
261 target_property: Option<&'static FerroProperty>,
262 anchor: Option<&Ref<FerroObject>>,
263 ) -> Rc<dyn BindingExpressionBase> {
264 if let Some(typed) = self.typed_element_for(target, target_property) {
265 let (mode, _) =
268 resolve_defaults_from_metadata(self.mode(), self.update_source_trigger(), target, target_property);
269 return typed.create_expression(mode, self.priority());
270 }
271
272 let enable_data_validation =
273 target_property.is_some_and(|p| p.get_metadata_for(target).enable_data_validation() == Some(true));
274 let mut nodes: Vec<Rc<dyn ExpressionNode>> = Vec::new();
275 let path = self.path();
276 let is_rooted = path.as_ref().is_some_and(|p| p.build_expression(&mut nodes));
277 let explicit_source = explicit_source(self.source());
278
279 if explicit_source.is_none() && !is_rooted {
282 nodes.insert(0, create_data_context_node(target_property));
283 }
284
285 let target_ref = target.to_ref();
289 let default_anchor = self.default_anchor().as_ref().and_then(WeakRef::upgrade);
290 let anchor = anchor.or(default_anchor.as_ref());
291 let source = match nodes.first().and_then(|n| n.as_source_node()) {
292 Some(source_node) => source_node.select_source(explicit_source.as_ref(), &target_ref, anchor),
293 None => match &explicit_source {
294 Some(source) => source.clone(),
295 None => Some(Rc::new(target_ref.clone()) as BoxedValue),
296 },
297 };
298
299 let (mode, trigger) =
300 resolve_defaults_from_metadata(self.mode(), self.update_source_trigger(), target, target_property);
301
302 BindingExpression::new(
303 source,
304 nodes,
305 BindingExpressionOptions {
306 delay: self.delay(),
307 fallback_value: self.fallback_value(),
308 converter: self.converter(),
309 converter_culture: self.converter_culture(),
310 converter_parameter: self.converter_parameter(),
311 enable_data_validation,
312 mode,
313 priority: self.priority(),
314 string_format: self.string_format(),
315 target_null_value: self.target_null_value(),
316 target_property,
317 target_type_converter: Some(TargetTypeConverter::get_default_converter()),
318 update_source_trigger: trigger,
319 },
320 )
321 }
322}