1use crate::data::converters::IValueConverter;
2use crate::data::core::plugins::{get_property_value, set_property_value};
3use crate::data::core::{
4 impl_untyped_binding_expression, ExpressionError, Publish, TargetTypeConverter, UntypedBindingExpression,
5 UntypedBindingExpressionBase, ValueTypes,
6};
7use crate::data::{BindingMode, BindingOperations, BindingPriority};
8use crate::logging::LogEventLevel;
9use crate::reactive::IDisposable;
10use crate::utilities::CultureInfo;
11use crate::{BoxedValue, FerroObject, FerroProperty, Ref, StyledElement};
12use std::cell::{Cell, RefCell};
13use std::rc::{Rc, Weak};
14
15pub struct TemplateBindingExpression {
18 this: Weak<TemplateBindingExpression>,
19 base: UntypedBindingExpressionBase,
20 converter: Option<Rc<dyn IValueConverter>>,
21 converter_culture: Option<CultureInfo>,
22 converter_parameter: Option<BoxedValue>,
23 mode: BindingMode,
24 property: Option<&'static FerroProperty>,
25 has_published_value: Cell<bool>,
26 target_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
27 parent_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
28}
29
30impl TemplateBindingExpression {
31 pub fn new(
32 property: Option<&'static FerroProperty>,
33 converter: Option<Rc<dyn IValueConverter>>,
34 converter_culture: Option<CultureInfo>,
35 converter_parameter: Option<BoxedValue>,
36 mode: BindingMode,
37 ) -> Rc<Self> {
38 crate::perf_count!(TemplateBindingExpressionsCreated);
39 Rc::new_cyclic(|this: &Weak<TemplateBindingExpression>| Self {
40 this: this.clone(),
41 base: UntypedBindingExpressionBase::new(this.clone(), BindingPriority::Template, None, false),
42 converter,
43 converter_culture,
44 converter_parameter,
45 mode,
46 property,
47 has_published_value: Cell::new(false),
48 target_subscription: RefCell::new(None),
49 parent_subscription: RefCell::new(None),
50 })
51 }
52
53 fn try_get_templated_parent(&self) -> Option<Ref<FerroObject>> {
54 let target = self.base.try_get_target()?;
55 target.downcast_ref::<StyledElement>()?.templated_parent()
56 }
57
58 fn convert_to_target_type(&self, value: Option<BoxedValue>) -> Option<BoxedValue> {
59 let target_type = self.base.target_type();
60 if self.base.target_property().is_none() && target_type.is_object() {
61 return value;
62 }
63 match TargetTypeConverter::get_default_converter().try_convert(
64 value.as_ref(),
65 target_type,
66 &CultureInfo::invariant_culture(),
67 ) {
68 Some(result) => result,
69 None => {
70 let message = format!(
71 "Could not convert '{}' ({}) to '{}'.",
72 ValueTypes::to_display_string(value.as_ref()),
73 value.as_ref().map_or("null", |v| (**v).type_name()),
74 target_type
75 );
76 self.log_error(&message, LogEventLevel::Warning);
77 Some(FerroProperty::unset_value())
78 }
79 }
80 }
81
82 fn publish(&self) {
83 if self.mode == BindingMode::OneWayToSource {
84 return;
85 }
86
87 if let Some(templated_parent) = self.try_get_templated_parent() {
88 let mut value = match self.property {
89 Some(property) => get_property_value(&templated_parent, property),
90 None => Some(Rc::new(templated_parent.clone()) as BoxedValue),
91 };
92 let mut error: Option<ExpressionError> = None;
93 if let Some(converter) = &self.converter {
94 value = self.base.convert(
95 &|| self.should_log_error(),
96 &|| self.description(),
97 &**converter,
98 self.converter_culture.as_ref(),
99 self.converter_parameter.as_ref(),
100 value.as_ref(),
101 self.base.target_type(),
102 &mut error,
103 );
104 }
105 let value = self.convert_to_target_type(value);
106 self.base.publish_value(Publish::Value(value), error, false);
107 self.has_published_value.set(true);
108
109 if self.mode == BindingMode::OneTime {
110 self.stop();
111 }
112 } else if self.has_published_value.get() {
113 self.base.publish_value(Publish::Value(Some(FerroProperty::unset_value())), None, false);
114 }
115 }
116
117 fn unsubscribe_parent(&self) {
118 let subscription = self.parent_subscription.borrow_mut().take();
119 if let Some(s) = subscription {
120 s.dispose();
121 }
122 }
123
124 fn on_templated_parent_changed(&self) {
125 self.unsubscribe_parent();
126 if let Some(templated_parent) = self.try_get_templated_parent() {
127 let weak = self.this.clone();
128 let id = self.property.map(FerroProperty::id);
129 let subscription = templated_parent.property_changed(move |e| {
130 if Some(e.property().id()) == id {
131 if let Some(this) = weak.upgrade() {
132 this.publish();
133 }
134 }
135 });
136 self.parent_subscription.replace(Some(subscription));
137 }
138 self.publish();
139 }
140
141 fn on_target_property_changed(&self, property_id: u32) {
142 if property_id == StyledElement::templated_parent_property().id() {
143 self.on_templated_parent_changed();
144 } else if matches!(self.mode, BindingMode::TwoWay | BindingMode::OneWayToSource)
145 && self.base.target_property().is_some_and(|p| p.id() == property_id)
146 {
147 if let (Some(target), Some(property)) = (self.base.try_get_target(), self.base.target_property()) {
148 self.write_value_to_source(get_property_value(&target, property));
149 }
150 }
151 }
152}
153
154impl UntypedBindingExpression for TemplateBindingExpression {
155 fn base(&self) -> &UntypedBindingExpressionBase {
156 &self.base
157 }
158
159 fn description(&self) -> String {
160 format!("{{TemplateBinding {}}}", self.property.map_or("", |p| p.name()))
161 }
162
163 fn start_core(&self) {
164 self.has_published_value.set(false);
165 self.on_templated_parent_changed();
166 if let Some(target) = self.base.try_get_target() {
167 let weak = self.this.clone();
168 let subscription = target.property_changed(move |e| {
169 if let Some(this) = weak.upgrade() {
170 this.on_target_property_changed(e.property().id());
171 }
172 });
173 self.target_subscription.replace(Some(subscription));
174 }
175 }
176
177 fn stop_core(&self) {
178 self.unsubscribe_parent();
179 let subscription = self.target_subscription.borrow_mut().take();
180 if let Some(s) = subscription {
181 s.dispose();
182 }
183 }
184
185 fn write_value_to_source(&self, value: Option<BoxedValue>) -> bool {
186 let (Some(property), Some(templated_parent)) = (self.property, self.try_get_templated_parent()) else {
187 return false;
188 };
189 let mut value = value;
190 if let Some(converter) = &self.converter {
191 value = self.base.convert_back(
192 &|| self.should_log_error(),
193 &|| self.description(),
194 &**converter,
195 self.converter_culture.as_ref(),
196 self.converter_parameter.as_ref(),
197 value.as_ref(),
198 self.base.target_type(),
199 );
200 }
201 if !BindingOperations::is_do_nothing(value.as_ref()) {
202 let target_type = crate::data::core::plugins::property_value_type(property);
205 match ValueTypes::try_convert(value.as_ref(), target_type) {
206 Some(Some(v)) if BindingOperations::is_unset(Some(&v)) || property.is_valid_value((*v).as_any()) => {
207 templated_parent.set_current_value_untyped(property, (*v).as_any());
208 }
209 _ => {
210 if BindingOperations::is_unset(value.as_ref()) {
211 let _ = set_property_value(&templated_parent, property, value.as_ref(), BindingPriority::LocalValue);
212 }
213 }
214 }
215 }
216 true
217 }
218}
219
220impl_untyped_binding_expression!(TemplateBindingExpression);
221
222impl Drop for TemplateBindingExpression {
223 fn drop(&mut self) {
224 if let Some(s) = self.parent_subscription.get_mut().take() {
225 s.dispose();
226 }
227 if let Some(s) = self.target_subscription.get_mut().take() {
228 s.dispose();
229 }
230 }
231}