Skip to main content

ferroui_base/data/core/
binding_expression.rs

1use super::expression_nodes::{ExpressionNode, NodeSource};
2use super::{
3    impl_untyped_binding_expression, ExpressionError, ObservableSink, Publish, TargetTypeConverter,
4    UntypedBindingExpression, UntypedBindingExpressionBase, ValueType, ValueTypes, WeakValue,
5};
6use crate::data::converters::{composite_format, IValueConverter};
7use crate::data::{
8    BindingChainException, BindingError, BindingErrorType, BindingMode, BindingNotification, BindingOperations,
9    BindingPriority, UpdateSourceTrigger,
10};
11use crate::logging::{LogArea, LogEventLevel, Logger};
12use crate::input::InputElement;
13use crate::interactivity::RoutedEventHandlerToken;
14use crate::reactive::IDisposable;
15use crate::threading::{Dispatcher, DispatcherPriority, DispatcherTimer};
16use crate::utilities::CultureInfo;
17use crate::{BoxedValue, FerroObject, FerroProperty, Ref, UnsetValueType};
18use std::any::Any;
19use std::cell::{Cell, RefCell};
20use std::rc::{Rc, Weak};
21use std::time::Duration;
22
23/// The options of a [`BindingExpression`].
24pub struct BindingExpressionOptions {
25    /// The amount of time, in milliseconds, to wait before updating the
26    /// binding source after the value on the target changes.
27    pub delay: i32,
28    /// The fallback value; the unset marker for no fallback.
29    pub fallback_value: Option<BoxedValue>,
30    pub converter: Option<Rc<dyn IValueConverter>>,
31    /// The culture in which to evaluate the converter; `None` for the
32    /// current culture.
33    pub converter_culture: Option<CultureInfo>,
34    pub converter_parameter: Option<BoxedValue>,
35    pub enable_data_validation: bool,
36    pub mode: BindingMode,
37    pub priority: BindingPriority,
38    pub string_format: Option<String>,
39    /// The value to use when the binding produces null; the unset marker (or
40    /// null) for none.
41    pub target_null_value: Option<BoxedValue>,
42    pub target_property: Option<&'static FerroProperty>,
43    pub target_type_converter: Option<TargetTypeConverter>,
44    pub update_source_trigger: UpdateSourceTrigger,
45}
46
47impl Default for BindingExpressionOptions {
48    fn default() -> Self {
49        Self {
50            delay: 0,
51            fallback_value: Some(FerroProperty::unset_value()),
52            converter: None,
53            converter_culture: None,
54            converter_parameter: None,
55            enable_data_validation: false,
56            mode: BindingMode::OneWay,
57            priority: BindingPriority::LocalValue,
58            string_format: None,
59            target_null_value: Some(FerroProperty::unset_value()),
60            target_property: None,
61            target_type_converter: None,
62            update_source_trigger: UpdateSourceTrigger::PropertyChanged,
63        }
64    }
65}
66
67/// Uncommonly used fields are separated out to reduce memory usage.
68struct UncommonFields {
69    delay: Duration,
70    delay_timer: RefCell<Option<Rc<DispatcherTimer>>>,
71    delay_tick: RefCell<Option<Rc<dyn IDisposable>>>,
72    converter: Option<Rc<dyn IValueConverter>>,
73    converter_culture: Option<CultureInfo>,
74    converter_parameter: Option<BoxedValue>,
75    fallback_value: Option<BoxedValue>,
76    string_format: Option<String>,
77    target_null_value: Option<BoxedValue>,
78    update_source_trigger: UpdateSourceTrigger,
79}
80
81fn is_unset(value: &Option<BoxedValue>) -> bool {
82    value.as_ref().is_some_and(|v| v.is::<UnsetValueType>())
83}
84
85/// A binding expression which accepts and produces (possibly boxed) untyped
86/// values: a source, a path of expression nodes read from it, and the
87/// conversion of the result to the target.
88pub struct BindingExpression {
89    this: Weak<BindingExpression>,
90    base: UntypedBindingExpressionBase,
91    /// `None` when the binding source is null.
92    source: Option<WeakValue>,
93    mode: BindingMode,
94    nodes: Vec<Rc<dyn ExpressionNode>>,
95    target_type_converter: Option<TargetTypeConverter>,
96    uncommon: Option<Box<UncommonFields>>,
97    update_target_depth: Cell<u32>,
98    should_update_one_time_binding_target: Cell<bool>,
99    target_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
100    lost_focus_token: RefCell<Option<RoutedEventHandlerToken>>,
101}
102
103impl BindingExpression {
104    /// Creates a binding expression.
105    ///
106    /// `source` is the object from which the value is read (null is `None`);
107    /// `nodes` represent the binding path.
108    pub fn new(
109        source: Option<BoxedValue>,
110        nodes: Vec<Rc<dyn ExpressionNode>>,
111        options: BindingExpressionOptions,
112    ) -> Rc<Self> {
113        if options.mode == BindingMode::Default {
114            panic!("Binding mode cannot be Default.");
115        }
116        if options.update_source_trigger == UpdateSourceTrigger::Default {
117            panic!("UpdateSourceTrigger cannot be Default.");
118        }
119        let source = source.filter(|s| !s.is::<UnsetValueType>());
120        let string_format = options.string_format.filter(|s| !s.trim().is_empty());
121        let has_target_null_value = options.target_null_value.as_ref().is_some_and(|v| !v.is::<UnsetValueType>());
122
123        let uncommon = if options.delay > 0
124            || options.converter.is_some()
125            || options.converter_culture.is_some()
126            || options.converter_parameter.is_some()
127            || !is_unset(&options.fallback_value)
128            || string_format.is_some()
129            || has_target_null_value
130            || options.update_source_trigger != UpdateSourceTrigger::PropertyChanged
131        {
132            Some(Box::new(UncommonFields {
133                delay: Duration::from_millis(options.delay.max(0) as u64),
134                delay_timer: RefCell::new(None),
135                delay_tick: RefCell::new(None),
136                converter: options.converter,
137                converter_culture: options.converter_culture,
138                converter_parameter: options.converter_parameter,
139                fallback_value: options.fallback_value,
140                string_format: string_format.map(|s| if s.contains('{') { s } else { format!("{{0:{s}}}") }),
141                target_null_value: if has_target_null_value {
142                    options.target_null_value
143                } else {
144                    Some(FerroProperty::unset_value())
145                },
146                update_source_trigger: options.update_source_trigger,
147            }))
148        } else {
149            None
150        };
151
152        let mode = options.mode;
153        let this = Rc::new_cyclic(|this: &Weak<BindingExpression>| BindingExpression {
154            this: this.clone(),
155            base: UntypedBindingExpressionBase::new(
156                this.clone(),
157                options.priority,
158                options.target_property,
159                options.enable_data_validation,
160            ),
161            source: source.as_ref().map(WeakValue::new),
162            mode,
163            nodes,
164            target_type_converter: options.target_type_converter,
165            uncommon,
166            update_target_depth: Cell::new(0),
167            should_update_one_time_binding_target: Cell::new(mode == BindingMode::OneTime),
168            target_subscription: RefCell::new(None),
169            lost_focus_token: RefCell::new(None),
170        });
171
172        let mut leaf_accessor = None;
173        for (i, node) in this.nodes.iter().enumerate() {
174            node.set_owner(Rc::downgrade(&this), i);
175            if let Some(n) = node.as_property_accessor_node() {
176                leaf_accessor = Some(n);
177            }
178        }
179        if options.enable_data_validation {
180            if let Some(leaf) = leaf_accessor {
181                leaf.enable_data_validation();
182            }
183        }
184        this
185    }
186
187    /// The type of the value accepted by the binding source, if it can be
188    /// written.
189    pub fn source_type(&self) -> Option<ValueType> {
190        self.leaf_node().and_then(|n| n.as_settable().and_then(|s| s.value_type()))
191    }
192
193    pub fn converter(&self) -> Option<&Rc<dyn IValueConverter>> {
194        self.uncommon.as_ref().and_then(|u| u.converter.as_ref())
195    }
196
197    /// The culture in which the converter is evaluated: the converter
198    /// culture of the binding, or the current culture when it names none.
199    pub fn converter_culture(&self) -> CultureInfo {
200        self.uncommon
201            .as_ref()
202            .and_then(|uncommon| uncommon.converter_culture.clone())
203            .unwrap_or_else(CultureInfo::current_culture)
204    }
205
206    pub fn converter_parameter(&self) -> Option<&BoxedValue> {
207        self.uncommon.as_ref().and_then(|u| u.converter_parameter.as_ref())
208    }
209
210    /// The fallback value; the unset marker if there is none.
211    pub fn fallback_value(&self) -> Option<BoxedValue> {
212        match &self.uncommon {
213            Some(u) => u.fallback_value.clone(),
214            None => Some(FerroProperty::unset_value()),
215        }
216    }
217
218    pub fn leaf_node(&self) -> Option<&Rc<dyn ExpressionNode>> {
219        self.nodes.last()
220    }
221
222    pub fn nodes(&self) -> &[Rc<dyn ExpressionNode>] {
223        &self.nodes
224    }
225
226    pub fn mode(&self) -> BindingMode {
227        self.mode
228    }
229
230    pub fn string_format(&self) -> Option<&str> {
231        self.uncommon.as_ref().and_then(|u| u.string_format.as_deref())
232    }
233
234    /// The target null value; the unset marker if there is none.
235    pub fn target_null_value(&self) -> Option<BoxedValue> {
236        match &self.uncommon {
237            Some(u) => u.target_null_value.clone(),
238            None => Some(FerroProperty::unset_value()),
239        }
240    }
241
242    /// The amount of time to wait before updating the binding source after
243    /// the value on the target changes.
244    pub fn delay(&self) -> Duration {
245        self.uncommon.as_ref().map_or(Duration::ZERO, |u| u.delay)
246    }
247
248    fn stop_delay_timer(&self) {
249        if let Some(u) = &self.uncommon {
250            let timer = u.delay_timer.borrow().clone();
251            if let Some(timer) = timer {
252                timer.stop();
253            }
254        }
255    }
256
257    pub fn update_source_trigger(&self) -> UpdateSourceTrigger {
258        self.uncommon.as_ref().map_or(UpdateSourceTrigger::PropertyChanged, |u| u.update_source_trigger)
259    }
260
261    /// The current error state of the binding expression.
262    pub fn error_type(&self) -> BindingErrorType {
263        self.base.error_type()
264    }
265
266    /// Produces an observable subject over the expression, see
267    /// [`ObservableSink`].
268    pub fn to_observable(self: &Rc<Self>, target: Option<&FerroObject>) -> Rc<ObservableSink> {
269        ObservableSink::create(self, target)
270    }
271
272    /// Called by a node belonging to this binding when its value changes.
273    pub(crate) fn on_node_value_changed(
274        &self,
275        node_index: usize,
276        value: Option<BoxedValue>,
277        data_validation_error: Option<&BindingError>,
278    ) {
279        debug_assert!(node_index < self.nodes.len());
280        let count = self.nodes.len();
281
282        if node_index == count - 1 {
283            if self.mode == BindingMode::OneTime {
284                // In OneTime mode, only changing the data context updates the binding.
285                if !self.should_update_one_time_binding_target.get() && !self.nodes[node_index].is_data_context_node() {
286                    return;
287                }
288                self.should_update_one_time_binding_target.set(false);
289            }
290
291            let error = data_validation_error
292                .map(|e| ExpressionError::new(e.clone(), BindingErrorType::DataValidationError));
293
294            // The leaf node has changed. If the binding mode is not
295            // OneWayToSource, publish the value to the target.
296            if self.mode != BindingMode::OneWayToSource {
297                // An explicit target update must reapply the source value
298                // even if this expression already has it cached: a two-way
299                // target may contain an uncommitted local value.
300                let force_update = self.mode == BindingMode::OneWay || self.update_target_depth.get() > 0;
301                self.convert_and_publish_value(value, error, force_update);
302            } else if self.base.is_data_validation_enabled() {
303                // In OneWayToSource mode the value must not be published to
304                // the target, but any data validation error produced when
305                // writing to the source still has to be published (or
306                // cleared) so that it can be displayed.
307                self.base.publish_value(Publish::Unchanged, error, false);
308            }
309        } else if self.mode == BindingMode::OneWayToSource && node_index + 2 == count && value.is_some() {
310            // When the binding mode is OneWayToSource, the value is written
311            // to the source when the object holding the source property
312            // changes; this is the node before the leaf node. First update
313            // the leaf node's source, then write the value to its property.
314            self.nodes[node_index + 1].set_source(NodeSource::Value(value), data_validation_error);
315            self.write_target_value_to_source();
316        } else {
317            if self.mode == BindingMode::OneTime && self.nodes[node_index].is_data_context_node() {
318                self.should_update_one_time_binding_target.set(true);
319            }
320            self.nodes[node_index + 1].set_source(NodeSource::Value(value), data_validation_error);
321        }
322    }
323
324    /// Called by a node belonging to this binding when a null-conditional
325    /// operator applied to it encounters a null source: the remainder of the
326    /// path is short-circuited and null is published as the binding's value.
327    pub(crate) fn on_node_null_short_circuit(&self, node_index: usize) {
328        for node in &self.nodes[node_index + 1..] {
329            node.propagate_null_short_circuit_value();
330        }
331        // Publish the null value as if it were produced by the leaf node.
332        self.on_node_value_changed(self.nodes.len() - 1, None, None);
333    }
334
335    /// Called by a node belonging to this binding when an error occurs
336    /// reading its value. `node_index` is -1 if the source is null.
337    pub(crate) fn on_node_error(&self, node_index: isize, error: &str) {
338        // Set the source of all nodes after the one that errored to unset.
339        // This needs to be done for each node individually because setting
340        // the source to unset does not notify the binding.
341        for node in &self.nodes[(node_index + 1) as usize..] {
342            node.set_source(NodeSource::Unset, None);
343        }
344
345        if self.mode == BindingMode::OneWayToSource {
346            return;
347        }
348
349        let error_point = self.calculate_error_point(node_index);
350        if let Some(target) = self.should_log_error() {
351            self.log_at(&target, error, &error_point, LogEventLevel::Warning);
352        }
353
354        // Clear the current value and publish the error.
355        let binding_error = ExpressionError::new(
356            BindingError::new(BindingChainException::with_expression(error, self.description(), error_point)),
357            BindingErrorType::Error,
358        );
359        self.convert_and_publish_value(Some(FerroProperty::unset_value()), Some(binding_error), false);
360    }
361
362    pub(crate) fn on_data_validation_error(&self, error: BindingError) {
363        let binding_error = ExpressionError::new(error, BindingErrorType::DataValidationError);
364        self.base.publish_value(Publish::Unchanged, Some(binding_error), false);
365    }
366
367    fn calculate_error_point(&self, node_index: isize) -> String {
368        // A string describing the binding chain up to the node that errored.
369        let mut result = String::new();
370        if node_index >= 0 {
371            self.nodes[node_index as usize].build_string(&mut result);
372        } else {
373            result.push_str("(source)");
374        }
375        result
376    }
377
378    fn log_at(&self, target: &FerroObject, error: &str, error_point: &str, level: LogEventLevel) {
379        let Some(log) = Logger::try_get(level, LogArea::BINDING) else { return };
380        let property = self.base.target_property().map_or("(unknown)", |p| p.name());
381        let description = self.description();
382        log.log_with_values(
383            Some(target as &dyn Any),
384            "An error occurred binding {Property} to {Expression} at {ExpressionErrorPoint}: {Message}",
385            &[&property, &description, &error_point, &error],
386        );
387    }
388
389    fn convert_and_publish_value(
390        &self,
391        value: Option<BoxedValue>,
392        error: Option<ExpressionError>,
393        force_update: bool,
394    ) {
395        let mut value = value;
396        let mut error = error;
397        let mut is_target_null_value = false;
398        let target_type = self.base.target_type();
399
400        // All values other than the unset and do-nothing markers are passed
401        // to the converter.
402        if let Some(converter) = self.converter() {
403            if !is_unset(&value) && !BindingOperations::is_do_nothing(value.as_ref()) {
404                value = self.base.convert(
405                    &|| self.should_log_error(),
406                    &|| self.description(),
407                    &**converter,
408                    Some(&self.converter_culture()),
409                    self.converter_parameter(),
410                    value.as_ref(),
411                    target_type,
412                    &mut error,
413                );
414                // A converter may report its result as a notification.
415                let converted = value.clone();
416                if let Some(n) = converted.as_ref().and_then(|v| v.downcast_ref::<BindingNotification>()) {
417                    if let Some(e) = n.error() {
418                        error = Some(ExpressionError::new(e, n.error_type()));
419                    }
420                    value = if n.has_value() { n.value() } else { Some(FerroProperty::unset_value()) };
421                }
422            }
423        }
424
425        // Check this here as the converter may return the do-nothing marker.
426        if BindingOperations::is_do_nothing(value.as_ref()) {
427            return;
428        }
429
430        // The target null value only applies when the value is null: the
431        // unset marker indicates a binding error, for which it is not used.
432        let target_null_value = self.target_null_value();
433        if value.is_none() && !is_unset(&target_null_value) {
434            value = self.convert_fallback(target_null_value, "TargetNullValue");
435            is_target_null_value = true;
436        }
437
438        // If we have a value, try to convert it to the target type.
439        if !is_unset(&value) {
440            match self.string_format() {
441                Some(string_format)
442                    if (target_type.is_object() || target_type.is_string()) && !is_target_null_value =>
443                {
444                    // The string format applies if the target can accept a
445                    // string and the value isn't the target null value.
446                    value = match composite_format::format_values_with(
447                        string_format,
448                        std::slice::from_ref(&value),
449                        &self.converter_culture(),
450                    ) {
451                        Ok(s) => self.convert_from(Some(Rc::new(s)), &mut error),
452                        Err(e) => {
453                            error = Some(ExpressionError::new(BindingError::new(e), BindingErrorType::Error));
454                            Some(FerroProperty::unset_value())
455                        }
456                    };
457                }
458                _ => value = self.convert_from(value, &mut error),
459            }
460        }
461
462        // The fallback value applies if the result from the binding,
463        // converter or target type converter is the unset marker.
464        let fallback_value = self.fallback_value();
465        if is_unset(&value) && !is_unset(&fallback_value) {
466            value = self.convert_fallback(fallback_value, "FallbackValue");
467        }
468
469        self.base.publish_value(Publish::Value(value), error, force_update);
470    }
471
472    fn write_target_value_to_source(&self) {
473        #[cfg(all(debug_assertions, feature = "hot-reload"))]
474        if self.base.is_reload_paused() { return; }
475
476        debug_assert!(matches!(self.mode, BindingMode::TwoWay | BindingMode::OneWayToSource));
477        self.stop_delay_timer();
478        if let (Some(target), Some(property)) = (self.base.try_get_target(), self.base.target_property()) {
479            let value = ValueTypes::normalize(target.get_value_untyped(property));
480            self.write_value_to_source(value);
481        }
482    }
483
484    fn convert_fallback(&self, fallback: Option<BoxedValue>, fallback_name: &str) -> Option<BoxedValue> {
485        let target_type = self.base.target_type();
486        if is_unset(&fallback) || (self.base.target_property().is_none() && target_type.is_object()) {
487            return fallback;
488        }
489        // The original converts with its target type converter and the converter culture.
490        let converted = match &self.target_type_converter {
491            Some(converter) => converter.try_convert(fallback.as_ref(), target_type, &self.converter_culture()),
492            None => ValueTypes::try_convert(fallback.as_ref(), target_type),
493        };
494        if let Some(result) = converted {
495            return result;
496        }
497        if let Some(target) = self.base.try_get_target() {
498            self.base.log(
499                &target,
500                &self.description(),
501                &format!(
502                    "Could not convert {fallback_name} '{}' to '{target_type}'.",
503                    ValueTypes::to_display_string(fallback.as_ref())
504                ),
505                LogEventLevel::Error,
506            );
507        }
508        Some(FerroProperty::unset_value())
509    }
510
511    /// Converts a produced value to the target type. Without a target
512    /// property (an expression observed directly) values pass through.
513    fn convert_from(&self, value: Option<BoxedValue>, error: &mut Option<ExpressionError>) -> Option<BoxedValue> {
514        let target_type = self.base.target_type();
515        if self.base.target_property().is_none() && target_type.is_object() {
516            return value;
517        }
518        // The converter of string-path bindings knows conversions the table
519        // of value types does not (the delegate of a method to a command).
520        let converted = match &self.target_type_converter {
521            Some(converter) => converter.try_convert(value.as_ref(), target_type, &self.converter_culture()),
522            None => ValueTypes::try_convert(value.as_ref(), target_type),
523        };
524        if let Some(result) = converted {
525            return result;
526        }
527        let message = format!(
528            "Could not convert '{}' ({}) to '{}'.",
529            ValueTypes::to_display_string(value.as_ref()),
530            value.as_ref().map_or("null", |v| (**v).type_name()),
531            target_type
532        );
533        if let Some(target) = self.should_log_error() {
534            self.base.log(&target, &self.description(), &message, LogEventLevel::Warning);
535        }
536        *error = Some(ExpressionError::new(BindingError::message(message), BindingErrorType::Error));
537        Some(FerroProperty::unset_value())
538    }
539
540    /// Runs the converter on a value on its way to the source. Returns false
541    /// if the converter reported an error, in which case the value is not
542    /// written.
543    fn try_convert_back(&self, value_type: ValueType, value: &mut Option<BoxedValue>) -> bool {
544        let Some(converter) = self.converter() else { return true };
545        if is_unset(value) || BindingOperations::is_do_nothing(value.as_ref()) {
546            return true;
547        }
548        *value = self.base.convert_back(
549            &|| self.should_log_error(),
550            &|| self.description(),
551            &**converter,
552            Some(&self.converter_culture()),
553            self.converter_parameter(),
554            value.as_ref(),
555            value_type,
556        );
557        let converted = value.clone();
558        if let Some(notification) = converted.as_ref().and_then(|v| v.downcast_ref::<BindingNotification>()) {
559            if let Some(error) = notification.error() {
560                match notification.error_type() {
561                    BindingErrorType::DataValidationError => {
562                        if self.base.is_data_validation_enabled() {
563                            self.on_data_validation_error(error);
564                        }
565                    }
566                    _ => {
567                        if let Some(target) = self.should_log_error() {
568                            self.base.log(&target, &self.description(), &error.to_string(), LogEventLevel::Warning);
569                        }
570                        self.base.publish_value(
571                            Publish::Unchanged,
572                            Some(ExpressionError::new(error, BindingErrorType::Error)),
573                            false,
574                        );
575                    }
576                }
577                return false;
578            }
579            *value = notification.value();
580        }
581        true
582    }
583
584    fn on_target_property_changed(&self) {
585        debug_assert!(matches!(self.mode, BindingMode::TwoWay | BindingMode::OneWayToSource));
586        let delay = self.delay();
587        let Some(uncommon) = self.uncommon.as_ref().filter(|_| !delay.is_zero()) else {
588            // The value is read from the target object instead of the change
589            // notification because it may have changed again in between.
590            self.write_target_value_to_source();
591            return;
592        };
593
594        let existing = uncommon.delay_timer.borrow().clone();
595        let timer = match existing {
596            Some(timer) => {
597                timer.stop();
598                timer
599            }
600            None => {
601                let timer = DispatcherTimer::with_interval(
602                    delay,
603                    DispatcherPriority::DEFAULT,
604                    &Dispatcher::current_dispatcher(),
605                );
606                let weak = self.this.clone();
607                let tick = timer.tick(move |_| {
608                    if let Some(this) = weak.upgrade() {
609                        this.write_target_value_to_source();
610                    }
611                });
612                uncommon.delay_tick.replace(Some(tick));
613                uncommon.delay_timer.replace(Some(timer.clone()));
614                timer
615            }
616        };
617        timer.start();
618    }
619}
620
621impl UntypedBindingExpression for BindingExpression {
622    fn base(&self) -> &UntypedBindingExpressionBase {
623        &self.base
624    }
625
626    fn description(&self) -> String {
627        let mut b = String::new();
628        if let Some(leaf) = self.leaf_node() {
629            leaf.build_string_with_nodes(&mut b, &self.nodes);
630        }
631        b
632    }
633
634    fn update_source_core(&self) {
635        if matches!(self.mode, BindingMode::TwoWay | BindingMode::OneWayToSource) {
636            self.write_target_value_to_source();
637        }
638    }
639
640    fn update_target_core(&self) {
641        let Some(first) = self.nodes.first() else { return };
642        let source = first.state().source();
643        self.update_target_depth.set(self.update_target_depth.get() + 1);
644        for node in &self.nodes {
645            node.set_source(NodeSource::Unset, None);
646        }
647        // A dead or null source reads as null, as in the managed
648        // implementation.
649        first.set_source(NodeSource::Value(source), None);
650        self.update_target_depth.set(self.update_target_depth.get() - 1);
651    }
652
653    fn write_value_to_source(&self, value: Option<BoxedValue>) -> bool {
654        #[cfg(all(debug_assertions, feature = "hot-reload"))]
655        if self.base.is_reload_paused() {
656            return false;
657        }
658        self.stop_delay_timer();
659        let mut value = value;
660        let Some(leaf) = self.leaf_node() else { return false };
661        let Some(setter) = leaf.as_settable() else { return false };
662        let Some(type_) = setter.value_type() else { return false };
663
664        // Invoke any converter on the value before writing it to the source.
665        // If the converter returns an error the value is not written.
666        if !self.try_convert_back(type_, &mut value) {
667            return false;
668        }
669
670        // A converter may return the do-nothing marker.
671        if BindingOperations::is_do_nothing(value.as_ref()) {
672            return true;
673        }
674
675        // Use the target type converter to convert the value to the source
676        // type if necessary.
677        if let Some(converter) = &self.target_type_converter {
678            if let Some(converted) = converter.try_convert(value.as_ref(), type_, &self.converter_culture()) {
679                value = converted;
680            } else if !is_unset(&self.fallback_value()) {
681                value = self.fallback_value();
682            } else if self.base.is_data_validation_enabled() {
683                self.on_data_validation_error(BindingError::message(format!(
684                    "Could not convert '{}' ({}) to {}.",
685                    ValueTypes::to_display_string(value.as_ref()),
686                    value.as_ref().map_or("null", |v| (**v).type_name()),
687                    type_
688                )));
689                return false;
690            } else {
691                return false;
692            }
693        }
694
695        // Don't set the value if it's unchanged. If there is a binding
696        // error, the value still has to be set in order to clear the error.
697        let current = leaf.state().value();
698        let current = current.and_then(ValueTypes::normalize);
699        let new = value.clone().and_then(ValueTypes::normalize);
700        if ValueTypes::identity_equals(current.as_ref(), new.as_ref())
701            && self.base.error_type() == BindingErrorType::None
702        {
703            return true;
704        }
705
706        setter.write_value_to_source(value.as_ref(), &self.nodes).unwrap_or(false)
707    }
708
709    fn should_log_error(&self) -> Option<Ref<FerroObject>> {
710        let target = self.base.try_get_target()?;
711        if let Some(first) = self.nodes.first() {
712            if let Some(source_node) = first.as_source_node() {
713                if !source_node.should_log_errors(first.state(), &target) {
714                    return None;
715                }
716            }
717        }
718        Some(target)
719    }
720
721    #[cfg(all(debug_assertions, feature = "hot-reload"))]
722    fn supports_reload_pause(&self) -> bool {
723        matches!(self.mode, BindingMode::OneWay | BindingMode::TwoWay)
724            && self.delay().is_zero()
725            && self.update_source_trigger() == UpdateSourceTrigger::PropertyChanged
726    }
727
728    fn start_core(&self) {
729        let source = self.source.as_ref().and_then(WeakValue::upgrade);
730        let Some(source) = source else {
731            self.on_node_error(-1, "Binding Source is null.");
732            return;
733        };
734
735        match self.nodes.first() {
736            Some(first) => first.set_source(NodeSource::Value(Some(source)), None),
737            None => self.convert_and_publish_value(Some(source), None, false),
738        }
739
740        if matches!(self.mode, BindingMode::TwoWay | BindingMode::OneWayToSource) {
741            if let (Some(target), Some(property)) = (self.base.try_get_target(), self.base.target_property()) {
742                if self.update_source_trigger() == UpdateSourceTrigger::PropertyChanged {
743                    let weak = self.this.clone();
744                    let id = property.id();
745                    let subscription = target.property_changed(move |e| {
746                        if e.property().id() == id {
747                            if let Some(this) = weak.upgrade() {
748                                this.on_target_property_changed();
749                            }
750                        }
751                    });
752                    self.target_subscription.replace(Some(subscription));
753                } else if self.update_source_trigger() == UpdateSourceTrigger::LostFocus {
754                    if let Some(element) = target.downcast_ref::<InputElement>() {
755                        let weak = self.this.clone();
756                        let token = element.add_handler(InputElement::lost_focus_event(), move |_, _| {
757                            if let Some(this) = weak.upgrade() {
758                                this.write_target_value_to_source();
759                            }
760                        });
761                        self.lost_focus_token.replace(Some(token));
762                    }
763                }
764            }
765        }
766    }
767
768    fn stop_core(&self) {
769        self.stop_delay_timer();
770        let token = self.lost_focus_token.borrow_mut().take();
771        if let Some(token) = token {
772            if let Some(target) = self.base.try_get_target() {
773                if let Some(element) = target.downcast_ref::<InputElement>() {
774                    element.remove_handler(InputElement::lost_focus_event(), token);
775                }
776            }
777        }
778        for node in &self.nodes {
779            node.set_source(NodeSource::Unset, None);
780        }
781        let subscription = self.target_subscription.borrow_mut().take();
782        if let Some(s) = subscription {
783            s.dispose();
784        }
785    }
786}
787
788impl_untyped_binding_expression!(BindingExpression);
789
790impl Drop for BindingExpression {
791    fn drop(&mut self) {
792        if let Some(s) = self.target_subscription.get_mut().take() {
793            s.dispose();
794        }
795    }
796}