ferroui_base/data/core/
indexer_binding_expression.rs1use super::{
2 impl_untyped_binding_expression, Publish, UntypedBindingExpression, UntypedBindingExpressionBase, ValueType, ValueTypes,
3};
4use crate::data::{BindingMode, BindingPriority};
5use crate::reactive::IDisposable;
6use crate::{BoxedValue, FerroObject, FerroProperty, WeakRef};
7use std::cell::RefCell;
8use std::rc::{Rc, Weak};
9
10pub struct IndexerBindingExpression {
19 this: Weak<IndexerBindingExpression>,
20 base: UntypedBindingExpressionBase,
21 source: WeakRef<FerroObject>,
22 source_property: &'static FerroProperty,
23 target: WeakRef<FerroObject>,
24 target_property: Option<&'static FerroProperty>,
25 mode: BindingMode,
26 source_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
27 target_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
28}
29
30impl IndexerBindingExpression {
31 pub fn new(
32 source: &FerroObject,
33 source_property: &'static FerroProperty,
34 target: &FerroObject,
35 target_property: Option<&'static FerroProperty>,
36 mode: BindingMode,
37 ) -> Rc<Self> {
38 Rc::new_cyclic(|this: &Weak<IndexerBindingExpression>| Self {
39 this: this.clone(),
40 base: UntypedBindingExpressionBase::new(this.clone(), BindingPriority::LocalValue, None, false),
41 source: source.to_weak(),
42 source_property,
43 target: target.to_weak(),
44 target_property,
45 mode,
46 source_subscription: RefCell::new(None),
47 target_subscription: RefCell::new(None),
48 })
49 }
50
51 fn publish_source_value(&self) {
52 if let Some(source) = self.source.upgrade() {
53 let value = source.get_value_untyped(self.source_property);
54 let value = match self.target_property {
55 Some(target_property) => cast_to_property(value, target_property),
56 None => value,
57 };
58 self.base.publish_value(Publish::Value(Some(value)), None, false);
59 }
60 }
61}
62
63fn cast_to_property(value: BoxedValue, property: &'static FerroProperty) -> BoxedValue {
77 let target = ValueType::new(property.property_type(), property.property_type_name());
78 if ValueType::of_value(&*value).id() == target.id() {
79 return value;
80 }
81
82 let converted = match ValueTypes::normalize(value.clone()) {
83 Some(inner) => ValueTypes::try_cast(&inner, target),
84 None => ValueTypes::null_value(target),
85 };
86 converted.unwrap_or(value)
87}
88
89impl UntypedBindingExpression for IndexerBindingExpression {
90 fn base(&self) -> &UntypedBindingExpressionBase {
91 &self.base
92 }
93
94 fn description(&self) -> String {
95 format!("IndexerBinding {})", self.source_property.name())
96 }
97
98 fn write_value_to_source(&self, value: Option<BoxedValue>) -> bool {
99 if let (Some(source), Some(value)) = (self.source.upgrade(), value) {
100 source.set_value_untyped(self.source_property, (*value).as_any(), BindingPriority::LocalValue);
101 }
102 true
103 }
104
105 fn start_core(&self) {
106 if matches!(self.mode, BindingMode::TwoWay | BindingMode::OneWayToSource) {
107 if let (Some(target), Some(target_property)) = (self.target.upgrade(), self.target_property) {
108 let weak = self.this.clone();
109 let id = target_property.id();
110 let subscription = target.property_changed(move |e| {
111 if e.property().id() != id {
112 return;
113 }
114 let Some(this) = weak.upgrade() else { return };
115 if let Some(source) = this.source.upgrade() {
116 source.set_value_untyped(this.source_property, e.new_value(), BindingPriority::LocalValue);
117 }
118 });
119 self.target_subscription.replace(Some(subscription));
120 }
121 }
122
123 if self.mode != BindingMode::OneWayToSource {
124 if let Some(source) = self.source.upgrade() {
125 let weak = self.this.clone();
126 let id = self.source_property.id();
127 let subscription = source.property_changed(move |e| {
128 if e.property().id() == id {
129 if let Some(this) = weak.upgrade() {
130 this.publish_source_value();
131 }
132 }
133 });
134 self.source_subscription.replace(Some(subscription));
135 }
136 self.publish_source_value();
137 }
138
139 if self.mode == BindingMode::OneTime {
140 self.stop();
141 }
142 }
143
144 fn stop_core(&self) {
145 let subscription = self.source_subscription.borrow_mut().take();
146 if let Some(s) = subscription {
147 s.dispose();
148 }
149 let subscription = self.target_subscription.borrow_mut().take();
150 if let Some(s) = subscription {
151 s.dispose();
152 }
153 }
154}
155
156impl_untyped_binding_expression!(IndexerBindingExpression);
157
158impl Drop for IndexerBindingExpression {
159 fn drop(&mut self) {
160 if let Some(s) = self.source_subscription.get_mut().take() {
161 s.dispose();
162 }
163 if let Some(s) = self.target_subscription.get_mut().take() {
164 s.dispose();
165 }
166 }
167}