1use super::*;
5use alloc::rc::Rc;
6use core::cell::Cell;
7use core::cell::UnsafeCell;
8use core::marker::PhantomData;
9use core::marker::PhantomPinned;
10use core::pin::Pin;
11
12struct TwoWayBinding<T> {
13 common_property: Pin<Rc<Property<T>>>,
14}
15unsafe impl<T: PartialEq + Clone + 'static> BindingCallable<T> for TwoWayBinding<T> {
16 fn evaluate(self: Pin<&Self>, value: &mut T) -> BindingResult {
17 *value = self.common_property.as_ref().get();
18 BindingResult::KeepBinding
19 }
20
21 fn intercept_set(self: Pin<&Self>, value: &T) -> bool {
22 self.common_property.as_ref().set(value.clone());
23 true
24 }
25
26 unsafe fn intercept_set_binding(self: Pin<&Self>, new_binding: *mut BindingHolder) -> bool {
27 self.common_property.handle.set_binding_impl(new_binding);
28 true
29 }
30
31 const IS_TWO_WAY_BINDING: bool = true;
32}
33
34impl<T: PartialEq + Clone + 'static> Property<T> {
35 pub(crate) fn check_common_property(self: Pin<&Self>) -> Option<Pin<Rc<Property<T>>>> {
37 let handle_val = self.handle.handle.get();
38 if let Some(holder) = PropertyHandle::pointer_to_binding(handle_val) {
39 if unsafe { (*holder).is_two_way_binding } {
41 return Some(unsafe {
43 (*(holder as *const BindingHolder<TwoWayBinding<T>>))
44 .binding
45 .common_property
46 .clone()
47 });
48 }
49 }
50 None
51 }
52
53 pub fn link_two_way(prop1: Pin<&Self>, prop2: Pin<&Self>) {
57 #[cfg(slint_debug_property)]
58 let debug_name =
59 alloc::format!("<{}<=>{}>", prop1.debug_name.borrow(), prop2.debug_name.borrow());
60
61 let value = prop2.get_internal();
62
63 if let Some(common_property) = prop1.check_common_property() {
64 unsafe {
66 prop2.handle.set_binding(
67 TwoWayBinding::<T> { common_property },
68 #[cfg(slint_debug_property)]
69 debug_name.as_str(),
70 );
71 }
72 prop2.set(value);
73 return;
74 }
75
76 if let Some(common_property) = prop2.check_common_property() {
77 unsafe {
79 prop1.handle.set_binding(
80 TwoWayBinding::<T> { common_property },
81 #[cfg(slint_debug_property)]
82 debug_name.as_str(),
83 );
84 }
85 return;
86 }
87
88 let prop2_handle_val = prop2.handle.handle.get();
89 let handle = if PropertyHandle::is_pointer_to_binding(prop2_handle_val) {
90 prop2.handle.handle.set(0);
92 PropertyHandle { handle: Cell::new(prop2_handle_val) }
93 } else {
94 PropertyHandle::default()
95 };
96
97 let common_property = Rc::pin(Property {
98 handle,
99 value: UnsafeCell::new(value),
100 pinned: PhantomPinned,
101 #[cfg(slint_debug_property)]
102 debug_name: debug_name.clone().into(),
103 });
104 unsafe {
106 prop1.handle.set_binding(
107 TwoWayBinding { common_property: common_property.clone() },
108 #[cfg(slint_debug_property)]
109 debug_name.as_str(),
110 );
111 prop2.handle.set_binding(
112 TwoWayBinding { common_property },
113 #[cfg(slint_debug_property)]
114 debug_name.as_str(),
115 );
116 }
117 }
118
119 pub fn link_two_way_with_map<T2: PartialEq + Clone + 'static>(
125 prop1: Pin<&Self>,
126 prop2: Pin<&Property<T2>>,
127 map_to: impl Fn(&T) -> T2 + Clone + 'static, map_from: impl Fn(&mut T, &T2) + Clone + 'static,
129 ) {
130 let common_property = if let Some(common_property) = prop1.check_common_property() {
131 common_property
132 } else {
133 let prop1_handle_val = prop1.handle.handle.get();
134 let handle = if PropertyHandle::is_pointer_to_binding(prop1_handle_val) {
135 prop1.handle.handle.set(0);
137 PropertyHandle { handle: Cell::new(prop1_handle_val) }
138 } else {
139 PropertyHandle::default()
140 };
141
142 #[cfg(slint_debug_property)]
143 let debug_name = alloc::format!("{}*", prop1.debug_name.borrow());
144
145 let common_property = Rc::pin(Property {
146 handle,
147 value: UnsafeCell::new(prop1.get_internal()),
148 pinned: PhantomPinned,
149 #[cfg(slint_debug_property)]
150 debug_name: debug_name.clone().into(),
151 });
152 unsafe {
154 prop1.handle.set_binding(
155 TwoWayBinding::<T> { common_property: common_property.clone() },
156 #[cfg(slint_debug_property)]
157 debug_name.as_str(),
158 );
159 }
160 common_property
161 };
162 Self::link_two_way_with_map_to_common_property(common_property, prop2, map_to, map_from);
163 }
164
165 pub(crate) fn link_two_way_with_map_to_common_property<T2: PartialEq + Clone + 'static>(
168 common_property: Pin<Rc<Self>>,
169 prop2: Pin<&Property<T2>>,
170 map_to: impl Fn(&T) -> T2 + Clone + 'static,
171 map_from: impl Fn(&mut T, &T2) + Clone + 'static,
172 ) {
173 struct TwoWayBindingWithMap<T, T2, M1, M2> {
174 common_property: Pin<Rc<Property<T>>>,
175 map_to: M1,
176 map_from: M2,
177 marker: PhantomData<(T, T2)>,
178 }
179 unsafe impl<
180 T: PartialEq + Clone + 'static,
181 T2: PartialEq + Clone + 'static,
182 M1: Fn(&T) -> T2 + Clone + 'static,
183 M2: Fn(&mut T, &T2) + Clone + 'static,
184 > BindingCallable<T2> for TwoWayBindingWithMap<T, T2, M1, M2>
185 {
186 fn evaluate(self: Pin<&Self>, value: &mut T2) -> BindingResult {
187 *value = (self.map_to)(&self.common_property.as_ref().get());
188 BindingResult::KeepBinding
189 }
190
191 fn intercept_set(self: Pin<&Self>, value: &T2) -> bool {
192 let mut old = self.common_property.as_ref().get();
193 (self.map_from)(&mut old, value);
194 self.common_property.as_ref().set(old);
195 true
196 }
197
198 unsafe fn intercept_set_binding(
199 self: Pin<&Self>,
200 new_binding: *mut BindingHolder,
201 ) -> bool {
202 let new_new_binding = alloc_binding_holder(BindingMapper::<T, T2, M1, M2> {
203 b: new_binding,
204 map_to: self.map_to.clone(),
205 map_from: self.map_from.clone(),
206 marker: PhantomData,
207 });
208 self.common_property.handle.set_binding_impl(new_new_binding);
209 true
210 }
211 }
212
213 struct BindingMapper<T, T2, M1, M2> {
215 b: *mut BindingHolder,
217 map_to: M1,
218 map_from: M2,
219 marker: PhantomData<(T, T2)>,
220 }
221 unsafe impl<
222 T: PartialEq + Clone + 'static,
223 T2: PartialEq + Clone + 'static,
224 M1: Fn(&T) -> T2 + 'static,
225 M2: Fn(&mut T, &T2) + 'static,
226 > BindingCallable<T> for BindingMapper<T, T2, M1, M2>
227 {
228 fn evaluate(self: Pin<&Self>, value: &mut T) -> BindingResult {
229 let mut sub_value = (self.map_to)(value);
230 unsafe {
232 ((*self.b).vtable.evaluate)(self.b, &mut sub_value as *mut T2 as *mut ());
233 }
234 (self.map_from)(value, &sub_value);
235 BindingResult::KeepBinding
236 }
237
238 fn intercept_set(self: Pin<&Self>, value: &T) -> bool {
239 let sub_value = (self.map_to)(value);
240 unsafe {
242 ((*self.b).vtable.intercept_set)(self.b, &sub_value as *const T2 as *const ())
243 }
244 }
245 }
246 impl<T, T2, M1, M2> Drop for BindingMapper<T, T2, M1, M2> {
247 fn drop(&mut self) {
248 unsafe {
249 ((*self.b).vtable.drop)(self.b);
250 }
251 }
252 }
253
254 #[cfg(slint_debug_property)]
255 let debug_name = alloc::format!(
256 "<{}<=>{}>",
257 common_property.debug_name.borrow(),
258 prop2.debug_name.borrow()
259 );
260
261 let old_binding = prop2.handle.detach_binding();
268
269 unsafe {
270 prop2.handle.set_binding(
271 TwoWayBindingWithMap { common_property, map_to, map_from, marker: PhantomData },
272 #[cfg(slint_debug_property)]
273 debug_name.as_str(),
274 );
275
276 if let Some(binding) = old_binding {
277 prop2.handle.set_binding_impl(binding);
278 }
279 };
280 }
281}
282
283#[test]
284fn property_two_ways_test() {
285 let p1 = Rc::pin(Property::new(42));
286 let p2 = Rc::pin(Property::new(88));
287
288 let depends = Box::pin(Property::new(0));
289 depends.as_ref().set_binding({
290 let p1 = p1.clone();
291 move || p1.as_ref().get() + 8
292 });
293 assert_eq!(depends.as_ref().get(), 42 + 8);
294 Property::link_two_way(p1.as_ref(), p2.as_ref());
295 assert_eq!(p1.as_ref().get(), 88);
296 assert_eq!(p2.as_ref().get(), 88);
297 assert_eq!(depends.as_ref().get(), 88 + 8);
298 p2.as_ref().set(5);
299 assert_eq!(p1.as_ref().get(), 5);
300 assert_eq!(p2.as_ref().get(), 5);
301 assert_eq!(depends.as_ref().get(), 5 + 8);
302 p1.as_ref().set(22);
303 assert_eq!(p1.as_ref().get(), 22);
304 assert_eq!(p2.as_ref().get(), 22);
305 assert_eq!(depends.as_ref().get(), 22 + 8);
306}
307
308#[test]
309fn property_two_ways_test_binding() {
310 let p1 = Rc::pin(Property::new(42));
311 let p2 = Rc::pin(Property::new(88));
312 let global = Rc::pin(Property::new(23));
313 p2.as_ref().set_binding({
314 let global = global.clone();
315 move || global.as_ref().get() + 9
316 });
317
318 let depends = Box::pin(Property::new(0));
319 depends.as_ref().set_binding({
320 let p1 = p1.clone();
321 move || p1.as_ref().get() + 8
322 });
323
324 Property::link_two_way(p1.as_ref(), p2.as_ref());
325 assert_eq!(p1.as_ref().get(), 23 + 9);
326 assert_eq!(p2.as_ref().get(), 23 + 9);
327 assert_eq!(depends.as_ref().get(), 23 + 9 + 8);
328 global.as_ref().set(55);
329 assert_eq!(p1.as_ref().get(), 55 + 9);
330 assert_eq!(p2.as_ref().get(), 55 + 9);
331 assert_eq!(depends.as_ref().get(), 55 + 9 + 8);
332}
333
334#[test]
335fn property_two_ways_recurse_from_binding() {
336 let xx = Rc::pin(Property::new(0));
337
338 let p1 = Rc::pin(Property::new(42));
339 let p2 = Rc::pin(Property::new(88));
340 let global = Rc::pin(Property::new(23));
341
342 let done = Rc::new(Cell::new(false));
343 xx.set_binding({
344 let p1 = p1.clone();
345 let p2 = p2.clone();
346 let global = global.clone();
347 let xx_weak = pin_weak::rc::PinWeak::downgrade(xx.clone());
348 move || {
349 if !done.get() {
350 done.set(true);
351 Property::link_two_way(p1.as_ref(), p2.as_ref());
352 let xx_weak = xx_weak.clone();
353 p1.as_ref().set_binding(move || xx_weak.upgrade().unwrap().as_ref().get() + 9);
354 }
355 global.as_ref().get() + 2
356 }
357 });
358 assert_eq!(xx.as_ref().get(), 23 + 2);
359 assert_eq!(p1.as_ref().get(), 23 + 2 + 9);
360 assert_eq!(p2.as_ref().get(), 23 + 2 + 9);
361
362 global.as_ref().set(55);
363 assert_eq!(p1.as_ref().get(), 55 + 2 + 9);
364 assert_eq!(p2.as_ref().get(), 55 + 2 + 9);
365 assert_eq!(xx.as_ref().get(), 55 + 2);
366}
367
368#[test]
369fn property_two_ways_binding_of_two_way_binding_first() {
370 let p1_1 = Rc::pin(Property::new(2));
371 let p1_2 = Rc::pin(Property::new(4));
372 Property::link_two_way(p1_1.as_ref(), p1_2.as_ref());
373
374 assert_eq!(p1_1.as_ref().get(), 4);
375 assert_eq!(p1_2.as_ref().get(), 4);
376
377 let p2 = Rc::pin(Property::new(3));
378 Property::link_two_way(p1_1.as_ref(), p2.as_ref());
379
380 assert_eq!(p1_1.as_ref().get(), 3);
381 assert_eq!(p1_2.as_ref().get(), 3);
382 assert_eq!(p2.as_ref().get(), 3);
383
384 p1_1.set(6);
385
386 assert_eq!(p1_1.as_ref().get(), 6);
387 assert_eq!(p1_2.as_ref().get(), 6);
388 assert_eq!(p2.as_ref().get(), 6);
389
390 p1_2.set(8);
391
392 assert_eq!(p1_1.as_ref().get(), 8);
393 assert_eq!(p1_2.as_ref().get(), 8);
394 assert_eq!(p2.as_ref().get(), 8);
395
396 p2.set(7);
397
398 assert_eq!(p1_1.as_ref().get(), 7);
399 assert_eq!(p1_2.as_ref().get(), 7);
400 assert_eq!(p2.as_ref().get(), 7);
401}
402
403#[test]
404fn property_two_ways_binding_of_two_way_binding_second() {
405 let p1 = Rc::pin(Property::new(2));
406 let p2_1 = Rc::pin(Property::new(3));
407 let p2_2 = Rc::pin(Property::new(5));
408 Property::link_two_way(p2_1.as_ref(), p2_2.as_ref());
409
410 assert_eq!(p2_1.as_ref().get(), 5);
411 assert_eq!(p2_2.as_ref().get(), 5);
412
413 Property::link_two_way(p1.as_ref(), p2_2.as_ref());
414
415 assert_eq!(p1.as_ref().get(), 5);
416 assert_eq!(p2_1.as_ref().get(), 5);
417 assert_eq!(p2_2.as_ref().get(), 5);
418
419 p1.set(6);
420
421 assert_eq!(p1.as_ref().get(), 6);
422 assert_eq!(p2_1.as_ref().get(), 6);
423 assert_eq!(p2_2.as_ref().get(), 6);
424
425 p2_1.set(7);
426
427 assert_eq!(p1.as_ref().get(), 7);
428 assert_eq!(p2_1.as_ref().get(), 7);
429 assert_eq!(p2_2.as_ref().get(), 7);
430
431 p2_2.set(9);
432
433 assert_eq!(p1.as_ref().get(), 9);
434 assert_eq!(p2_1.as_ref().get(), 9);
435 assert_eq!(p2_2.as_ref().get(), 9);
436}
437
438#[test]
439fn property_two_ways_binding_of_two_two_way_bindings() {
440 let p1_1 = Rc::pin(Property::new(2));
441 let p1_2 = Rc::pin(Property::new(4));
442 Property::link_two_way(p1_1.as_ref(), p1_2.as_ref());
443 assert_eq!(p1_1.as_ref().get(), 4);
444 assert_eq!(p1_2.as_ref().get(), 4);
445
446 let p2_1 = Rc::pin(Property::new(3));
447 let p2_2 = Rc::pin(Property::new(5));
448 Property::link_two_way(p2_1.as_ref(), p2_2.as_ref());
449
450 assert_eq!(p2_1.as_ref().get(), 5);
451 assert_eq!(p2_2.as_ref().get(), 5);
452
453 Property::link_two_way(p1_1.as_ref(), p2_2.as_ref());
454
455 assert_eq!(p1_1.as_ref().get(), 5);
456 assert_eq!(p1_2.as_ref().get(), 5);
457 assert_eq!(p2_1.as_ref().get(), 5);
458 assert_eq!(p2_2.as_ref().get(), 5);
459
460 p1_1.set(6);
461 assert_eq!(p1_1.as_ref().get(), 6);
462 assert_eq!(p1_2.as_ref().get(), 6);
463 assert_eq!(p2_1.as_ref().get(), 6);
464 assert_eq!(p2_2.as_ref().get(), 6);
465
466 p1_2.set(8);
467 assert_eq!(p1_1.as_ref().get(), 8);
468 assert_eq!(p1_2.as_ref().get(), 8);
469 assert_eq!(p2_1.as_ref().get(), 8);
470 assert_eq!(p2_2.as_ref().get(), 8);
471
472 p2_1.set(7);
473 assert_eq!(p1_1.as_ref().get(), 7);
474 assert_eq!(p1_2.as_ref().get(), 7);
475 assert_eq!(p2_1.as_ref().get(), 7);
476 assert_eq!(p2_2.as_ref().get(), 7);
477
478 p2_2.set(9);
479 assert_eq!(p1_1.as_ref().get(), 9);
480 assert_eq!(p1_2.as_ref().get(), 9);
481 assert_eq!(p2_1.as_ref().get(), 9);
482 assert_eq!(p2_2.as_ref().get(), 9);
483}
484
485#[test]
486fn test_two_way_with_map() {
487 #[derive(PartialEq, Clone, Default, Debug)]
488 struct Struct {
489 foo: i32,
490 bar: alloc::string::String,
491 }
492 let p1 = Rc::pin(Property::new(Struct { foo: 42, bar: "hello".into() }));
493 let p2 = Rc::pin(Property::new(88));
494 let p3 = Rc::pin(Property::new(alloc::string::String::from("xyz")));
495 Property::link_two_way_with_map(p1.as_ref(), p2.as_ref(), |s| s.foo, |s, foo| s.foo = *foo);
496 assert_eq!(p1.as_ref().get(), Struct { foo: 42, bar: "hello".into() });
497 assert_eq!(p2.as_ref().get(), 42);
498
499 p2.as_ref().set(81);
500 assert_eq!(p1.as_ref().get(), Struct { foo: 81, bar: "hello".into() });
501 assert_eq!(p2.as_ref().get(), 81);
502
503 p1.as_ref().set(Struct { foo: 78, bar: "world".into() });
504 assert_eq!(p1.as_ref().get(), Struct { foo: 78, bar: "world".into() });
505 assert_eq!(p2.as_ref().get(), 78);
506
507 Property::link_two_way_with_map(
508 p1.as_ref(),
509 p3.as_ref(),
510 |s| s.bar.clone(),
511 |s, bar| s.bar = bar.clone(),
512 );
513 assert_eq!(p1.as_ref().get(), Struct { foo: 78, bar: "world".into() });
514 assert_eq!(p2.as_ref().get(), 78);
515 assert_eq!(p3.as_ref().get(), "world");
516
517 p3.as_ref().set("abc".into());
518 assert_eq!(p1.as_ref().get(), Struct { foo: 78, bar: "abc".into() });
519 assert_eq!(p2.as_ref().get(), 78);
520 assert_eq!(p3.as_ref().get(), "abc");
521
522 let p4 = Rc::pin(Property::new(123));
523 p2.set_binding({
524 let p4 = p4.clone();
525 move || p4.as_ref().get() + 1
526 });
527
528 assert_eq!(p1.as_ref().get(), Struct { foo: 124, bar: "abc".into() });
529 assert_eq!(p2.as_ref().get(), 124);
530 assert_eq!(p3.as_ref().get(), "abc");
531
532 p4.as_ref().set(456);
533 assert_eq!(p1.as_ref().get(), Struct { foo: 457, bar: "abc".into() });
534 assert_eq!(p2.as_ref().get(), 457);
535 assert_eq!(p3.as_ref().get(), "abc");
536
537 p3.as_ref().set("def".into());
538 assert_eq!(p1.as_ref().get(), Struct { foo: 457, bar: "def".into() });
539 assert_eq!(p2.as_ref().get(), 457);
540 assert_eq!(p3.as_ref().get(), "def");
541
542 p4.as_ref().set(789);
543 assert_eq!(p1.as_ref().get(), Struct { foo: 457, bar: "def".into() });
545 assert_eq!(p2.as_ref().get(), 457);
546 assert_eq!(p3.as_ref().get(), "def");
547}
548
549#[test]
562fn test_two_way_with_map_dependency_list_transfer() {
563 #[derive(PartialEq, Clone, Default, Debug)]
564 struct Wrapper {
565 value: i32,
566 }
567
568 let source = Rc::pin(Property::new(10));
569
570 let tracker = Box::pin(<PropertyTracker>::default());
574
575 let p_field = Rc::pin(Property::new(0i32));
576 p_field.as_ref().set_binding({
577 let source = source.clone();
578 move || source.as_ref().get() * 2
579 });
580 assert_eq!(p_field.as_ref().get(), 20);
581
582 let val = tracker.as_ref().evaluate({
585 let p_field = p_field.clone();
586 move || p_field.as_ref().get()
587 });
588 assert_eq!(val, 20);
589 assert!(!tracker.as_ref().is_dirty());
590
591 let p_struct = Rc::pin(Property::new(Wrapper { value: 0 }));
595 Property::link_two_way_with_map(
596 p_struct.as_ref(),
597 p_field.as_ref(),
598 |s| s.value,
599 |s, v| s.value = *v,
600 );
601
602 assert_eq!(p_field.as_ref().get(), 20);
603 assert_eq!(p_struct.as_ref().get(), Wrapper { value: 20 });
604
605 source.as_ref().set(5);
607 assert!(tracker.as_ref().is_dirty());
608
609 }