ferroui_base/data/core/
multi_binding_expression.rs1use super::{
2 impl_untyped_binding_expression, IBindingExpressionSink, Publish, SinkRef, UntypedBindingExpression,
3 UntypedBindingExpressionBase, ValueTypes,
4};
5use crate::data::converters::IMultiValueConverter;
6use crate::data::{BindingBase, BindingExpressionBase, BindingNotification, BindingOperations, BindingPriority};
7use crate::utilities::CultureInfo;
8use crate::{BoxedValue, FerroProperty};
9use std::cell::RefCell;
10use std::rc::{Rc, Weak};
11
12pub struct MultiBindingExpression {
15 this: Weak<MultiBindingExpression>,
16 base: UntypedBindingExpressionBase,
17 bindings: Vec<Rc<dyn BindingBase>>,
18 converter: Option<Rc<dyn IMultiValueConverter>>,
19 converter_culture: Option<CultureInfo>,
20 converter_parameter: Option<BoxedValue>,
21 expressions: RefCell<Vec<Option<Rc<dyn BindingExpressionBase>>>>,
22 fallback_value: Option<BoxedValue>,
23 target_null_value: Option<BoxedValue>,
24 values: RefCell<Vec<Option<Option<BoxedValue>>>>,
27}
28
29impl MultiBindingExpression {
30 pub fn new(
31 priority: BindingPriority,
32 bindings: Vec<Rc<dyn BindingBase>>,
33 converter: Option<Rc<dyn IMultiValueConverter>>,
34 converter_culture: Option<CultureInfo>,
35 converter_parameter: Option<BoxedValue>,
36 fallback_value: Option<BoxedValue>,
37 target_null_value: Option<BoxedValue>,
38 ) -> Rc<Self> {
39 let count = bindings.len();
40 Rc::new_cyclic(|this: &Weak<MultiBindingExpression>| Self {
41 this: this.clone(),
42 base: UntypedBindingExpressionBase::new(this.clone(), priority, None, false),
43 bindings,
44 converter,
45 converter_culture,
46 converter_parameter,
47 expressions: RefCell::new(vec![None; count]),
48 fallback_value,
49 target_null_value,
50 values: RefCell::new(vec![None; count]),
51 })
52 }
53
54 pub fn converter(&self) -> Option<&Rc<dyn IMultiValueConverter>> {
55 self.converter.as_ref()
56 }
57
58 pub fn converter_culture(&self) -> Option<&CultureInfo> {
61 self.converter_culture.as_ref()
62 }
63
64 fn publish(&self) {
65 let values: Vec<Option<BoxedValue>> = {
66 let values = self.values.borrow();
67 if values.iter().any(Option::is_none) {
68 return;
69 }
70 values.iter().map(|v| v.clone().expect("checked")).collect()
71 };
72
73 let target_type = self.base.target_type();
74 let has_target = self.base.target_property().is_some();
75
76 match &self.converter {
77 Some(converter) => {
78 let culture = self.converter_culture.clone().unwrap_or_else(CultureInfo::current_culture);
79 let converted = match converter.convert(&values, target_type, self.converter_parameter.as_ref(), &culture)
80 {
81 Ok(v) => v,
82 Err(_) => Some(FerroProperty::unset_value()),
83 };
84 let mut converted = BindingNotification::extract_value(converted.as_ref());
85 if BindingOperations::is_do_nothing(converted.as_ref()) {
86 return;
87 }
88 if converted.is_none() {
89 converted = self.target_null_value.clone();
90 }
91 if BindingOperations::is_unset(converted.as_ref()) {
92 converted = self.fallback_value.clone();
93 }
94 if has_target && !BindingOperations::is_unset(converted.as_ref()) {
95 converted = ValueTypes::try_convert(converted.as_ref(), target_type)
96 .unwrap_or_else(|| Some(FerroProperty::unset_value()));
97 }
98 self.base.publish_value(Publish::Value(converted), None, false);
99 }
100 None => {
101 let list: BoxedValue = Rc::new(values);
102 let value = if has_target {
103 ValueTypes::try_convert(Some(&list), target_type)
104 .unwrap_or_else(|| Some(FerroProperty::unset_value()))
105 } else {
106 Some(list)
107 };
108 self.base.publish_value(Publish::Value(value), None, false);
109 }
110 }
111 }
112}
113
114impl IBindingExpressionSink for MultiBindingExpression {
115 fn on_changed(&self, instance: &Rc<dyn BindingExpressionBase>, _has_value_changed: bool, _has_error_changed: bool) {
116 let index = self
117 .expressions
118 .borrow()
119 .iter()
120 .position(|e| e.as_ref().is_some_and(|e| std::ptr::addr_eq(Rc::as_ptr(e), Rc::as_ptr(instance))));
121 let Some(index) = index else { return };
122 let value = if instance.has_value() {
123 BindingNotification::extract_value(instance.get_value_boxed().as_ref())
124 } else {
125 Some(FerroProperty::unset_value())
126 };
127 self.values.borrow_mut()[index] = Some(value);
128 self.publish();
129 }
130
131 fn on_completed(&self, _instance: &Rc<dyn BindingExpressionBase>) {
132 }
134}
135
136impl UntypedBindingExpression for MultiBindingExpression {
137 fn base(&self) -> &UntypedBindingExpressionBase {
138 &self.base
139 }
140
141 fn description(&self) -> String {
142 "MultiBinding".to_string()
143 }
144
145 fn start_core(&self) {
146 let target = self.base.try_get_target().expect("MultiBindingExpression has no target.");
147 let sink: Weak<dyn IBindingExpressionSink> = self.this.clone();
148 for (i, binding) in self.bindings.iter().enumerate() {
149 let expression = binding.create_instance(&target, None, None);
150 let Some(untyped) = expression.as_untyped() else {
151 panic!("Unsupported BindingExpressionBase implementation.");
152 };
153 self.expressions.borrow_mut()[i] = Some(expression.clone());
154 untyped.base().attach(SinkRef::Other(sink.clone()), None, &target, None, self.base.priority());
155 untyped.start(true);
156 }
157 }
158
159 fn stop_core(&self) {
160 let expressions: Vec<_> = self.expressions.borrow_mut().iter_mut().map(Option::take).collect();
161 for expression in expressions.into_iter().flatten() {
162 expression.dispose();
163 }
164 for value in self.values.borrow_mut().iter_mut() {
165 *value = None;
166 }
167 }
168}
169
170impl_untyped_binding_expression!(MultiBindingExpression);