Skip to content

Commit 321cfd6

Browse files
LeonMatthesR-Cramer4
authored andcommitted
Animate Bindings Reactivity (slint-ui#10387)
* Add test case for reactive Animate bindings See issue slint-ui#348 * Add C++ test case for animate bindings reactivity * Fix animted property bindings for C++ and Rust * Fix animate bindings reactivity in interpreter * Use optional start time instead of current_tick This avoids re-evaluating the PropertyAnimation during the mark_dirty function and re-evaluates it on the first call to evaluate. * Remove set_animated_binding This has been replaced by set_animated_binding_for_transition * Rename set_animated_binding_for_transation to set_animated_binding * Revert Animation::Transition to a single CodeBlock The `state` variable is referenced by both expressions, so using a single CodeBlock is easier. In the generated code, use tuple destructuring to convert the Instant to an Option<Instant>. * Fix set_animated_binding unit tests
1 parent 62d925f commit 321cfd6

File tree

11 files changed

+272
-202
lines changed

11 files changed

+272
-202
lines changed

api/cpp/include/slint_properties.h

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,41 +23,37 @@ using cbindgen_private::StateInfo;
2323
inline void slint_property_set_animated_binding_helper(
2424
const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, int *),
2525
void *user_data, void (*drop_user_data)(void *),
26-
const cbindgen_private::PropertyAnimation *animation_data,
27-
cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *))
26+
cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t **))
2827
{
29-
cbindgen_private::slint_property_set_animated_binding_int(
30-
handle, binding, user_data, drop_user_data, animation_data, transition_data);
28+
cbindgen_private::slint_property_set_animated_binding_int(handle, binding, user_data,
29+
drop_user_data, transition_data);
3130
}
3231

3332
inline void slint_property_set_animated_binding_helper(
3433
const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, float *),
3534
void *user_data, void (*drop_user_data)(void *),
36-
const cbindgen_private::PropertyAnimation *animation_data,
37-
cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *))
35+
cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t **))
3836
{
39-
cbindgen_private::slint_property_set_animated_binding_float(
40-
handle, binding, user_data, drop_user_data, animation_data, transition_data);
37+
cbindgen_private::slint_property_set_animated_binding_float(handle, binding, user_data,
38+
drop_user_data, transition_data);
4139
}
4240

4341
inline void slint_property_set_animated_binding_helper(
4442
const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, Color *),
4543
void *user_data, void (*drop_user_data)(void *),
46-
const cbindgen_private::PropertyAnimation *animation_data,
47-
cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *))
44+
cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t **))
4845
{
49-
cbindgen_private::slint_property_set_animated_binding_color(
50-
handle, binding, user_data, drop_user_data, animation_data, transition_data);
46+
cbindgen_private::slint_property_set_animated_binding_color(handle, binding, user_data,
47+
drop_user_data, transition_data);
5148
}
5249

5350
inline void slint_property_set_animated_binding_helper(
5451
const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, Brush *),
5552
void *user_data, void (*drop_user_data)(void *),
56-
const cbindgen_private::PropertyAnimation *animation_data,
57-
cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *))
53+
cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t **))
5854
{
59-
cbindgen_private::slint_property_set_animated_binding_brush(
60-
handle, binding, user_data, drop_user_data, animation_data, transition_data);
55+
cbindgen_private::slint_property_set_animated_binding_brush(handle, binding, user_data,
56+
drop_user_data, transition_data);
6157
}
6258

6359
template<typename T>
@@ -106,21 +102,9 @@ struct Property
106102

107103
inline void set_animated_value(const T &value,
108104
const cbindgen_private::PropertyAnimation &animation_data) const;
109-
template<typename F>
110-
inline void
111-
set_animated_binding(F binding, const cbindgen_private::PropertyAnimation &animation_data) const
112-
{
113-
private_api::slint_property_set_animated_binding_helper(
114-
&inner,
115-
[](void *user_data, T *value) {
116-
*reinterpret_cast<T *>(value) = (*reinterpret_cast<F *>(user_data))();
117-
},
118-
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
119-
&animation_data, nullptr);
120-
}
121105

122106
template<typename F, typename Trans>
123-
inline void set_animated_binding_for_transition(F binding, Trans animation) const
107+
inline void set_animated_binding(F binding, Trans animation) const
124108
{
125109
struct UserData
126110
{
@@ -134,8 +118,8 @@ struct Property
134118
reinterpret_cast<UserData *>(user_data)->binding();
135119
},
136120
new UserData { binding, animation },
137-
[](void *user_data) { delete reinterpret_cast<UserData *>(user_data); }, nullptr,
138-
[](void *user_data, uint64_t *instant) {
121+
[](void *user_data) { delete reinterpret_cast<UserData *>(user_data); },
122+
[](void *user_data, uint64_t **instant) {
139123
return reinterpret_cast<UserData *>(user_data)->animation(instant);
140124
});
141125
}

api/rs/slint/private_unstable_api.rs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -67,33 +67,18 @@ pub fn set_property_binding<
6767
pub fn set_animated_property_binding<
6868
T: Clone + i_slint_core::properties::InterpolatedPropertyValue + 'static,
6969
StrongRef: StrongItemTreeRef + 'static,
70-
>(
71-
property: Pin<&Property<T>>,
72-
component_strong: &StrongRef,
73-
binding: fn(StrongRef) -> T,
74-
animation_data: PropertyAnimation,
75-
) {
76-
let weak = component_strong.to_weak();
77-
property.set_animated_binding(
78-
move || binding(<StrongRef as StrongItemTreeRef>::from_weak(&weak).unwrap()),
79-
animation_data,
80-
)
81-
}
82-
83-
pub fn set_animated_property_binding_for_transition<
84-
T: Clone + i_slint_core::properties::InterpolatedPropertyValue + 'static,
85-
StrongRef: StrongItemTreeRef + 'static,
8670
>(
8771
property: Pin<&Property<T>>,
8872
component_strong: &StrongRef,
8973
binding: fn(StrongRef) -> T,
9074
compute_animation_details: fn(
9175
StrongRef,
92-
) -> (PropertyAnimation, i_slint_core::animations::Instant),
76+
)
77+
-> (PropertyAnimation, Option<i_slint_core::animations::Instant>),
9378
) {
9479
let weak_1 = component_strong.to_weak();
9580
let weak_2 = weak_1.clone();
96-
property.set_animated_binding_for_transition(
81+
property.set_animated_binding(
9782
move || binding(<StrongRef as StrongItemTreeRef>::from_weak(&weak_1).unwrap()),
9883
move || {
9984
compute_animation_details(<StrongRef as StrongItemTreeRef>::from_weak(&weak_2).unwrap())
@@ -186,7 +171,7 @@ pub mod re_exports {
186171
pub use i_slint_core::accessibility::{
187172
AccessibilityAction, AccessibleStringProperty, SupportedAccessibilityAction,
188173
};
189-
pub use i_slint_core::animations::{EasingCurve, animation_tick};
174+
pub use i_slint_core::animations::{EasingCurve, animation_tick, current_tick};
190175
pub use i_slint_core::api::{LogicalPosition, StyledText};
191176
pub use i_slint_core::callbacks::Callback;
192177
pub use i_slint_core::date_time::*;

internal/compiler/generator/cpp.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -669,19 +669,26 @@ fn handle_property_init(
669669
match &binding_expression.animation {
670670
Some(llr::Animation::Static(anim)) => {
671671
let anim = compile_expression(anim, ctx);
672-
format!("{prop_access}.set_animated_binding({binding_code}, {anim});")
672+
// Note: The start_time defaults to the current tick, so doesn't need to be
673+
// udpated here.
674+
format!("{prop_access}.set_animated_binding({binding_code},
675+
[this](uint64_t **start_time) -> slint::cbindgen_private::PropertyAnimation {{
676+
[[maybe_unused]] auto self = this;
677+
auto anim = {anim};
678+
*start_time = nullptr;
679+
return anim;
680+
}});",
681+
)
673682
}
674-
Some(llr::Animation::Transition (
675-
anim
676-
)) => {
677-
let anim = compile_expression(anim, ctx);
683+
Some(llr::Animation::Transition(animation)) => {
684+
let animation = compile_expression(animation, ctx);
678685
format!(
679-
"{prop_access}.set_animated_binding_for_transition({binding_code},
680-
[this](uint64_t *start_time) -> slint::cbindgen_private::PropertyAnimation {{
686+
"{prop_access}.set_animated_binding({binding_code},
687+
[this](uint64_t **start_time) -> slint::cbindgen_private::PropertyAnimation {{
681688
[[maybe_unused]] auto self = this;
682-
auto [anim, time] = {anim};
683-
*start_time = time;
684-
return anim;
689+
auto [animation, change_time] = {animation};
690+
**start_time = change_time;
691+
return animation;
685692
}});",
686693
)
687694
}

internal/compiler/generator/rust.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -633,16 +633,21 @@ fn handle_property_init(
633633
let anim = compile_expression(anim, ctx);
634634
quote! { {
635635
#init_self_pin_ref
636-
slint::private_unstable_api::set_animated_property_binding(#rust_property, &self_rc, #binding_tokens, #anim);
636+
slint::private_unstable_api::set_animated_property_binding(
637+
#rust_property, &self_rc, #binding_tokens, move |self_rc| {
638+
#init_self_pin_ref
639+
(#anim, None)
640+
});
637641
} }
638642
}
639-
Some(llr::Animation::Transition(anim)) => {
640-
let anim = compile_expression(anim, ctx);
643+
Some(llr::Animation::Transition(animation)) => {
644+
let animation = compile_expression(animation, ctx);
641645
quote! {
642-
slint::private_unstable_api::set_animated_property_binding_for_transition(
646+
slint::private_unstable_api::set_animated_property_binding(
643647
#rust_property, &self_rc, #binding_tokens, move |self_rc| {
644648
#init_self_pin_ref
645-
#anim
649+
let (animation, change_time) = #animation;
650+
(animation, Some(change_time))
646651
}
647652
);
648653
}

internal/compiler/llr/optim_passes/count_property_use.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ fn visit_binding_expression(binding: &BindingExpression, ctx: &EvaluationContext
159159
binding.expression.borrow().visit_property_references(ctx, &mut visit_property);
160160
match &binding.animation {
161161
Some(Animation::Static(e) | Animation::Transition(e)) => {
162-
e.visit_property_references(ctx, &mut visit_property)
162+
e.visit_property_references(ctx, &mut visit_property);
163163
}
164164
None => (),
165165
}

internal/compiler/llr/optim_passes/remove_unused.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -501,9 +501,8 @@ mod visitor {
501501
) {
502502
visit_expression(expression.get_mut(), scope, state, visitor);
503503
match animation {
504-
Some(Animation::Static(anim)) => visit_expression(anim, scope, state, visitor),
505-
Some(Animation::Transition(anim)) => {
506-
visit_expression(anim, scope, state, visitor);
504+
Some(Animation::Static(anim) | Animation::Transition(anim)) => {
505+
visit_expression(anim, scope, state, visitor)
507506
}
508507
None => (),
509508
}

internal/core/properties/ffi.rs

Lines changed: 48 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,10 @@ unsafe fn c_set_animated_binding<T: InterpolatedPropertyValue + Clone>(
261261
binding: extern "C" fn(*mut c_void, *mut T),
262262
user_data: *mut c_void,
263263
drop_user_data: Option<extern "C" fn(*mut c_void)>,
264-
animation_data: Option<&PropertyAnimation>,
265-
transition_data: Option<
266-
extern "C" fn(user_data: *mut c_void, start_instant: &mut u64) -> PropertyAnimation,
267-
>,
264+
transition_data: extern "C" fn(
265+
user_data: *mut c_void,
266+
start_instant: &mut *mut u64,
267+
) -> PropertyAnimation,
268268
) {
269269
unsafe {
270270
let binding = core::mem::transmute::<
@@ -286,27 +286,31 @@ unsafe fn c_set_animated_binding<T: InterpolatedPropertyValue + Clone>(
286286
let animation_data = RefCell::new(properties_animations::PropertyValueAnimationData::new(
287287
T::default(),
288288
T::default(),
289-
animation_data.cloned().unwrap_or_default(),
289+
PropertyAnimation::default(),
290290
));
291-
if let Some(transition_data) = transition_data {
292-
handle.0.set_binding(properties_animations::AnimatedBindingCallable::<T, _> {
293-
original_binding,
294-
state: Cell::new(properties_animations::AnimatedBindingState::NotAnimating),
295-
animation_data,
296-
compute_animation_details: move || -> properties_animations::AnimationDetail {
297-
let mut start_instant = 0;
298-
let anim = transition_data(user_data, &mut start_instant);
299-
Some((anim, crate::animations::Instant(start_instant)))
300-
},
301-
});
302-
} else {
303-
handle.0.set_binding(properties_animations::AnimatedBindingCallable::<T, _> {
304-
original_binding,
305-
state: Cell::new(properties_animations::AnimatedBindingState::NotAnimating),
306-
animation_data,
307-
compute_animation_details: || -> properties_animations::AnimationDetail { None },
308-
});
309-
}
291+
292+
handle.0.set_binding(properties_animations::AnimatedBindingCallable::<T, _> {
293+
original_binding,
294+
state: Cell::new(properties_animations::AnimatedBindingState::NotAnimating),
295+
animation_data,
296+
compute_animation_details: move || -> properties_animations::AnimationDetail {
297+
// The transition_data function receives a *mut *mut u64 pointer for the
298+
// timestamp.
299+
// If the function sets the pointer to nullptr, it doesn't provide a start_time.
300+
// Otherwise, we assume it has written a value to the start_instant.
301+
// This basically models a `&mut Option<u64>`, which is then converted to an
302+
// `Option<Instant>`
303+
let mut start_instant = 0u64;
304+
let mut start_instant_ref = &mut start_instant as *mut u64;
305+
let anim = transition_data(user_data, &mut start_instant_ref);
306+
let start_instant = if start_instant_ref.is_null() {
307+
None
308+
} else {
309+
Some(crate::animations::Instant(start_instant))
310+
};
311+
(anim, start_instant)
312+
},
313+
});
310314
handle.0.mark_dirty();
311315
}
312316
}
@@ -318,20 +322,13 @@ pub unsafe extern "C" fn slint_property_set_animated_binding_int(
318322
binding: extern "C" fn(*mut c_void, *mut core::ffi::c_int),
319323
user_data: *mut c_void,
320324
drop_user_data: Option<extern "C" fn(*mut c_void)>,
321-
animation_data: Option<&PropertyAnimation>,
322-
transition_data: Option<
323-
extern "C" fn(user_data: *mut c_void, start_instant: &mut u64) -> PropertyAnimation,
324-
>,
325+
transition_data: extern "C" fn(
326+
user_data: *mut c_void,
327+
start_instant: &mut *mut u64,
328+
) -> PropertyAnimation,
325329
) {
326330
unsafe {
327-
c_set_animated_binding(
328-
handle,
329-
binding,
330-
user_data,
331-
drop_user_data,
332-
animation_data,
333-
transition_data,
334-
);
331+
c_set_animated_binding(handle, binding, user_data, drop_user_data, transition_data);
335332
}
336333
}
337334

@@ -342,20 +339,13 @@ pub unsafe extern "C" fn slint_property_set_animated_binding_float(
342339
binding: extern "C" fn(*mut c_void, *mut f32),
343340
user_data: *mut c_void,
344341
drop_user_data: Option<extern "C" fn(*mut c_void)>,
345-
animation_data: Option<&PropertyAnimation>,
346-
transition_data: Option<
347-
extern "C" fn(user_data: *mut c_void, start_instant: &mut u64) -> PropertyAnimation,
348-
>,
342+
transition_data: extern "C" fn(
343+
user_data: *mut c_void,
344+
start_instant: &mut *mut u64,
345+
) -> PropertyAnimation,
349346
) {
350347
unsafe {
351-
c_set_animated_binding(
352-
handle,
353-
binding,
354-
user_data,
355-
drop_user_data,
356-
animation_data,
357-
transition_data,
358-
);
348+
c_set_animated_binding(handle, binding, user_data, drop_user_data, transition_data);
359349
}
360350
}
361351

@@ -366,20 +356,13 @@ pub unsafe extern "C" fn slint_property_set_animated_binding_color(
366356
binding: extern "C" fn(*mut c_void, *mut Color),
367357
user_data: *mut c_void,
368358
drop_user_data: Option<extern "C" fn(*mut c_void)>,
369-
animation_data: Option<&PropertyAnimation>,
370-
transition_data: Option<
371-
extern "C" fn(user_data: *mut c_void, start_instant: &mut u64) -> PropertyAnimation,
372-
>,
359+
transition_data: extern "C" fn(
360+
user_data: *mut c_void,
361+
start_instant: &mut *mut u64,
362+
) -> PropertyAnimation,
373363
) {
374364
unsafe {
375-
c_set_animated_binding(
376-
handle,
377-
binding,
378-
user_data,
379-
drop_user_data,
380-
animation_data,
381-
transition_data,
382-
);
365+
c_set_animated_binding(handle, binding, user_data, drop_user_data, transition_data);
383366
}
384367
}
385368

@@ -390,20 +373,13 @@ pub unsafe extern "C" fn slint_property_set_animated_binding_brush(
390373
binding: extern "C" fn(*mut c_void, *mut Brush),
391374
user_data: *mut c_void,
392375
drop_user_data: Option<extern "C" fn(*mut c_void)>,
393-
animation_data: Option<&PropertyAnimation>,
394-
transition_data: Option<
395-
extern "C" fn(user_data: *mut c_void, start_instant: &mut u64) -> PropertyAnimation,
396-
>,
376+
transition_data: extern "C" fn(
377+
user_data: *mut c_void,
378+
start_instant: &mut *mut u64,
379+
) -> PropertyAnimation,
397380
) {
398381
unsafe {
399-
c_set_animated_binding(
400-
handle,
401-
binding,
402-
user_data,
403-
drop_user_data,
404-
animation_data,
405-
transition_data,
406-
);
382+
c_set_animated_binding(handle, binding, user_data, drop_user_data, transition_data);
407383
}
408384
}
409385

0 commit comments

Comments
 (0)