Skip to main content

i_slint_core/properties/
erased_bindings.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4//! Install property bindings and callback handlers on a component without
5//! monomorphizing the machinery per component type.
6//!
7//! The generated code of every component would otherwise instantiate its own
8//! copy of the binding holder machinery for each property type it uses. The
9//! functions here keep the component behind an [`ErasedWeakFn`], which pairs a
10//! type-erased weak reference with the binding function so that everything past
11//! the `#[inline]` shims is monomorphized per property type only, not per
12//! component. The erasure and its safety live in the `vtable` crate; this module
13//! is entirely safe.
14//!
15//! The binding functions all take an extra `&Arg` (`&()` for those without a
16//! meaningful argument), so that erasing them never changes the function's arity.
17
18use super::Property;
19use crate::callbacks::Callback;
20use core::pin::Pin;
21use vtable::{ErasedWeakFn, VRcMapped, VTableMetaDropInPlace};
22
23/// Sets the binding of `property` to evaluate `binding` on the component
24/// `self_rc`, or to produce the default value once the component is gone.
25#[inline]
26pub fn set_property_binding_erased<
27    T: Clone + Default + 'static,
28    VT: VTableMetaDropInPlace + 'static,
29    X,
30>(
31    property: Pin<&Property<T>>,
32    self_rc: &VRcMapped<VT, X>,
33    binding: fn(Pin<&X>, &()) -> T,
34) {
35    set_property_binding_impl(property, VRcMapped::downgrade_erased_fn(self_rc, binding))
36}
37
38fn set_property_binding_impl<T: Clone + Default + 'static, VT: VTableMetaDropInPlace + 'static>(
39    property: Pin<&Property<T>>,
40    binding: ErasedWeakFn<VT, T>,
41) {
42    property.set_binding(move || binding.upgrade_and_call(&()).unwrap_or_default())
43}
44
45/// Like [`set_property_binding_erased`], for an animated binding.
46/// The component must outlive the binding.
47#[inline]
48pub fn set_animated_property_binding_erased<
49    T: Clone + super::InterpolatedPropertyValue + 'static,
50    VT: VTableMetaDropInPlace + 'static,
51    X,
52>(
53    property: Pin<&Property<T>>,
54    self_rc: &VRcMapped<VT, X>,
55    binding: fn(Pin<&X>, &()) -> T,
56    compute_animation_details: fn(
57        Pin<&X>,
58        &(),
59    ) -> (
60        crate::items::PropertyAnimation,
61        Option<crate::animations::Instant>,
62    ),
63) {
64    set_animated_property_binding_impl(
65        property,
66        VRcMapped::downgrade_erased_fn(self_rc, binding),
67        VRcMapped::downgrade_erased_fn(self_rc, compute_animation_details),
68    )
69}
70
71fn set_animated_property_binding_impl<
72    T: Clone + super::InterpolatedPropertyValue + 'static,
73    VT: VTableMetaDropInPlace + 'static,
74>(
75    property: Pin<&Property<T>>,
76    binding: ErasedWeakFn<VT, T>,
77    compute_animation_details: ErasedWeakFn<
78        VT,
79        (crate::items::PropertyAnimation, Option<crate::animations::Instant>),
80    >,
81) {
82    property.set_animated_binding(
83        move || binding.upgrade_and_call(&()).expect("binding evaluated on dropped component"),
84        move || {
85            compute_animation_details
86                .upgrade_and_call(&())
87                .expect("binding evaluated on dropped component")
88        },
89    )
90}
91
92/// Like [`set_property_binding_erased`], for a state binding.
93/// The component must outlive the binding.
94#[inline]
95pub fn set_property_state_binding_erased<VT: VTableMetaDropInPlace + 'static, X>(
96    property: Pin<&Property<super::StateInfo>>,
97    self_rc: &VRcMapped<VT, X>,
98    binding: fn(Pin<&X>, &()) -> i32,
99) {
100    set_property_state_binding_impl(property, VRcMapped::downgrade_erased_fn(self_rc, binding))
101}
102
103fn set_property_state_binding_impl<VT: VTableMetaDropInPlace + 'static>(
104    property: Pin<&Property<super::StateInfo>>,
105    binding: ErasedWeakFn<VT, i32>,
106) {
107    super::set_state_binding(property, move || {
108        binding.upgrade_and_call(&()).expect("binding evaluated on dropped component")
109    })
110}
111
112/// Initialize `change_tracker` to evaluate `eval` on the component `self_rc`
113/// and call `notify` when the result changes. The component must outlive the
114/// change tracker.
115#[inline]
116pub fn change_tracker_init_erased<
117    T: Default + PartialEq + 'static,
118    VT: VTableMetaDropInPlace + 'static,
119    X,
120>(
121    change_tracker: &super::ChangeTracker,
122    self_rc: &VRcMapped<VT, X>,
123    eval: fn(Pin<&X>, &()) -> T,
124    notify: fn(Pin<&X>, &T),
125) {
126    change_tracker_init_impl(
127        change_tracker,
128        VRcMapped::downgrade_erased_fn(self_rc, eval),
129        VRcMapped::downgrade_erased_fn(self_rc, notify),
130    )
131}
132
133fn change_tracker_init_impl<
134    T: Default + PartialEq + 'static,
135    VT: VTableMetaDropInPlace + 'static,
136>(
137    change_tracker: &super::ChangeTracker,
138    eval: ErasedWeakFn<VT, T>,
139    notify: ErasedWeakFn<VT, (), T>,
140) {
141    change_tracker.init(
142        (eval, notify),
143        |(eval, _)| eval.upgrade_and_call(&()).expect("change tracker on dropped component"),
144        |(_, notify), value| {
145            notify.upgrade_and_call(value).expect("change tracker on dropped component");
146        },
147    )
148}
149
150/// Sets the handler of `callback` to evaluate `handler` on the component
151/// `self_rc`. The component must outlive the callback handler.
152#[inline]
153pub fn set_callback_handler_erased<
154    Arg: ?Sized + 'static,
155    Ret: Default + 'static,
156    VT: VTableMetaDropInPlace + 'static,
157    X,
158>(
159    callback: Pin<&Callback<Arg, Ret>>,
160    self_rc: &VRcMapped<VT, X>,
161    handler: fn(Pin<&X>, &Arg) -> Ret,
162) {
163    set_callback_handler_impl(callback, VRcMapped::downgrade_erased_fn(self_rc, handler))
164}
165
166fn set_callback_handler_impl<
167    Arg: ?Sized + 'static,
168    Ret: Default + 'static,
169    VT: VTableMetaDropInPlace + 'static,
170>(
171    callback: Pin<&Callback<Arg, Ret>>,
172    handler: ErasedWeakFn<VT, Ret, Arg>,
173) {
174    callback.set_handler(move |arg| {
175        handler.upgrade_and_call(arg).expect("callback invoked on a dropped component")
176    })
177}