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_untracked();
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(core::ptr::null_mut());
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(core::ptr::null_mut());
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(
163 common_property,
164 prop2,
165 map_to,
166 map_from,
167 false,
168 );
169 }
170
171 pub(crate) fn link_two_way_with_map_to_common_property<T2: PartialEq + Clone + 'static>(
177 common_property: Pin<Rc<Self>>,
178 prop2: Pin<&Property<T2>>,
179 map_to: impl Fn(&T) -> T2 + Clone + 'static,
180 map_from: impl Fn(&mut T, &T2) + Clone + 'static,
181 preserve_prop2_binding: bool,
182 ) {
183 struct TwoWayBindingWithMap<T, T2, M1, M2> {
184 common_property: Pin<Rc<Property<T>>>,
185 map_to: M1,
186 map_from: M2,
187 marker: PhantomData<(T, T2)>,
188 }
189 unsafe impl<
190 T: PartialEq + Clone + 'static,
191 T2: PartialEq + Clone + 'static,
192 M1: Fn(&T) -> T2 + Clone + 'static,
193 M2: Fn(&mut T, &T2) + Clone + 'static,
194 > BindingCallable<T2> for TwoWayBindingWithMap<T, T2, M1, M2>
195 {
196 fn evaluate(self: Pin<&Self>, value: &mut T2) -> BindingResult {
197 *value = (self.map_to)(&self.common_property.as_ref().get());
198 BindingResult::KeepBinding
199 }
200
201 fn intercept_set(self: Pin<&Self>, value: &T2) -> bool {
202 let mut old = self.common_property.as_ref().get();
203 (self.map_from)(&mut old, value);
204 self.common_property.as_ref().set(old);
205 true
206 }
207
208 unsafe fn intercept_set_binding(
209 self: Pin<&Self>,
210 new_binding: *mut BindingHolder,
211 ) -> bool {
212 let new_new_binding = alloc_binding_holder(BindingMapper::<T, T2, M1, M2> {
213 b: new_binding,
214 map_to: self.map_to.clone(),
215 map_from: self.map_from.clone(),
216 marker: PhantomData,
217 });
218 self.common_property.handle.set_binding_impl(new_new_binding);
219 true
220 }
221 }
222
223 struct BindingMapper<T, T2, M1, M2> {
225 b: *mut BindingHolder,
227 map_to: M1,
228 map_from: M2,
229 marker: PhantomData<(T, T2)>,
230 }
231 unsafe impl<
232 T: PartialEq + Clone + 'static,
233 T2: PartialEq + Clone + 'static,
234 M1: Fn(&T) -> T2 + 'static,
235 M2: Fn(&mut T, &T2) + 'static,
236 > BindingCallable<T> for BindingMapper<T, T2, M1, M2>
237 {
238 fn evaluate(self: Pin<&Self>, value: &mut T) -> BindingResult {
239 let mut sub_value = (self.map_to)(value);
240 unsafe {
242 ((*self.b).vtable.evaluate)(
243 self.b,
244 (&mut sub_value as *mut T2).cast::<c_void>(),
245 );
246 }
247 (self.map_from)(value, &sub_value);
248 BindingResult::KeepBinding
249 }
250
251 fn intercept_set(self: Pin<&Self>, value: &T) -> bool {
252 let sub_value = (self.map_to)(value);
253 unsafe {
255 ((*self.b).vtable.intercept_set)(
256 self.b,
257 (&sub_value as *const T2).cast::<c_void>(),
258 )
259 }
260 }
261 }
262 impl<T, T2, M1, M2> Drop for BindingMapper<T, T2, M1, M2> {
263 fn drop(&mut self) {
264 unsafe {
265 ((*self.b).vtable.drop)(self.b);
266 }
267 }
268 }
269
270 #[cfg(slint_debug_property)]
271 let debug_name = alloc::format!(
272 "<{}<=>{}>",
273 common_property.debug_name.borrow(),
274 prop2.debug_name.borrow()
275 );
276
277 let old_binding = prop2.handle.detach_binding();
278
279 unsafe {
280 if let Some(old) = old_binding {
281 let new_binding = alloc_binding_holder(TwoWayBindingWithMap {
282 common_property,
283 map_to,
284 map_from,
285 marker: PhantomData,
286 });
287 if ((*old).vtable.intercept_set_binding)(old, new_binding) {
288 prop2.handle.set_binding_impl(old);
289 } else {
290 prop2.handle.set_binding_impl(new_binding);
291 if preserve_prop2_binding {
292 prop2.handle.set_binding_impl(old);
295 } else {
296 ((*old).vtable.drop)(old);
297 }
298 }
299 } else {
300 prop2.handle.set_binding(
301 TwoWayBindingWithMap { common_property, map_to, map_from, marker: PhantomData },
302 #[cfg(slint_debug_property)]
303 debug_name.as_str(),
304 );
305 }
306 }
307 }
308}
309
310struct TwoWayBindingModel<T, ItemTree, Getter, Setter> {
311 phantom: PhantomData<fn(T) -> T>,
312 item_tree: ItemTree,
313 getter: Getter,
314 setter: Setter,
315}
316
317unsafe impl<T, ItemTree, Getter, Setter> BindingCallable<T>
319 for TwoWayBindingModel<T, ItemTree, Getter, Setter>
320where
321 Getter: Fn(&ItemTree) -> Option<T>,
322 Setter: Fn(&ItemTree, &T),
323{
324 fn evaluate(self: Pin<&Self>, value: &mut T) -> BindingResult {
325 if let Some(v) = (self.getter)(&self.item_tree) {
326 *value = v;
327 }
328 BindingResult::KeepBinding
329 }
330
331 unsafe fn intercept_set_binding(self: Pin<&Self>, _new_binding: *mut BindingHolder) -> bool {
332 false
333 }
334
335 fn intercept_set(self: Pin<&Self>, value: &T) -> bool {
336 (self.setter)(&self.item_tree, value);
337 true
338 }
339}
340
341impl<T: 'static> Property<T> {
342 pub fn link_two_way_to_model_data<ItemTree: 'static>(
346 self: Pin<&Self>,
347 item_tree: ItemTree,
348 getter: impl Fn(&ItemTree) -> Option<T> + 'static,
349 setter: impl Fn(&ItemTree, &T) + 'static,
350 ) {
351 let binding = TwoWayBindingModel { phantom: PhantomData, item_tree, getter, setter };
352 unsafe {
354 self.handle.set_binding(
355 binding,
356 #[cfg(slint_debug_property)]
357 &alloc::format!("{}<=>[model]", self.debug_name.borrow()),
358 )
359 };
360 }
361}
362
363#[test]
364fn property_two_ways_test() {
365 let p1 = Rc::pin(Property::new(42));
366 let p2 = Rc::pin(Property::new(88));
367
368 let depends = Box::pin(Property::new(0));
369 depends.as_ref().set_binding({
370 let p1 = p1.clone();
371 move || p1.as_ref().get() + 8
372 });
373 assert_eq!(depends.as_ref().get(), 42 + 8);
374 Property::link_two_way(p1.as_ref(), p2.as_ref());
375 assert_eq!(p1.as_ref().get(), 88);
376 assert_eq!(p2.as_ref().get(), 88);
377 assert_eq!(depends.as_ref().get(), 88 + 8);
378 p2.as_ref().set(5);
379 assert_eq!(p1.as_ref().get(), 5);
380 assert_eq!(p2.as_ref().get(), 5);
381 assert_eq!(depends.as_ref().get(), 5 + 8);
382 p1.as_ref().set(22);
383 assert_eq!(p1.as_ref().get(), 22);
384 assert_eq!(p2.as_ref().get(), 22);
385 assert_eq!(depends.as_ref().get(), 22 + 8);
386}
387
388#[test]
389fn property_two_ways_test_binding() {
390 let p1 = Rc::pin(Property::new(42));
391 let p2 = Rc::pin(Property::new(88));
392 let global = Rc::pin(Property::new(23));
393 p2.as_ref().set_binding({
394 let global = global.clone();
395 move || global.as_ref().get() + 9
396 });
397
398 let depends = Box::pin(Property::new(0));
399 depends.as_ref().set_binding({
400 let p1 = p1.clone();
401 move || p1.as_ref().get() + 8
402 });
403
404 Property::link_two_way(p1.as_ref(), p2.as_ref());
405 assert_eq!(p1.as_ref().get(), 23 + 9);
406 assert_eq!(p2.as_ref().get(), 23 + 9);
407 assert_eq!(depends.as_ref().get(), 23 + 9 + 8);
408 global.as_ref().set(55);
409 assert_eq!(p1.as_ref().get(), 55 + 9);
410 assert_eq!(p2.as_ref().get(), 55 + 9);
411 assert_eq!(depends.as_ref().get(), 55 + 9 + 8);
412}
413
414#[test]
415fn property_two_ways_recurse_from_binding() {
416 let xx = Rc::pin(Property::new(0));
417
418 let p1 = Rc::pin(Property::new(42));
419 let p2 = Rc::pin(Property::new(88));
420 let global = Rc::pin(Property::new(23));
421
422 let done = Rc::new(Cell::new(false));
423 xx.set_binding({
424 let p1 = p1.clone();
425 let p2 = p2.clone();
426 let global = global.clone();
427 let xx_weak = pin_weak::rc::PinWeak::downgrade(xx.clone());
428 move || {
429 if !done.get() {
430 done.set(true);
431 Property::link_two_way(p1.as_ref(), p2.as_ref());
432 let xx_weak = xx_weak.clone();
433 p1.as_ref().set_binding(move || xx_weak.upgrade().unwrap().as_ref().get() + 9);
434 }
435 global.as_ref().get() + 2
436 }
437 });
438 assert_eq!(xx.as_ref().get(), 23 + 2);
439 assert_eq!(p1.as_ref().get(), 23 + 2 + 9);
440 assert_eq!(p2.as_ref().get(), 23 + 2 + 9);
441
442 global.as_ref().set(55);
443 assert_eq!(p1.as_ref().get(), 55 + 2 + 9);
444 assert_eq!(p2.as_ref().get(), 55 + 2 + 9);
445 assert_eq!(xx.as_ref().get(), 55 + 2);
446}
447
448#[test]
449fn property_two_ways_binding_of_two_way_binding_first() {
450 let p1_1 = Rc::pin(Property::new(2));
451 let p1_2 = Rc::pin(Property::new(4));
452 Property::link_two_way(p1_1.as_ref(), p1_2.as_ref());
453
454 assert_eq!(p1_1.as_ref().get(), 4);
455 assert_eq!(p1_2.as_ref().get(), 4);
456
457 let p2 = Rc::pin(Property::new(3));
458 Property::link_two_way(p1_1.as_ref(), p2.as_ref());
459
460 assert_eq!(p1_1.as_ref().get(), 3);
461 assert_eq!(p1_2.as_ref().get(), 3);
462 assert_eq!(p2.as_ref().get(), 3);
463
464 p1_1.set(6);
465
466 assert_eq!(p1_1.as_ref().get(), 6);
467 assert_eq!(p1_2.as_ref().get(), 6);
468 assert_eq!(p2.as_ref().get(), 6);
469
470 p1_2.set(8);
471
472 assert_eq!(p1_1.as_ref().get(), 8);
473 assert_eq!(p1_2.as_ref().get(), 8);
474 assert_eq!(p2.as_ref().get(), 8);
475
476 p2.set(7);
477
478 assert_eq!(p1_1.as_ref().get(), 7);
479 assert_eq!(p1_2.as_ref().get(), 7);
480 assert_eq!(p2.as_ref().get(), 7);
481}
482
483#[test]
484fn property_two_ways_binding_of_two_way_binding_second() {
485 let p1 = Rc::pin(Property::new(2));
486 let p2_1 = Rc::pin(Property::new(3));
487 let p2_2 = Rc::pin(Property::new(5));
488 Property::link_two_way(p2_1.as_ref(), p2_2.as_ref());
489
490 assert_eq!(p2_1.as_ref().get(), 5);
491 assert_eq!(p2_2.as_ref().get(), 5);
492
493 Property::link_two_way(p1.as_ref(), p2_2.as_ref());
494
495 assert_eq!(p1.as_ref().get(), 5);
496 assert_eq!(p2_1.as_ref().get(), 5);
497 assert_eq!(p2_2.as_ref().get(), 5);
498
499 p1.set(6);
500
501 assert_eq!(p1.as_ref().get(), 6);
502 assert_eq!(p2_1.as_ref().get(), 6);
503 assert_eq!(p2_2.as_ref().get(), 6);
504
505 p2_1.set(7);
506
507 assert_eq!(p1.as_ref().get(), 7);
508 assert_eq!(p2_1.as_ref().get(), 7);
509 assert_eq!(p2_2.as_ref().get(), 7);
510
511 p2_2.set(9);
512
513 assert_eq!(p1.as_ref().get(), 9);
514 assert_eq!(p2_1.as_ref().get(), 9);
515 assert_eq!(p2_2.as_ref().get(), 9);
516}
517
518#[test]
519fn property_two_ways_binding_of_two_two_way_bindings() {
520 let p1_1 = Rc::pin(Property::new(2));
521 let p1_2 = Rc::pin(Property::new(4));
522 Property::link_two_way(p1_1.as_ref(), p1_2.as_ref());
523 assert_eq!(p1_1.as_ref().get(), 4);
524 assert_eq!(p1_2.as_ref().get(), 4);
525
526 let p2_1 = Rc::pin(Property::new(3));
527 let p2_2 = Rc::pin(Property::new(5));
528 Property::link_two_way(p2_1.as_ref(), p2_2.as_ref());
529
530 assert_eq!(p2_1.as_ref().get(), 5);
531 assert_eq!(p2_2.as_ref().get(), 5);
532
533 Property::link_two_way(p1_1.as_ref(), p2_2.as_ref());
534
535 assert_eq!(p1_1.as_ref().get(), 5);
536 assert_eq!(p1_2.as_ref().get(), 5);
537 assert_eq!(p2_1.as_ref().get(), 5);
538 assert_eq!(p2_2.as_ref().get(), 5);
539
540 p1_1.set(6);
541 assert_eq!(p1_1.as_ref().get(), 6);
542 assert_eq!(p1_2.as_ref().get(), 6);
543 assert_eq!(p2_1.as_ref().get(), 6);
544 assert_eq!(p2_2.as_ref().get(), 6);
545
546 p1_2.set(8);
547 assert_eq!(p1_1.as_ref().get(), 8);
548 assert_eq!(p1_2.as_ref().get(), 8);
549 assert_eq!(p2_1.as_ref().get(), 8);
550 assert_eq!(p2_2.as_ref().get(), 8);
551
552 p2_1.set(7);
553 assert_eq!(p1_1.as_ref().get(), 7);
554 assert_eq!(p1_2.as_ref().get(), 7);
555 assert_eq!(p2_1.as_ref().get(), 7);
556 assert_eq!(p2_2.as_ref().get(), 7);
557
558 p2_2.set(9);
559 assert_eq!(p1_1.as_ref().get(), 9);
560 assert_eq!(p1_2.as_ref().get(), 9);
561 assert_eq!(p2_1.as_ref().get(), 9);
562 assert_eq!(p2_2.as_ref().get(), 9);
563}
564
565#[test]
566fn test_two_way_with_map() {
567 #[derive(PartialEq, Clone, Default, Debug)]
568 struct Struct {
569 foo: i32,
570 bar: alloc::string::String,
571 }
572 let p1 = Rc::pin(Property::new(Struct { foo: 42, bar: "hello".into() }));
573 let p2 = Rc::pin(Property::new(88));
574 let p3 = Rc::pin(Property::new(alloc::string::String::from("xyz")));
575 Property::link_two_way_with_map(p1.as_ref(), p2.as_ref(), |s| s.foo, |s, foo| s.foo = *foo);
576 assert_eq!(p1.as_ref().get(), Struct { foo: 42, bar: "hello".into() });
577 assert_eq!(p2.as_ref().get(), 42);
578
579 p2.as_ref().set(81);
580 assert_eq!(p1.as_ref().get(), Struct { foo: 81, bar: "hello".into() });
581 assert_eq!(p2.as_ref().get(), 81);
582
583 p1.as_ref().set(Struct { foo: 78, bar: "world".into() });
584 assert_eq!(p1.as_ref().get(), Struct { foo: 78, bar: "world".into() });
585 assert_eq!(p2.as_ref().get(), 78);
586
587 Property::link_two_way_with_map(
588 p1.as_ref(),
589 p3.as_ref(),
590 |s| s.bar.clone(),
591 |s, bar| s.bar = bar.clone(),
592 );
593 assert_eq!(p1.as_ref().get(), Struct { foo: 78, bar: "world".into() });
594 assert_eq!(p2.as_ref().get(), 78);
595 assert_eq!(p3.as_ref().get(), "world");
596
597 p3.as_ref().set("abc".into());
598 assert_eq!(p1.as_ref().get(), Struct { foo: 78, bar: "abc".into() });
599 assert_eq!(p2.as_ref().get(), 78);
600 assert_eq!(p3.as_ref().get(), "abc");
601
602 let p4 = Rc::pin(Property::new(123));
603 p2.set_binding({
604 let p4 = p4.clone();
605 move || p4.as_ref().get() + 1
606 });
607
608 assert_eq!(p1.as_ref().get(), Struct { foo: 124, bar: "abc".into() });
609 assert_eq!(p2.as_ref().get(), 124);
610 assert_eq!(p3.as_ref().get(), "abc");
611
612 p4.as_ref().set(456);
613 assert_eq!(p1.as_ref().get(), Struct { foo: 457, bar: "abc".into() });
614 assert_eq!(p2.as_ref().get(), 457);
615 assert_eq!(p3.as_ref().get(), "abc");
616
617 p3.as_ref().set("def".into());
618 assert_eq!(p1.as_ref().get(), Struct { foo: 457, bar: "def".into() });
619 assert_eq!(p2.as_ref().get(), 457);
620 assert_eq!(p3.as_ref().get(), "def");
621
622 p4.as_ref().set(789);
623 assert_eq!(p1.as_ref().get(), Struct { foo: 457, bar: "def".into() });
625 assert_eq!(p2.as_ref().get(), 457);
626 assert_eq!(p3.as_ref().get(), "def");
627}
628
629#[test]
641fn test_two_way_with_map_dependency_list_transfer() {
642 #[derive(PartialEq, Clone, Default, Debug)]
643 struct Wrapper {
644 value: i32,
645 }
646
647 let source = Rc::pin(Property::new(10));
648
649 let tracker = Box::pin(<PropertyTracker>::default());
653
654 let p_field = Rc::pin(Property::new(0i32));
655 p_field.as_ref().set_binding({
656 let source = source.clone();
657 move || source.as_ref().get() * 2
658 });
659 assert_eq!(p_field.as_ref().get(), 20);
660
661 let val = tracker.as_ref().evaluate({
664 let p_field = p_field.clone();
665 move || p_field.as_ref().get()
666 });
667 assert_eq!(val, 20);
668 assert!(!tracker.as_ref().is_dirty());
669
670 let p_struct = Rc::pin(Property::new(Wrapper { value: 0 }));
675 Property::link_two_way_with_map(
676 p_struct.as_ref(),
677 p_field.as_ref(),
678 |s| s.value,
679 |s, v| s.value = *v,
680 );
681
682 assert_eq!(p_field.as_ref().get(), 0);
683 assert_eq!(p_struct.as_ref().get(), Wrapper { value: 0 });
684
685 assert!(tracker.as_ref().is_dirty());
688
689 }